document.addEventListener('DOMContentLoaded', () => { // SweetAlert2 Toast Mixin const Toast = (typeof Swal !== 'undefined') ? Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 3000, timerProgressBar: true, didOpen: (toast) => { toast.addEventListener('mouseenter', Swal.stopTimer); toast.addEventListener('mouseleave', Swal.resumeTimer); } }) : null; function showToast(msg, type = 'primary') { if (!Toast) { console.log(`Toast: ${msg} (${type})`); return; } let icon = 'info'; if (type === 'success') icon = 'success'; if (type === 'danger') icon = 'error'; if (type === 'warning') icon = 'warning'; Toast.fire({ icon: icon, title: msg }); } // Global settings with fallbacks const settings = (typeof COMPANY_SETTINGS !== 'undefined') ? COMPANY_SETTINGS : { currency_symbol: '$', currency_decimals: 2, vat_rate: 0 }; let cart = []; let currentOrderId = null; const cartItemsContainer = document.getElementById('cart-items'); const cartTotalPrice = document.getElementById('cart-total-price'); const cartSubtotal = document.getElementById('cart-subtotal'); const cartVatInput = document.getElementById('cart-vat-input'); const quickOrderBtn = document.getElementById('quick-order-btn'); const placeOrderBtn = document.getElementById('place-order-btn'); const recallBtn = document.getElementById('recall-bill-btn'); const reprintReceiptBtn = document.getElementById('reprint-receipt-btn'); const recallModalEl = document.getElementById('recallOrderModal'); const recallModal = recallModalEl ? new bootstrap.Modal(recallModalEl) : null; const recallList = document.getElementById('recall-orders-list'); let isLoyaltyRedemption = false; const loyaltySection = document.getElementById('loyalty-section'); const loyaltyPointsDisplay = document.getElementById('loyalty-points-display'); const loyaltyMessage = document.getElementById('loyalty-message'); const redeemLoyaltyBtn = document.getElementById('redeem-loyalty-btn'); const viewPointsHistoryBtn = document.getElementById('view-points-history-btn'); const pointsHistoryModalEl = document.getElementById('pointsHistoryModal'); const pointsHistoryModal = pointsHistoryModalEl ? new bootstrap.Modal(pointsHistoryModalEl) : null; const pointsHistoryBody = document.getElementById('points-history-body'); const pointsHistoryEmpty = document.getElementById('points-history-empty'); let currentTableId = null; let currentTableName = null; const tableDisplay = document.getElementById('current-table-display'); const tableModalEl = document.getElementById('tableSelectionModal'); const tableSelectionModal = tableModalEl ? new bootstrap.Modal(tableModalEl) : null; const variantModalEl = document.getElementById('variantSelectionModal'); const variantSelectionModal = variantModalEl ? new bootstrap.Modal(variantModalEl) : null; let pendingProduct = null; const customerSearchInput = document.getElementById('customer-search'); const customerResults = document.getElementById('customer-results'); const selectedCustomerId = document.getElementById('selected-customer-id'); const clearCustomerBtn = document.getElementById('clear-customer'); const customerInfo = document.getElementById('customer-info'); const customerNameDisplay = document.getElementById('customer-name-display'); let currentCustomer = null; const paymentModalEl = document.getElementById('paymentSelectionModal'); const paymentSelectionModal = paymentModalEl ? new bootstrap.Modal(paymentModalEl) : null; const paymentMethodsContainer = document.getElementById('payment-methods-container'); const productSearchInput = document.getElementById('product-search'); let currentCategory = 'all'; let currentSearchQuery = ''; function formatCurrency(amount) { const symbol = settings.currency_symbol || '$'; const decimals = parseInt(settings.currency_decimals || 2); return symbol + parseFloat(amount).toFixed(decimals); } function filterProducts() { const items = document.querySelectorAll('.product-item'); items.forEach(item => { const matchesCategory = (currentCategory == 'all' || item.dataset.category == currentCategory); const name = (item.dataset.name || '').toLowerCase(); const sku = (item.dataset.sku || '').toLowerCase(); const matchesSearch = name.includes(currentSearchQuery) || sku.includes(currentSearchQuery); item.style.display = (matchesCategory && matchesSearch) ? 'block' : 'none'; }); } window.filterCategory = function(categoryId, btnElement) { currentCategory = categoryId; document.querySelectorAll('.category-btn').forEach(btn => btn.classList.remove('active')); if (btnElement) { btnElement.classList.add('active'); } else { const btn = document.querySelector(`.category-btn[data-category="${categoryId}"]`); if (btn) btn.classList.add('active'); } filterProducts(); }; document.querySelectorAll('.category-btn').forEach(btn => { btn.addEventListener('click', () => { filterCategory(btn.dataset.category, btn); }); }); if (productSearchInput) { productSearchInput.addEventListener('input', (e) => { currentSearchQuery = e.target.value.trim().toLowerCase(); filterProducts(); }); } window.openRecallOrderModal = function() { if (!recallModal) return; fetchRecallOrders(); recallModal.show(); }; function fetchRecallOrders() { if (!recallList) return; recallList.innerHTML = '
'; const outletId = CURRENT_OUTLET ? CURRENT_OUTLET.id : 1; fetch(`api/recall_orders.php?action=list&outlet_id=${outletId}`) .then(res => res.json()) .then(data => { recallList.innerHTML = ''; if (data.success && data.orders.length > 0) { data.orders.forEach(order => { const item = document.createElement('button'); item.className = 'list-group-item list-group-item-action d-flex justify-content-between align-items-center'; item.innerHTML = `
Order #${order.id} ${order.order_type}
${order.customer_name || 'Guest'} ${order.table_number ? ' • Table ' + order.table_number : ''} • ${order.time_formatted}
${formatCurrency(order.total_amount)}
${order.item_count} items
`; item.onclick = () => loadRecalledOrder(order.id); recallList.appendChild(item); }); } else { recallList.innerHTML = '
No unpaid bills found.
'; } }) .catch(() => { recallList.innerHTML = '
Error fetching orders.
'; }); } function loadRecalledOrder(orderId) { fetch(`api/recall_orders.php?action=details&id=${orderId}`) .then(res => res.json()) .then(data => { if (data.success) { currentOrderId = data.order.id; if (data.customer) selectCustomer(data.customer); else if (clearCustomerBtn) clearCustomerBtn.click(); const otInput = document.querySelector(`input[name="order_type"][value="${data.order.order_type}"]`); if (otInput) { otInput.checked = true; if (data.order.order_type === 'dine-in' && data.order.table_id) { selectTable(data.order.table_id, data.order.table_number); } else { checkOrderType(); } } cart = data.items; updateCart(); if (recallModal) recallModal.hide(); showToast(`Order #${orderId} loaded!`, 'success'); } else { showToast(data.error || 'Failed to load order', 'danger'); } }); } if (customerSearchInput) { let searchTimeout; customerSearchInput.addEventListener('input', (e) => { const query = e.target.value.trim(); clearTimeout(searchTimeout); if (query.length < 2) { if (customerResults) customerResults.style.display = 'none'; return; } searchTimeout = setTimeout(() => { fetch(`api/search_customers.php?q=${encodeURIComponent(query)}`) .then(res => res.json()) .then(data => { if (!customerResults) return; customerResults.innerHTML = ''; if (data.length > 0) { data.forEach(cust => { const a = document.createElement('a'); a.href = '#'; a.className = 'list-group-item list-group-item-action'; a.innerHTML = `
${cust.name}
${cust.phone || ''}
`; a.onclick = (ev) => { ev.preventDefault(); selectCustomer(cust); }; customerResults.appendChild(a); }); customerResults.style.display = 'block'; } else { customerResults.innerHTML = '
No results found
'; customerResults.style.display = 'block'; } }); }, 300); }); document.addEventListener('click', (e) => { if (customerResults && !customerSearchInput.contains(e.target) && !customerResults.contains(e.target)) { customerResults.style.display = 'none'; } }); } function selectCustomer(cust) { currentCustomer = cust; if (selectedCustomerId) selectedCustomerId.value = cust.id; if (customerNameDisplay) customerNameDisplay.textContent = cust.name; if (customerSearchInput) { customerSearchInput.value = cust.name; customerSearchInput.disabled = true; } if (customerResults) customerResults.style.display = 'none'; if (clearCustomerBtn) clearCustomerBtn.classList.remove('d-none'); if (loyaltySection && typeof LOYALTY_SETTINGS !== 'undefined' && LOYALTY_SETTINGS.is_enabled) { loyaltySection.classList.remove('d-none'); if (loyaltyPointsDisplay) loyaltyPointsDisplay.textContent = cust.points + ' pts'; if (redeemLoyaltyBtn) { redeemLoyaltyBtn.disabled = !cust.eligible_for_free_meal; if (loyaltyMessage) { if (cust.eligible_for_free_meal) loyaltyMessage.innerHTML = 'Eligible for Free Meal!'; else loyaltyMessage.textContent = `${cust.points_needed || 0} pts away from a free meal.`; } } } isLoyaltyRedemption = false; } if (clearCustomerBtn) { clearCustomerBtn.addEventListener('click', () => { currentCustomer = null; if (selectedCustomerId) selectedCustomerId.value = ''; if (customerSearchInput) { customerSearchInput.value = ''; customerSearchInput.disabled = false; } clearCustomerBtn.classList.add('d-none'); if (customerInfo) customerInfo.classList.add('d-none'); if (loyaltySection) loyaltySection.classList.add('d-none'); isLoyaltyRedemption = false; updateCart(); }); } window.checkOrderType = function() { const checked = document.querySelector('input[name="order_type"]:checked'); if (!checked) return; const selected = checked.value; if (selected === 'dine-in') { if (!currentTableId && tableSelectionModal) openTableSelectionModal(); if (tableDisplay) tableDisplay.style.display = 'inline-block'; } else { if (tableDisplay) tableDisplay.style.display = 'none'; } }; document.querySelectorAll('input[name="order_type"]').forEach(input => { input.addEventListener('change', checkOrderType); }); window.handleProductClick = function(product, variants) { if (variants && variants.length > 0) { openVariantModal(product, variants); } else { addToCart({ id: product.id, name: product.name, name_ar: product.name_ar || "", price: parseFloat(product.price), base_price: parseFloat(product.price), hasVariants: false, quantity: 1, variant_id: null, variant_name: null }); } }; function openVariantModal(product, variants) { if (!variantSelectionModal) return; const list = document.getElementById('variant-list'); const title = document.getElementById('variantModalTitle'); if (title) title.textContent = `Option: ${product.name}`; if (!list) return; list.innerHTML = ''; variants.forEach(v => { const btn = document.createElement('button'); btn.className = 'list-group-item list-group-item-action d-flex justify-content-between align-items-center'; const finalPrice = parseFloat(product.price) + parseFloat(v.price_adjustment); btn.innerHTML = `${v.name}${formatCurrency(finalPrice)}`; btn.onclick = () => { addToCart({ id: product.id, name: product.name, name_ar: product.name_ar || "", price: finalPrice, base_price: parseFloat(product.price), hasVariants: true, quantity: 1, variant_id: v.id, variant_name: v.name }); variantSelectionModal.hide(); }; list.appendChild(btn); }); variantSelectionModal.show(); } window.addToCart = function(product) { const existing = cart.find(item => item.id === product.id && item.variant_id === product.variant_id); if (existing) existing.quantity++; else cart.push({...product}); updateCart(); }; window.changeQuantity = function(index, delta) { if (cart[index]) { cart[index].quantity += delta; if (cart[index].quantity <= 0) cart.splice(index, 1); updateCart(); } }; window.removeFromCart = function(index) { cart.splice(index, 1); updateCart(); }; window.clearCart = function() { if (cart.length === 0) return; cart = []; currentOrderId = null; isLoyaltyRedemption = false; updateCart(); showToast("Cart cleared", "success"); }; function updateCart() { if (!cartItemsContainer) return; if (cart.length === 0) { cartItemsContainer.innerHTML = '

Cart is empty

'; if (cartSubtotal) cartSubtotal.innerText = formatCurrency(0); if (cartTotalPrice) cartTotalPrice.innerText = formatCurrency(0); if (quickOrderBtn) quickOrderBtn.disabled = true; if (placeOrderBtn) placeOrderBtn.disabled = true; return; } cartItemsContainer.innerHTML = ''; let subtotal = 0; cart.forEach((item, index) => { const itemTotal = item.price * item.quantity; subtotal += itemTotal; const row = document.createElement('div'); row.className = 'd-flex justify-content-between align-items-center mb-3 border-bottom pb-2'; row.innerHTML = `
${item.name}
${item.name_ar ? `
${item.name_ar}
` : ''}
${formatCurrency(item.price)}
${item.quantity}
${formatCurrency(itemTotal)}
`; cartItemsContainer.appendChild(row); }); if (cartSubtotal) cartSubtotal.innerText = formatCurrency(subtotal); const vatRate = parseFloat(settings.vat_rate) || 0; const vat = subtotal * (vatRate / 100); if (cartVatInput) cartVatInput.value = vat.toFixed(2); const total = subtotal + vat; if (cartTotalPrice) cartTotalPrice.innerText = formatCurrency(total); if (quickOrderBtn) quickOrderBtn.disabled = false; if (placeOrderBtn) placeOrderBtn.disabled = false; } if (quickOrderBtn) { quickOrderBtn.addEventListener('click', () => { if (cart.length > 0 && paymentSelectionModal) { renderPaymentMethods(); paymentSelectionModal.show(); } }); } if (placeOrderBtn) { placeOrderBtn.addEventListener('click', () => { if (cart.length > 0) processOrder(null, 'Pay Later'); }); } function renderPaymentMethods() { if (!paymentMethodsContainer) return; paymentMethodsContainer.innerHTML = ''; if (typeof PAYMENT_TYPES !== 'undefined') { PAYMENT_TYPES.forEach(pt => { const col = document.createElement('div'); col.className = 'col-6'; col.innerHTML = ``; paymentMethodsContainer.appendChild(col); }); } } window.processOrder = function(paymentTypeId, paymentTypeName) { const orderTypeInput = document.querySelector('input[name="order_type"]:checked'); const orderType = orderTypeInput ? orderTypeInput.value : 'takeaway'; const subtotal = cart.reduce((acc, item) => acc + (item.price * item.quantity), 0); const vatRate = parseFloat(settings.vat_rate) || 0; const vat = subtotal * (vatRate / 100); const orderData = { order_id: currentOrderId, table_number: (orderType === 'dine-in') ? currentTableId : null, order_type: orderType, customer_id: selectedCustomerId ? selectedCustomerId.value : null, outlet_id: CURRENT_OUTLET ? CURRENT_OUTLET.id : 1, payment_type_id: paymentTypeId, total_amount: subtotal + vat, vat: vat, items: cart.map(item => ({ product_id: item.id, quantity: item.quantity, unit_price: item.price, variant_id: item.variant_id })) }; fetch('api/order.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(orderData) }) .then(res => res.json()) .then(data => { if (data.success) { showToast(`Order #${data.order_id} placed!`, 'success'); clearCart(); if (paymentSelectionModal) paymentSelectionModal.hide(); if (clearCustomerBtn) clearCustomerBtn.click(); } else showToast(data.error, 'danger'); }); }; });