Our Menu

Pizza

AI-generated pizza photo

Notice

')).catch(()=>' ') ]); document.querySelector('header').innerHTML = h; document.querySelector('footer').innerHTML = f; initHeaderInteractions(); initFooterInteractions(); }catch(e){ // minimal safe fallbacks already inserted } } function safeQS(q){ try{ return document.querySelector(q) }catch(_){ return null } } function initHeaderInteractions(){ const authModal = document.getElementById('authModal'); document.querySelectorAll('[data-open-auth]').forEach(b=>b.addEventListener('click',()=>authModal && authModal.showModal())); const closeAuth = document.querySelector('[data-close-auth]'); if(closeAuth && authModal){ closeAuth.addEventListener('click',()=>authModal.close()); } const themeBtn = document.querySelector('[data-open-theme]'); const themeModal = document.getElementById('themeModal'); if(themeBtn && themeModal){ themeBtn.addEventListener('click',()=>themeModal.showModal()); const closeTheme = document.querySelector('[data-close-theme]'); if(closeTheme) closeTheme.addEventListener('click',()=>themeModal.close()); document.querySelectorAll('[data-theme]').forEach(btn=>btn.addEventListener('click',()=>{ localStorage.setItem('theme',btn.dataset.theme); if(btn.dataset.theme==='dark'){document.documentElement.classList.add('dark')}else{document.documentElement.classList.remove('dark')} themeModal.close(); })); } // cart badge const cart = JSON.parse(localStorage.getItem('cart')||'[]'); const count = cart.reduce((a,c)=>a + Number(c.qty||0),0); const badge = document.querySelector('[data-cart-count]'); if(badge){badge.textContent = count;} // header search bridge const headerSearch = document.querySelector('[data-global-search]'); if(headerSearch){ headerSearch.addEventListener('input', (e)=>{ const q = document.getElementById('q'); if(q){ q.value = e.target.value; triggerFilter(); } }); } } function initFooterInteractions(){ const ok = localStorage.getItem('cookieConsent')==='1'; const c = document.getElementById('cookieBanner'); if(c && !ok) c.showModal(); const accept = document.getElementById('cookieAccept'); if(accept){accept.addEventListener('click',()=>{localStorage.setItem('cookieConsent','1'); c.close();});} } function buildChips(){ const chips = []; const q = document.getElementById('q').value.trim(); if(q) chips.push({k:'q', label:`Search: "${q}"`}); const category = document.getElementById('category').value; if(category) chips.push({k:'category', label:category}); const priceMaxVal = document.getElementById('priceMax').value; if(priceMaxVal) chips.push({k:'priceMax', label:`≤ ₹${priceMaxVal}`}); const spice = document.getElementById('spice').value; if(spice!==''){ const map={0:'Mild',1:'Medium',2:'Spicy',3:'Fiery'}; chips.push({k:'spice', label:`Spice: ${map[spice]}`}); } const veg = document.getElementById('veg').value; if(veg!==''){ chips.push({k:'veg', label: veg==='true'?'Veg':'Non-veg'}); } const sort = document.getElementById('sort').value; if(sort && sort!=='featured'){ const m={priceAsc:'Price ↑',priceDesc:'Price ↓',spiceDesc:'Spice ↓',calAsc:'Calories ↑'}; chips.push({k:'sort', label:m[sort]}); } const box = document.getElementById('activeChips'); box.innerHTML=''; chips.forEach(ch=>{ const b = document.createElement('button'); b.type='button'; b.className='px-2 py-1 rounded-full bg-white border border-slate-200 hover:bg-slate-50'; b.textContent=ch.label+' ✕'; b.addEventListener('click',()=>{ const el = document.getElementById(ch.k); if(el){ if(el.tagName==='SELECT'){ el.value=''; } else { el.value=''; } triggerFilter(); } }); box.appendChild(b); }); } function applyFilters(){ const q = document.getElementById('q').value.trim().toLowerCase(); const category = document.getElementById('category').value; const priceMax = Number(document.getElementById('priceMax').value||Infinity); const spice = document.getElementById('spice').value; const veg = document.getElementById('veg').value; const sort = document.getElementById('sort').value; let arr = state.data.filter(p=>{ const matchQ = !q || (p.name.toLowerCase().includes(q) || (Array.isArray(p.tags) && p.tags.join(' ').toLowerCase().includes(q)) || (p.description||'').toLowerCase().includes(q)); const matchCat = !category || p.category===category || (category==='Veg' && p.veg); const matchPrice = p.priceINR <= priceMax; const matchSpice = spice==='' || String(p.spicyLevel)===spice; const matchVeg = veg==='' || String(p.veg)===veg; return matchQ && matchCat && matchPrice && matchSpice && matchVeg; }); if(sort==='priceAsc') arr.sort((a,b)=>a.priceINR-b.priceINR); if(sort==='priceDesc') arr.sort((a,b)=>b.priceINR-a.priceINR); if(sort==='spiceDesc') arr.sort((a,b)=>b.spicyLevel-a.spicyLevel); if(sort==='calAsc') arr.sort((a,b)=>a.calories-b.calories); if(sort==='featured') arr.sort((a,b)=> (b.rating||0)-(a.rating||0)); buildChips(); render(arr); saveFilterState(); } function render(arr){ const grid = document.getElementById('grid'); grid.innerHTML=''; const total = arr.length; const pages = Math.max(1, Math.ceil(total/state.perPage)); if(state.page>pages) state.page = 1; const start = (state.page-1)*state.perPage; const slice = arr.slice(start, start+state.perPage); const favs = new Set(JSON.parse(localStorage.getItem('favorites')||'[]')); const cart = JSON.parse(localStorage.getItem('cart')||'[]'); const inCart = new Map(cart.map(i=>[i.id, Number(i.qty||0)])); slice.forEach(p=>{ const li = document.createElement('li'); const soldOut = Number(p.stock||0) <= 0; const favActive = favs.has(p.id); const qtyInCart = inCart.get(p.id)||0; li.innerHTML = `
AI-generated ${p.name} pizza with Indian-inspired toppings ${soldOut?'Sold out':''} ${p.veg?'Veg':'Non-veg'}

