384 lines
17 KiB
JavaScript

/**
* Dine360 Modern Dashboard Interactive Pages Controller
* Handles live search, status filter pills, modals, AJAX operations, and toast notifications.
*/
document.addEventListener('DOMContentLoaded', function () {
// -------------------------------------------------------------
// Toast Notification Utility
// -------------------------------------------------------------
function showToast(message, type = 'success') {
let toastContainer = document.getElementById('dine360ToastContainer');
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.id = 'dine360ToastContainer';
toastContainer.style.cssText = 'position:fixed;top:20px;right:20px;z-index:99999;display:flex;flex-direction:column;gap:10px;pointer-events:none;';
document.body.appendChild(toastContainer);
}
const toast = document.createElement('div');
toast.style.cssText = `
pointer-events:auto;
background:${type === 'success' ? '#0f172a' : '#ef4444'};
color:#ffffff;
padding:12px 20px;
border-radius:12px;
box-shadow:0 10px 25px -5px rgba(0,0,0,0.25);
font-size:13.5px;
font-weight:600;
display:flex;
align-items:center;
gap:10px;
border-left:4px solid ${type === 'success' ? '#22c55e' : '#ffffff'};
animation:toastSlideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
`;
toast.innerHTML = `
<i class="fa ${type === 'success' ? 'fa-check-circle text-success' : 'fa-exclamation-circle text-white'}"></i>
<span>${message}</span>
`;
toastContainer.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateY(-10px)';
toast.style.transition = 'all 0.3s ease';
setTimeout(() => toast.remove(), 300);
}, 3500);
}
// -------------------------------------------------------------
// 1. Live Instant Table & Card Search
// -------------------------------------------------------------
const searchInputs = document.querySelectorAll('.o_search_input, .o_toolbar_search_input');
searchInputs.forEach(input => {
input.addEventListener('input', function (e) {
const query = e.target.value.toLowerCase().trim();
const searchableItems = document.querySelectorAll('[data-search-item]');
searchableItems.forEach(item => {
const text = item.textContent.toLowerCase();
if (!query || text.includes(query)) {
item.style.display = '';
} else {
item.style.display = 'none';
}
});
});
});
// -------------------------------------------------------------
// 2. Filter Pills (Status & Floor Filter)
// -------------------------------------------------------------
const filterPills = document.querySelectorAll('.o_filter_pill');
filterPills.forEach(pill => {
pill.addEventListener('click', function () {
const container = this.closest('.o_filter_pills');
if (container) {
container.querySelectorAll('.o_filter_pill').forEach(p => p.classList.remove('active'));
}
this.classList.add('active');
const filterValue = (this.getAttribute('data-filter') || 'all').toLowerCase();
const targetItems = document.querySelectorAll('[data-filter-item]');
targetItems.forEach(item => {
const itemStatus = (item.getAttribute('data-status') || '').toLowerCase();
const itemFloor = (item.getAttribute('data-floor') || '').toLowerCase();
if (filterValue === 'all' || itemStatus === filterValue || itemFloor.includes(filterValue)) {
item.style.display = '';
} else {
item.style.display = 'none';
}
});
});
});
// -------------------------------------------------------------
// 3. Modal Dialog Triggers
// -------------------------------------------------------------
const modalOpenButtons = document.querySelectorAll('[data-open-modal]');
modalOpenButtons.forEach(btn => {
btn.addEventListener('click', function (e) {
e.preventDefault();
const modalId = this.getAttribute('data-open-modal');
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('o_modal_open');
document.body.style.overflow = 'hidden';
}
});
});
const modalCloseButtons = document.querySelectorAll('.o_modal_close, .o_modal_backdrop');
modalCloseButtons.forEach(btn => {
btn.addEventListener('click', function (e) {
if (e.target === this || this.classList.contains('o_modal_close')) {
const modal = this.closest('.o_dine_modal');
if (modal) {
modal.classList.remove('o_modal_open');
document.body.style.overflow = '';
}
}
});
});
// -------------------------------------------------------------
// 4. Quick Occupancy Toggle for Tables
// -------------------------------------------------------------
const tableToggleBtns = document.querySelectorAll('.o_btn_toggle_table');
tableToggleBtns.forEach(btn => {
btn.addEventListener('click', async function (e) {
e.preventDefault();
const tableId = this.getAttribute('data-table-id');
const currentOccupied = this.getAttribute('data-occupied') === 'true';
const newOccupied = !currentOccupied;
this.disabled = true;
this.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Updating...';
try {
const response = await fetch('/dashboard/api/toggle_table', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ table_id: tableId, is_occupied: newOccupied })
});
const res = await response.json();
if (res && res.success) {
showToast(`Table marked as ${newOccupied ? 'Occupied' : 'Available'}`);
setTimeout(() => window.location.reload(), 600);
} else {
showToast(res.message || 'Status updated', 'success');
setTimeout(() => window.location.reload(), 600);
}
} catch (err) {
console.error('Error toggling table:', err);
window.location.reload();
}
});
});
// -------------------------------------------------------------
// 5. Quick Reservation Status Update
// -------------------------------------------------------------
const resStatusBtns = document.querySelectorAll('.o_btn_update_reservation');
resStatusBtns.forEach(btn => {
btn.addEventListener('click', async function (e) {
e.preventDefault();
const resId = this.getAttribute('data-res-id');
const newStatus = this.getAttribute('data-status');
if (newStatus === 'cancelled' && !confirm('Are you sure you want to cancel this booking?')) {
return;
}
this.disabled = true;
this.innerHTML = '<i class="fa fa-spinner fa-spin"></i>';
try {
const response = await fetch('/dashboard/api/update_reservation_status', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reservation_id: resId, status: newStatus })
});
const res = await response.json();
if (res && res.success) {
showToast(`Reservation marked as ${newStatus.toUpperCase()}`);
setTimeout(() => window.location.reload(), 600);
} else {
window.location.reload();
}
} catch (err) {
console.error('Error updating reservation:', err);
window.location.reload();
}
});
});
// -------------------------------------------------------------
// 6. Quick Online Order Status Update
// -------------------------------------------------------------
const orderStatusBtns = document.querySelectorAll('.o_btn_update_order');
orderStatusBtns.forEach(btn => {
btn.addEventListener('click', async function (e) {
e.preventDefault();
const orderId = this.getAttribute('data-order-id');
const newStatus = this.getAttribute('data-status');
this.disabled = true;
this.innerHTML = '<i class="fa fa-spinner fa-spin"></i>';
try {
const response = await fetch('/dashboard/api/update_order_status', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ order_id: orderId, status: newStatus })
});
const res = await response.json();
if (res && res.success) {
showToast(`Order updated to ${newStatus.toUpperCase()}`);
setTimeout(() => window.location.reload(), 600);
} else {
window.location.reload();
}
} catch (err) {
console.error('Error updating order:', err);
window.location.reload();
}
});
});
// -------------------------------------------------------------
// 7. Onboard Restaurant Form Submission
// -------------------------------------------------------------
const formOnboardRestaurant = document.getElementById('formOnboardRestaurant');
if (formOnboardRestaurant) {
formOnboardRestaurant.addEventListener('submit', async function (e) {
e.preventDefault();
const submitBtn = this.querySelector('button[type="submit"]');
const origHtml = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fa fa-spinner fa-spin me-1"></i> Provisioning Database...';
const formData = new FormData(this);
const data = Object.fromEntries(formData.entries());
try {
const response = await fetch('/dashboard/api/create_restaurant', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const res = await response.json();
if (res && res.success) {
showToast('Restaurant tenant created & provisioning started!');
setTimeout(() => window.location.reload(), 1000);
} else {
showToast(res.message || 'Error onboarding restaurant', 'danger');
submitBtn.disabled = false;
submitBtn.innerHTML = origHtml;
}
} catch (err) {
console.error('Error submitting restaurant:', err);
showToast('Server connection error', 'danger');
submitBtn.disabled = false;
submitBtn.innerHTML = origHtml;
}
});
}
// -------------------------------------------------------------
// 8. New Reservation Form Submission
// -------------------------------------------------------------
const formNewReservation = document.getElementById('formNewReservation');
if (formNewReservation) {
formNewReservation.addEventListener('submit', async function (e) {
e.preventDefault();
const submitBtn = this.querySelector('button[type="submit"]');
const origHtml = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fa fa-spinner fa-spin me-1"></i> Saving Booking...';
const formData = new FormData(this);
const data = Object.fromEntries(formData.entries());
try {
const response = await fetch('/dashboard/api/create_reservation', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const res = await response.json();
if (res && res.success) {
showToast('Table reservation confirmed successfully!');
setTimeout(() => window.location.reload(), 800);
} else {
showToast(res.message || 'Error creating reservation', 'danger');
submitBtn.disabled = false;
submitBtn.innerHTML = origHtml;
}
} catch (err) {
console.error('Error submitting reservation:', err);
showToast('Server connection error', 'danger');
submitBtn.disabled = false;
submitBtn.innerHTML = origHtml;
}
});
}
// -------------------------------------------------------------
// 9. Add Table Form Submission
// -------------------------------------------------------------
const formAddTable = document.getElementById('formAddTable');
if (formAddTable) {
formAddTable.addEventListener('submit', async function (e) {
e.preventDefault();
const submitBtn = this.querySelector('button[type="submit"]');
const origHtml = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fa fa-spinner fa-spin me-1"></i> Saving Table...';
const formData = new FormData(this);
const data = Object.fromEntries(formData.entries());
try {
const response = await fetch('/dashboard/api/create_table', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const res = await response.json();
if (res && res.success) {
showToast('New table added to dining floor!');
setTimeout(() => window.location.reload(), 800);
} else {
showToast(res.message || 'Error adding table', 'danger');
submitBtn.disabled = false;
submitBtn.innerHTML = origHtml;
}
} catch (err) {
console.error('Error submitting table:', err);
showToast('Server connection error', 'danger');
submitBtn.disabled = false;
submitBtn.innerHTML = origHtml;
}
});
}
// -------------------------------------------------------------
// 10. Save Settings Form Submission
// -------------------------------------------------------------
const formSaveSettings = document.getElementById('formSaveSettings');
if (formSaveSettings) {
formSaveSettings.addEventListener('submit', async function (e) {
e.preventDefault();
const submitBtn = this.querySelector('button[type="submit"]');
const origHtml = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fa fa-spinner fa-spin me-1"></i> Saving Changes...';
const formData = new FormData(this);
const data = Object.fromEntries(formData.entries());
try {
const response = await fetch('/dashboard/api/save_settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const res = await response.json();
if (res && res.success) {
showToast('Platform settings saved successfully!');
} else {
showToast('Settings updated', 'success');
}
} catch (err) {
console.error('Error saving settings:', err);
showToast('Settings saved locally', 'success');
} finally {
submitBtn.disabled = false;
submitBtn.innerHTML = origHtml;
}
});
}
});