Masala Slice logo (generated) Masala Slice

메뉴 카탈로그

칼로리
무게
맵기
p_favorites') || '[]')); let cart = JSON.parse(localStorage.getItem('msp_cart') || '{}'); let cookieConsent = JSON.parse(localStorage.getItem('msp_cookie_consent') || 'null'); function saveFavorites(){ localStorage.setItem('msp_favorites', JSON.stringify(Array.from(favorites))); } function saveCart(){ localStorage.setItem('msp_cart', JSON.stringify(cart)); updateCartBadge(); } function updateCartBadge(){ const count = Object.values(cart).reduce((a,b)=>a+b,0); if($cartCount){ $cartCount.textContent = count; } } function showCookieBanner(){ if(cookieConsent && cookieConsent.accepted){ $cookieBanner.classList.add('hidden'); return; } $cookieBanner.classList.remove('hidden'); } function setCookieConsent(data){ cookieConsent = data; localStorage.setItem('msp_cookie_consent', JSON.stringify(cookieConsent)); showCookieBanner(); } function currencyINR(n){ try{ return new Intl.NumberFormat('en-IN', { style:'currency', currency:'INR', maximumFractionDigits:0 }).format(n).replace('₹','').trim(); }catch(e){ return String(n); } } function spicyLabel(level){ if(level===0) return 'Not spicy'; if(level===1) return 'Mild'; if(level===2) return 'Medium'; return 'Hot'; } function spicyDots(level){ return '🔥'.repeat(level||0) || '—'; } function buildCategoryOptions(){ const cats = Array.from(new Set(products.map(p=>p.category).filter(Boolean))).sort(); $category.innerHTML = '' + cats.map(c=>``).join(''); } function applyFilters(){ const term = ($search.value || '').toLowerCase().trim(); const veg = $veg.value; const spicy = $spicy.value; const minP = parseFloat($minPrice.value); const maxP = parseFloat($maxPrice.value); const minS = parseFloat($minSize.value); const avail = $availability.value; const cat = $category.value; filtered = products.filter(p=>{ if(cat!=='all' && p.category!==cat) return false; if(veg==='veg' && !p.vegetarian) return false; if(veg==='nonveg' && p.vegetarian) return false; if(spicy!=='any' && p.spicyLevel!==parseInt(spicy,10)) return false; if(!isNaN(minP) && p.pricemaxP) return false; if(!isNaN(minS) && p.size (b.reviewsCount||0)-(a.reviewsCount||0)); } else if(sort==='rating_desc'){ filtered.sort((a,b)=> (b.rating||0)-(a.rating||0)); } else if(sort==='price_asc'){ filtered.sort((a,b)=> (a.price||0)-(b.price||0)); } else if(sort==='price_desc'){ filtered.sort((a,b)=> (b.price||0)-(a.price||0)); } else if(sort==='name_asc'){ filtered.sort((a,b)=> (a.name||'').localeCompare(b.name||'')); } currentPage = 1; render(); } function paginate(arr, size, page){ const start=(page-1)*size; return arr.slice(start,start+size); } function render(){ const total = filtered.length; const totalPages = Math.max(1, Math.ceil(total / pageSize)); if(currentPage>totalPages) currentPage=totalPages; const pageItems = paginate(filtered, pageSize, currentPage); $grid.innerHTML = pageItems.map(p=>{ const fav = favorites.has(p.id); const inStock = !!p.availability; const vegTxt = p.vegetarian ? '🥬 Veg' : '🍖 Non-veg'; const vegColor = p.vegetarian ? 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-300' : 'bg-fuchsia-100 text-fuchsia-800 dark:bg-fuchsia-900/40 dark:text-fuchsia-300'; const thumb = p.thumbnail || p.image || './images/maximum_ditailes_of_this_image.pizza_thumb_topdown_cheese_tomato_basil.jpg'; const alt = escapeHtml(p.name || 'Pizza'); return `
  • ${alt}
    ${escapeHtml(p.name)}
    ${vegTxt}
    ${spicyDots(p.spicyLevel)} ${spicyLabel(p.spicyLevel)}
    ${(p.rating||0).toFixed(1)} ⭐️
    Size: ${escapeHtml(String(p.size||''))}" • ${escapeHtml(String(p.weight||''))}g
    ${currencyINR(p.price||0)}
  • `; }).join('') || `
  • No products match your filters.
  • `; $resultsInfo.textContent = total ? `Showing ${((currentPage-1)*pageSize)+1}-${Math.min(currentPage*pageSize,total)} of ${total}` : 'No results'; const pages = totalPages; const pageBtn = (p, label=p, active=false, disabled=false)=>`
  • `; let pagHtml = ''; pagHtml += pageBtn(Math.max(1,currentPage-1), 'Prev', false, currentPage===1); const windowSize = 5; let start = Math.max(1, currentPage - Math.floor(windowSize/2)); let end = Math.min(pages, start + windowSize - 1); if(end - start + 1 < windowSize){ start = Math.max(1, end - windowSize + 1); } if(start>1){ pagHtml += pageBtn(1, '1', currentPage===1); if(start>2){ pagHtml += `
  • `; } } for(let p=start;p<=end;p++){ pagHtml += pageBtn(p, String(p), currentPage===p); } if(end…`; } pagHtml += pageBtn(pages, String(pages), currentPage===pages); } pagHtml += pageBtn(Math.min(pages,currentPage+1), 'Next', false, currentPage===pages); $pagination.innerHTML = pagHtml; } function openProductModal(p){ $modalImage.src = p.image || p.thumbnail || './images/maximum_ditailes_of_this_image.pizza_closeup_mozzarella_tomatoes_basil_crust_studio_light_4k.jpg'; $modalImage.alt = p.name || 'Pizza'; $modalName.textContent = p.name || 'Pizza'; $modalCategory.textContent = p.category || '—'; $modalVeg.textContent = p.vegetarian ? '🥬 Veg' : '🍖 Non-veg'; $modalVeg.className = 'px-2 py-0.5 rounded-full ' + (p.vegetarian ? 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-300' : 'bg-fuchsia-100 text-fuchsia-800 dark:bg-fuchsia-900/40 dark:text-fuchsia-300'); $modalSpicy.textContent = `${spicyDots(p.spicyLevel)} ${spicyLabel(p.spicyLevel)}`; $modalRating.textContent = (p.rating||0).toFixed(1); $modalSize.textContent = `Size: ${p.size}"`; $modalWeight.textContent = `Weight: ${p.weight} g`; $modalDescription.textContent = p.description || ''; $modalIngredients.innerHTML = (Array.isArray(p.ingredients)?p.ingredients:[]).map(i=>`
  • ${escapeHtml(i)}
  • `).join('') || '
  • '; $modalPrice.textContent = currencyINR(p.price||0); $modalCartForm.setAttribute('data-id', p.id); $modalQty.value = 1; if(typeof $productModal.showModal === 'function'){ $productModal.showModal(); } else { $productModal.setAttribute('open',''); } } function openCart(){ renderCart(); if(typeof $cartModal.showModal === 'function'){ $cartModal.showModal(); } else { $cartModal.setAttribute('open',''); } } function renderCart(){ const items = Object.entries(cart); if(!items.length){ $cartList.innerHTML = '
  • Your cart is empty.
  • '; $cartTotal.textContent = '0'; return; } let total = 0; $cartList.innerHTML = items.map(([id,qty])=>{ const p = products.find(x=>x.id===id); if(!p){ return ''; } const line = (p.price||0)*qty; total += line; const thumb = p.thumbnail || p.image || './images/maximum_ditailes_of_this_image.pizza_thumb_topdown_cheese_tomato_basil.jpg'; return `
  • ${escapeAttr(p.name)}
    ${escapeHtml(p.name)}
    ₹${currencyINR(p.price)} × ${qty} = ₹${currencyINR(line)}
  • `; }).join(''); $cartTotal.textContent = currencyINR(total); } function addToCart(id, qty=1){ qty = Math.max(1, Math.min(99, parseInt(qty||1,10))); cart[id] = (cart[id]||0) + qty; saveCart(); } function escapeHtml(s=''){ return String(s).replace(/[&<>"']/g, m=>({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[m])); } function escapeAttr(s=''){ return escapeHtml(s).replace(/"/g,'"'); } function attachEvents(){ $filtersForm.addEventListener('submit', e=>{ e.preventDefault(); applyFilters(); }); $filterReset.addEventListener('click', ()=>{ $filtersForm.reset(); applyFilters(); }); $search.addEventListener('input', debounce(()=>applyFilters(), 250)); [$veg,$spicy,$minPrice,$maxPrice,$minSize,$availability,$category,$sort].forEach(el=>{ el.addEventListener('change', ()=>applyFilters()); }); $pageSizeForm.addEventListener('change', ()=>{ pageSize = parseInt($pageSize.value,10) || 9; currentPage = 1; render(); }); $pagination.addEventListener('click', e=>{ const b = e.target.closest('button[data-goto]'); if(!b) return; const p = parseInt(b.getAttribute('data-goto'),10); if(!isNaN(p)){ currentPage = p; render(); } }); $grid.addEventListener('click', e=>{ const favBtn = e.target.closest('button[data-fav]'); if(favBtn){ const id = favBtn.getAttribute('data-fav'); if(favorites.has(id)){ favorites.delete(id); } else { favorites.add(id); } saveFavorites(); render(); return; } const quickBtn = e.target.closest('button[data-quick]'); if(quickBtn){ const id = quickBtn.getAttribute('data-quick'); const p = products.find(x=>x.id===id); if(p){ openProductModal(p); } return; } const addBtn = e.target.closest('button[data-add]'); if(addBtn){ const id = addBtn.getAttribute('data-add'); addToCart(id, 1); return; } }); $modalClose.addEventListener('click', ()=>{ $productModal.close?.(); $productModal.removeAttribute('open'); }); $productModal.addEventListener('click', e=>{ if(e.target===$productModal){ $productModal.close?.(); $productModal.removeAttribute('open'); }}); $modalCartForm.addEventListener('submit', e=>{ e.preventDefault(); const id = $modalCartForm.getAttribute('data-id'); const qty = parseInt($modalQty.value,10)||1; addToCart(id, qty); $productModal.close?.(); $productModal.removeAttribute('open'); openCart(); }); $openCart.addEventListener('click', openCart); $cartClose.addEventListener('click', ()=>{ $cartModal.close?.(); $cartModal.removeAttribute('open'); }); $cartModal.addEventListener('click', e=>{ if(e.target===$cartModal){ $cartModal.close?.(); $cartModal.removeAttribute('open'); }}); $cartList.addEventListener('click', e=>{ const dec = e.target.closest('button[data-qty-dec]'); const inc = e.target.closest('button[data-qty-inc]'); const del = e.target.closest('button[data-qty-del]'); if(dec){ const id = dec.getAttribute('data-qty-dec'); const v = Math.max(1, (cart[id]||1) - 1); cart[id] = v; saveCart(); renderCart(); } else if(inc){ const id = inc.getAttribute('data-qty-inc'); const v = Math.min(99, (cart[id]||1) + 1); cart[id] = v; saveCart(); renderCart(); } else if(del){ const id = del.getAttribute('data-qty-del'); delete cart[id]; saveCart(); renderCart(); } }); $cartList.addEventListener('change', e=>{ const input = e.target.closest('input[type="number"][data-qty-id]'); if(!input) return; const id = input.getAttribute('data-qty-id'); let v = parseInt(input.value,10); if(isNaN(v)||v<1){ v=1; } if(v>99){ v=99; } cart[id]=v; saveCart(); renderCart(); }); $cartClear.addEventListener('click', ()=>{ cart = {}; saveCart(); renderCart(); }); $cartCheckout.addEventListener('click', ()=>{ const items = Object.entries(cart).length; if(!items){ alert('Your cart is empty.'); return; } alert('Thank you! Your order is being processed.'); cart = {}; saveCart(); renderCart(); $cartModal.close?.(); $cartModal.removeAttribute('open'); }); $themeToggle.addEventListener('click', ()=>{ const html = document.documentElement; if(html.classList.contains('dark')){ html.classList.remove('dark'); localStorage.setItem('theme','light'); } else { html.classList.add('dark'); localStorage.setItem('theme','dark'); } }); if(cookieConsent && cookieConsent.accepted){ $cookieBanner.classList.add('hidden'); } else { $cookieBanner.classList.remove('hidden'); } $cookieAccept.addEventListener('click', ()=>{ setCookieConsent({ accepted:true, analytics:true, ts:Date.now() }); }); $cookieManage.addEventListener('click', ()=>{ $cookieAnalytics.checked = !!(cookieConsent && cookieConsent.analytics); if(typeof $cookieModal.showModal === 'function'){ $cookieModal.showModal(); } else { $cookieModal.setAttribute('open',''); } }); $cookieAcceptAll.addEventListener('click', ()=>{ setCookieConsent({ accepted:true, analytics:true, ts:Date.now() }); $cookieModal.close?.(); $cookieModal.removeAttribute('open'); }); $cookieDecline.addEventListener('click', ()=>{ setCookieConsent({ accepted:true, analytics:false, ts:Date.now() }); $cookieModal.close?.(); $cookieModal.removeAttribute('open'); }); document.addEventListener('keydown', e=>{ if(e.key==='Escape'){ if($productModal.hasAttribute('open')){ $productModal.close?.(); $productModal.removeAttribute('open'); } if($cartModal.hasAttribute('open')){ $cartModal.close?.(); $cartModal.removeAttribute('open'); } if($cookieModal.hasAttribute('open')){ $cookieModal.close?.(); $cookieModal.removeAttribute('open'); } } }); } function debounce(fn, t){ let id; return function(...args){ clearTimeout(id); id=setTimeout(()=>fn.apply(this,args), t); }; } async function fetchHeaderFooter(){ try{ const [h, f] = await Promise.all([ fetch('./header.html').then(r=>r.ok?r.text():''), fetch('./footer.html').then(r=>r.ok?r.text():'') ]); if(h){ $includeHeader.innerHTML = h; } if(f){ $includeFooter.innerHTML = f; } }catch(e){} } async function init(){ attachEvents(); updateCartBadge(); showCookieBanner(); await fetchHeaderFooter(); try{ const res = await fetch('./catalog.json', { cache: 'no-store' }); if(!res.ok) throw new Error('Failed to load catalog.json'); const data = await res.json(); if(!Array.isArray(data)) throw new Error('Invalid catalog format'); products = data.filter(Boolean); }catch(e){ products = []; } buildCategoryOptions(); filtered = products.slice(); applyFilters(); } init(); })();