214 lines
5.8 KiB
JavaScript
214 lines
5.8 KiB
JavaScript
// 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();
|
|
}
|
|
});
|
|
}
|
|
});
|