/** * 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 = ` ${message} `; 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 = ' 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 = ''; 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 = ''; 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 = ' 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 = ' 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 = ' 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 = ' 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; } }); } });