// ── Order Page — The Roasted Bean ───────────────────────────────────── let menuData = null; let cart = []; let tableNumber = null; let tablesCount = 0; document.addEventListener('DOMContentLoaded', init); async function init() { // Get table from URL const params = new URLSearchParams(window.location.search); const tableParam = params.get('table'); if (tableParam) { tableNumber = parseInt(tableParam); document.getElementById('tableNumber').textContent = `#${tableNumber}`; } // Load data const [menuRes, tablesRes] = await Promise.all([ fetch('/api/menu'), fetch('/api/tables') ]); menuData = await menuRes.json(); const tablesData = await tablesRes.json(); tablesCount = tablesData.tables || 0; // No table from URL — show table selector if (!tableNumber) { document.getElementById('tableDisplay').style.display = 'none'; const sel = document.getElementById('cartTableSelect'); sel.innerHTML = '' + Array.from({ length: tablesCount }, (_, i) => `` ).join(''); sel.addEventListener('change', () => { tableNumber = parseInt(sel.value) || null; updateSubmit(); }); document.getElementById('cartTableRow').style.display = ''; } else { document.getElementById('cartTableRow').style.display = 'none'; } renderFilters(); renderItems(); updateSubmit(); setupCartToggle(); // Start live order polling if table is set if (tableNumber) { loadLiveOrders(); setInterval(loadLiveOrders, 10000); } } // ── Filters ────────────────────────────────────────────────────────── function renderFilters() { const cats = menuData.categories || []; const container = document.getElementById('ordFilters'); container.innerHTML = cats.map(c => ` `).join(''); container.addEventListener('click', (e) => { const btn = e.target.closest('.ord-filter-btn'); if (!btn) return; container.querySelectorAll('.ord-filter-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); renderItems(btn.dataset.cat); }); } // ── Items ──────────────────────────────────────────────────────────── function renderItems(catId = 'all') { const container = document.getElementById('ordItems'); let items = menuData.items || []; if (catId !== 'all') items = items.filter(i => i.category === catId); container.innerHTML = items.map(item => { const hasImg = item.image && item.image.startsWith('http'); const thumbSvg = svgPlaceholder(item.category); const inCart = cart.find(c => c.mid === item.id); const qty = inCart ? inCart.qty : 0; return `
${thumbSvg} ${hasImg ? `` : ''}
${esc(item.name)}
${esc(item.description || '')}
₹${item.price.toFixed(0)}
`; }).join(''); // Click handler container.querySelectorAll('.ord-item:not(.sold-out)').forEach(el => { el.addEventListener('click', () => { const id = el.dataset.id; const item = menuData.items.find(i => i.id === id); if (!item) return; addToCart(item); }); }); } // ── Cart ────────────────────────────────────────────────────────────── function addToCart(item) { const existing = cart.find(c => c.mid === item.id); if (existing) { existing.qty++; } else { cart.push({ mid: item.id, name: item.name, price: item.price, qty: 1 }); } renderCart(); renderItems(document.querySelector('.ord-filter-btn.active')?.dataset?.cat || 'all'); } function removeFromCart(mid) { cart = cart.filter(c => c.mid !== mid); renderCart(); renderItems(document.querySelector('.ord-filter-btn.active')?.dataset?.cat || 'all'); } function changeQty(mid, delta) { const item = cart.find(c => c.mid === mid); if (!item) return; item.qty += delta; if (item.qty <= 0) { removeFromCart(mid); return; } renderCart(); renderItems(document.querySelector('.ord-filter-btn.active')?.dataset?.cat || 'all'); } function renderCart() { const container = document.getElementById('cartItems'); const total = cart.reduce((sum, c) => sum + c.price * c.qty, 0); if (cart.length === 0) { container.innerHTML = '

Tap items from the menu to add them here.

'; } else { container.innerHTML = cart.map(c => `
${esc(c.name)}
₹${c.price.toFixed(0)} each
${c.qty}
₹${(c.price * c.qty).toFixed(0)}
`).join(''); container.querySelectorAll('.cart-qty-btn, .cart-item-remove').forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); const action = btn.dataset.action; const mid = btn.dataset.mid; if (action === 'plus') changeQty(mid, 1); else if (action === 'minus') changeQty(mid, -1); else if (action === 'remove') removeFromCart(mid); }); }); } document.getElementById('cartTotal').textContent = `₹${total.toFixed(0)}`; document.getElementById('cartCount').textContent = cart.reduce((s, c) => s + c.qty, 0); document.getElementById('cartBadge').style.display = cart.length > 0 ? '' : 'none'; updateSubmit(); } // ── Live Orders ─────────────────────────────────────────────────────── async function loadLiveOrders() { if (!tableNumber) return; try { const res = await fetch(`/api/orders/table/${tableNumber}`); if (!res.ok) return; const orders = await res.json(); const section = document.getElementById('ordLive'); const list = document.getElementById('ordLiveList'); if (orders.length === 0) { section.style.display = 'none'; return; } section.style.display = ''; const statusLabel = { pending: 'Preparing', served: 'On its way' }; list.innerHTML = orders.map(o => `
${statusLabel[o.status] || o.status} ${timeAgoShort(o.createdAt)}
${o.note ? `

Note: ${esc(o.note)}

` : ''}

₹${o.total.toFixed(0)}

`).join(''); } catch { /* silently fail — customer-facing */ } } function timeAgoShort(iso) { const diff = Math.floor((Date.now() - new Date(iso).getTime()) / 1000); if (diff < 60) return 'just now'; if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; return `${Math.floor(diff / 3600)}h ago`; } // ── Submit ──────────────────────────────────────────────────────────── function updateSubmit() { const btn = document.getElementById('submitBtn'); const valid = cart.length > 0 && !!tableNumber; btn.disabled = !valid; } document.getElementById('submitBtn').addEventListener('click', async () => { if (!tableNumber || cart.length === 0) return; const note = document.getElementById('orderNote').value.trim(); const btn = document.getElementById('submitBtn'); btn.disabled = true; btn.textContent = 'Placing order…'; try { const res = await fetch('/api/orders', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ table: tableNumber, items: cart, note }) }); if (!res.ok) throw new Error((await res.json()).error || 'Failed'); // Clear cart, show toast, refresh live orders cart = []; renderCart(); document.getElementById('orderNote').value = ''; renderItems(document.querySelector('.ord-filter-btn.active')?.dataset?.cat || 'all'); showToast('✓ Order sent to the kitchen!'); loadLiveOrders(); } catch (err) { btn.disabled = false; btn.textContent = 'Place order'; showToast('Something went wrong. Try again.'); console.error(err); } }); // ── Cart Toggle (mobile) ────────────────────────────────────────────── function setupCartToggle() { const cart = document.getElementById('ordCart'); const head = cart.querySelector('.ord-cart-head'); head.addEventListener('click', () => { cart.classList.toggle('collapsed'); }); } // ── Toast ──────────────────────────────────────────────────────────── function showToast(msg) { const container = document.getElementById('ordToast'); container.innerHTML = `
${msg}
`; container.classList.add('show'); setTimeout(() => container.classList.remove('show'), 2500); } // ── Utils ───────────────────────────────────────────────────────────── function esc(str) { const div = document.createElement('div'); div.textContent = str || ''; return div.innerHTML; } function svgPlaceholder(category) { const svgs = { 'coffee': '', 'tea': '', 'pastries': '', 'food': '' }; return svgs[category] || svgs['coffee']; }