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();
}
});
}
});