${p.name}

${currency.format(p.priceINR)} 🌶️ ${p.spicyLevel}
⭐ ${Number(p.rating||0).toFixed(1)} ${p.calories} kcal
`; grid.appendChild(li); }); const pagination = document.getElementById('pagination'); pagination.innerHTML=''; if(pages>1){ const prev = document.createElement('button'); prev.textContent='Prev'; prev.disabled = state.page===1; prev.className = 'px-3 py-1 rounded-md border border-slate-200 '+(prev.disabled?'text-slate-400 cursor-not-allowed':'hover:bg-slate-50'); prev.addEventListener('click',()=>{ if(state.page>1){ state.page--; applyFilters(); window.scrollTo({top:0,behavior:'smooth'}) }}); pagination.appendChild(prev); } for(let i=1;i<=pages;i++){ const b = document.createElement('button'); b.textContent=i; b.className = 'px-3 py-1 rounded-md border '+(i===state.page?'bg-slate-900 text-white border-slate-900':'border-slate-200 hover:bg-slate-50'); b.addEventListener('click',()=>{state.page=i; applyFilters(); window.scrollTo({top:0,behavior:'smooth'})}); pagination.appendChild(b); } if(pages>1){ const next = document.createElement('button'); next.textContent='Next'; next.disabled = state.page===pages; next.className = 'px-3 py-1 rounded-md border border-slate-200 '+(next.disabled?'text-slate-400 cursor-not-allowed':'hover:bg-slate-50'); next.addEventListener('click',()=>{ if(state.pagebtn.addEventListener('click',()=>openDetail(btn.dataset.detail))); document.querySelectorAll('[data-fav]').forEach(btn=>btn.addEventListener('click',()=>toggleFav(Number(btn.dataset.fav)))); document.querySelectorAll('[data-add]').forEach(btn=>btn.addEventListener('click',()=>{ const id=Number(btn.dataset.add); const prod = state.data.find(x=>x.id===id); if(!prod || Number(prod.stock||0)<=0) return; addToCart(id,1) })); } function toggleFav(id){ const favs = new Set(JSON.parse(localStorage.getItem('favorites')||'[]')); if(favs.has(id)) favs.delete(id); else favs.add(id); localStorage.setItem('favorites', JSON.stringify([...favs])); applyFilters(); } function addToCart(id, qty){ const cart = JSON.parse(localStorage.getItem('cart')||'[]'); const found = cart.find(i=>i.id===id); if(found) found.qty = Number(found.qty)+Number(qty); else cart.push({id, qty:Number(qty)}); localStorage.setItem('cart', JSON.stringify(cart)); const badge = document.querySelector('[data-cart-count]'); if(badge){badge.textContent = cart.reduce((a,c)=>a+Number(c.qty||0),0);} showError(`Added to cart. Item #${id}`); applyFilters(); } function showError(msg){ const d = document.getElementById('errorModal'); document.getElementById('errText').textContent=msg; d.showModal(); const close = document.getElementById('errClose'); if(close){ close.onclick=()=>d.close(); } d.addEventListener('click', (e)=>{ const rect = d.getBoundingClientRect(); const inside = e.clientX>=rect.left && e.clientX<=rect.right && e.clientY>=rect.top && e.clientY<=rect.bottom; if(!inside) d.close(); }); } function openDetail(id){ const p = state.data.find(x=>x.id===Number(id)); if(!p) return; const soldOut = Number(p.stock||0)<=0; document.getElementById('pmName').textContent = p.name; document.getElementById('pmImg').src = p.image; document.getElementById('pmImg').alt = `AI-generated ${p.name} close-up photo`; document.getElementById('pmDesc').textContent = p.description; document.getElementById('pmMeta').textContent = `${currency.format(p.priceINR)} · ${p.veg?'Veg':'Non-veg'} · 🌶️ ${p.spicyLevel} · ${p.calories} kcal · Rating ${Number(p.rating||0).toFixed(1)} · ${soldOut?'Out of stock':(p.stock+' in stock')}`; const pmQty = document.getElementById('pmQty'); pmQty.value = 1; pmQty.min = 1; pmQty.max = Math.max(1, Number(p.stock||99)); const addBtn = document.getElementById('pmAddCart'); addBtn.disabled = soldOut; addBtn.className = 'rounded-full '+(soldOut?'bg-slate-200 text-slate-500':'bg-amber-600 text-white hover:bg-amber-700')+' px-5 py-2 text-sm'; addBtn.textContent = soldOut?'Sold out':'Add to Cart'; addBtn.onclick = ()=>{ const q = Math.max(1, Math.min(Number(pmQty.max), Number(pmQty.value||1))); addToCart(p.id, q); document.getElementById('productModal').close(); }; document.getElementById('pmFav').onclick = ()=>{ toggleFav(p.id); }; document.getElementById('productModal').showModal(); } document.getElementById('pmClose').addEventListener('click',()=>document.getElementById('productModal').close()); document.getElementById('productModal').addEventListener('cancel',(e)=>{ e.preventDefault(); document.getElementById('productModal').close(); }); function triggerFilter(){ state.page=1; applyFilters(); } function debounce(fn, d=250){ let t; return (...a)=>{ clearTimeout(t); t=setTimeout(()=>fn.apply(null,a),d); } } const onFilterInput = debounce(()=>triggerFilter(), 150); document.getElementById('filters').addEventListener('input', onFilterInput); document.getElementById('clearFilters').addEventListener('click', ()=>{ ['q','category','priceMax','spice','veg','sort'].forEach(id=>{ const el=document.getElementById(id); if(el){ el.value = (id==='sort'?'featured':''); } }); triggerFilter(); }); function saveFilterState(){ const f = { q: document.getElementById('q').value || '', category: document.getElementById('category').value || '', priceMax: document.getElementById('priceMax').value || '', spice: document.getElementById('spice').value || '', veg: document.getElementById('veg').value || '', sort: document.getElementById('sort').value || 'featured', page: state.page }; localStorage.setItem('catalogFilters', JSON.stringify(f)); } function loadFilterState(){ try{ const f = JSON.parse(localStorage.getItem('catalogFilters')||'{}'); if(!f) return; if(f.q!==undefined) document.getElementById('q').value = f.q; if(f.category!==undefined) document.getElementById('category').value = f.category; if(f.priceMax!==undefined) document.getElementById('priceMax').value = f.priceMax; if(f.spice!==undefined) document.getElementById('spice').value = f.spice; if(f.veg!==undefined) document.getElementById('veg').value = f.veg; if(f.sort!==undefined) document.getElementById('sort').value = f.sort; if(f.page) state.page = Number(f.page)||1; }catch(_){} } // Theme persistence + keyboard toggle (T) (function syncTheme(){ const t=localStorage.getItem('theme'); if(t==='dark'){document.documentElement.classList.add('dark')} else {document.documentElement.classList.remove('dark')} window.addEventListener('keydown', (e)=>{ if(e.key.toLowerCase()==='t' && !document.getElementById('productModal').open && !document.getElementById('errorModal').open){ const c = document.documentElement.classList.contains('dark'); if(c){ document.documentElement.classList.remove('dark'); localStorage.setItem('theme','light'); } else { document.documentElement.classList.add('dark'); localStorage.setItem('theme','dark'); } }}); })(); // Load data then render async function loadData(){ try{ const r = await fetch('./catalog.json', {cache:'no-store'}); if(!r.ok) throw new Error('Failed to load catalog'); const json = await r.json(); if(!Array.isArray(json)) throw new Error('Invalid data format'); state.data=json; loadFilterState(); applyFilters(); }catch(e){ showError('Unable to load catalog. Please try again later.'); } } // Accessibility: close error modal on backdrop click (function(){ const d = document.getElementById('errorModal'); d.addEventListener('click', (e)=>{ const rect = d.getBoundingClientRect(); const inside = e.clientX>=rect.left && e.clientX<=rect.right && e.clientY>=rect.top && e.clientY<=rect.bottom; if(!inside) d.close(); }); const esc = (e)=>{ if(e.key==='Escape' && d.open) d.close(); }; window.addEventListener('keydown', esc); })(); // Init loadPartials(); loadData();