the promoseven group crm
This commit is contained in:
commit
12e3a44046
88 files changed
+12923
No files matched your search
@@ -0,0 +1,25 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
<IfModule mod_negotiation.c>
|
||||
Options -MultiViews -Indexes
|
||||
</IfModule>
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Handle Authorization Header
|
||||
RewriteCond %{HTTP:Authorization} .
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
# Handle X-XSRF-Token Header
|
||||
RewriteCond %{HTTP:x-xsrf-token} .
|
||||
RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}]
|
||||
|
||||
# Redirect Trailing Slashes If Not A Folder...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} (.+)/$
|
||||
RewriteRule ^ %1 [L,R=301]
|
||||
|
||||
# Send Requests To Front Controller...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
</IfModule>
|
||||
Whitespace-only changes.
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
// Determine if the application is in maintenance mode...
|
||||
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
||||
require $maintenance;
|
||||
}
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the request...
|
||||
/** @var Application $app */
|
||||
$app = require_once __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
$app->handleRequest(Request::capture());
|
||||
@@ -0,0 +1,213 @@
|
||||
// API Client
|
||||
class ApiClient {
|
||||
constructor() {
|
||||
this.baseUrl = '/api';
|
||||
this.token = localStorage.getItem('token');
|
||||
}
|
||||
|
||||
async request(endpoint, options = {}) {
|
||||
const url = `${this.baseUrl}${endpoint}`;
|
||||
const config = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
...options,
|
||||
};
|
||||
|
||||
if (this.token) {
|
||||
config.headers.Authorization = `Bearer ${this.token}`;
|
||||
}
|
||||
|
||||
const response = await fetch(url, config);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Auth
|
||||
async login(credentials) {
|
||||
const response = await fetch('/api/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(credentials),
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.token) {
|
||||
this.token = data.token;
|
||||
localStorage.setItem('token', data.token);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
// Guests
|
||||
async searchGuests(query) {
|
||||
return this.request(`/guests/search?q=${encodeURIComponent(query)}`);
|
||||
}
|
||||
|
||||
async getGuest(id) {
|
||||
return this.request(`/guests/${id}`);
|
||||
}
|
||||
|
||||
async createGuest(guestData) {
|
||||
return this.request('/guests', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(guestData),
|
||||
});
|
||||
}
|
||||
|
||||
async checkinGuest(id, checkinData) {
|
||||
return this.request(`/guests/${id}/checkin`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(checkinData),
|
||||
});
|
||||
}
|
||||
|
||||
async getCrossSellSuggestions(id) {
|
||||
return this.request(`/guests/${id}/cross-sell-suggestions`);
|
||||
}
|
||||
|
||||
async addGuestTags(id, tags) {
|
||||
return this.request(`/guests/${id}/tags`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ tags }),
|
||||
});
|
||||
}
|
||||
|
||||
async addGuestNote(id, note) {
|
||||
return this.request(`/guests/${id}/notes`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ note_text: note }),
|
||||
});
|
||||
}
|
||||
|
||||
// Loyalty
|
||||
async getTiers() {
|
||||
return this.request('/loyalty/tiers');
|
||||
}
|
||||
|
||||
async awardPoints(id, points) {
|
||||
return this.request(`/guests/${id}/award-points`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ points }),
|
||||
});
|
||||
}
|
||||
|
||||
async getLoyaltyStatus(id) {
|
||||
return this.request(`/guests/${id}/loyalty-status`);
|
||||
}
|
||||
|
||||
// Vouchers
|
||||
async getVouchers() {
|
||||
return this.request('/vouchers');
|
||||
}
|
||||
|
||||
async issueVoucher(guestId, voucherId) {
|
||||
return this.request(`/guests/${guestId}/issue-voucher/${voucherId}`, {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
|
||||
// Admin
|
||||
async getKpis() {
|
||||
return this.request('/dashboard/kpis');
|
||||
}
|
||||
|
||||
async getCharts() {
|
||||
return this.request('/dashboard/charts');
|
||||
}
|
||||
|
||||
async createCampaign(campaignData) {
|
||||
return this.request('/campaigns', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(campaignData),
|
||||
});
|
||||
}
|
||||
|
||||
async getVenues() {
|
||||
return this.request('/venues');
|
||||
}
|
||||
|
||||
async selectVenue(venueId) {
|
||||
return this.request(`/venues/${venueId}/select`, {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const api = new ApiClient();
|
||||
|
||||
// UI Helpers
|
||||
function showToast(message, type = 'success') {
|
||||
const toastHtml = `
|
||||
<div class="toast align-items-center text-white bg-${type} border-0" role="alert">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body">${message}</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = toastHtml;
|
||||
document.body.appendChild(container);
|
||||
|
||||
const toastElement = container.firstElementChild;
|
||||
const toast = new bootstrap.Toast(toastElement);
|
||||
toast.show();
|
||||
|
||||
toastElement.addEventListener('hidden.bs.toast', () => {
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize on page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Theme toggle
|
||||
const themeToggle = document.getElementById('theme-toggle');
|
||||
if (themeToggle) {
|
||||
themeToggle.addEventListener('click', function() {
|
||||
document.body.classList.toggle('dark-theme');
|
||||
localStorage.setItem('theme', document.body.classList.contains('dark-theme') ? 'dark' : 'light');
|
||||
});
|
||||
|
||||
// Load saved theme
|
||||
if (localStorage.getItem('theme') === 'dark') {
|
||||
document.body.classList.add('dark-theme');
|
||||
}
|
||||
}
|
||||
|
||||
// Venue selector
|
||||
const venueSelect = document.getElementById('venue-select');
|
||||
if (venueSelect) {
|
||||
venueSelect.addEventListener('change', async function() {
|
||||
try {
|
||||
await api.selectVenue(this.value);
|
||||
showToast('Venue changed successfully');
|
||||
} catch (error) {
|
||||
showToast('Failed to change venue', 'danger');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Global search
|
||||
const globalSearch = document.getElementById('global-search');
|
||||
const globalSearchBtn = document.getElementById('global-search-btn');
|
||||
if (globalSearch && globalSearchBtn) {
|
||||
globalSearchBtn.addEventListener('click', function() {
|
||||
const query = globalSearch.value.trim();
|
||||
if (query) {
|
||||
window.location.href = `/guests/search?q=${encodeURIComponent(query)}`;
|
||||
}
|
||||
});
|
||||
|
||||
globalSearch.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
globalSearchBtn.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
Reference in new issue
Block a user