384 lines
17 KiB
JavaScript
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;
|
|
}
|
|
});
|
|
}
|
|
});
|