← Home
Chevey's Morning Munchies logo

Chevey's Morning Munchies

Place Your Order Here

A little morning joy, packed for you. 💕

Choose your banana bread minis, breakfast favourites, or loaded mini pancakes, and let us add a little extra goodness to your day. 💕

🍌 Banana Bread Tuesdays🥞 Breakfast Thursdays & Fridays✨ Mini Pancakes Thursdays & Fridays📍 Pickup OnlyChecking orders…
+Number(n||0).toFixed(2); const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); function categoryList(category){if(category==='baked')return MENU.baked;if(category==='breakfast')return [...MENU.breakfastCombos,...MENU.breakfastExtras,...MENU.breakfastDrinks];return [...MENU.miniCombos,...MENU.extraFruit,...MENU.extraCrunch,...MENU.extraSauces]} function categoryCart(category){return categoryList(category).map(([name,price])=>({category,name,price,qty:qty[category+'|'+name]||0})).filter(x=>x.qty>0)} function morningCart(){return [...categoryCart('breakfast'),...categoryCart('mini_pancakes')]} function cart(){return type==='baked'?categoryCart('baked'):morningCart()} function hasBreakfastPancakes(){return categoryCart('breakfast').some(x=>BREAKFAST_PANCAKE_NAMES.has(x.name))} function hasExtraPancake(){return categoryCart('breakfast').some(x=>x.name==='Extra Pancake')} function hasMiniCombo(){return categoryCart('mini_pancakes').some(x=>MINI_COMBO_NAMES.has(x.name))} function hasMiniExtras(){return categoryCart('mini_pancakes').some(x=>/^Extra (Fruit|Crunch|Sauce) - /.test(x.name))} function hasMorningItems(){return morningCart().length>0} function displayItemName(name){return name.replace(/^Extra Fruit - /,'').replace(/^Extra Crunch - /,'').replace(/^Extra Sauce - /,'')} const PRODUCT_PHOTOS={}; // Add real product photo filenames here later. function productPhoto(name){ const key=(type+'|'+name); const src=PRODUCT_PHOTOS[key]||PRODUCT_PHOTOS[name]||''; if(src) return '
'+esc(displayItemName(name))+'
'; const icon=type==='baked'?'🍌':type==='breakfast'?'🍳':'🥞'; return '
'+icon+'Photo coming soon
'; } function itemCard([name,price,desc]){ const amount=qty[type+'|'+name]||0; const isExtra=/^Extra (Fruit|Crunch|Sauce) - /.test(name); if(isExtra){ const optionClass=amount>0?' item-with-options':''; return '

'+esc(displayItemName(name))+'

'+money(price)+'
'+amount+'
'+miniAddonSelectors(name,amount)+'
'; } const premiumBadge=(type==='baked'&&PREMIUM_BREAD_NAMES.has(name))?'Premium':''; const flavourClass=(amount>0&&needsItemFlavours(type,name))?' item-with-flavour':''; return '
'+productPhoto(name)+'

'+esc(displayItemName(name))+premiumBadge+'

'+(desc?'
'+esc(desc)+'
':'')+'
'+money(price)+'
'+amount+'
'+itemFlavourSelectors(type,name,amount)+'
'; } function selectedOption(value,current,label=value){ return ''; } const PANCAKE_FLAVORS=['Vanilla','Banana','Blueberry','Strawberry','Chocolate Chip','Biscoff']; function needsItemFlavours(category,name){ return (category==='breakfast'&&BREAKFAST_PANCAKE_NAMES.has(name))||(category==='mini_pancakes'&&MINI_COMBO_NAMES.has(name)); } function itemFlavourKey(category,name){return category+'|'+name} function syncItemFlavourCount(category,name,count){ if(!needsItemFlavours(category,name))return []; const key=itemFlavourKey(category,name); const current=Array.isArray(pancakeFlavorChoices[key])?pancakeFlavorChoices[key].slice():[]; while(current.length{ const label=count===1?'Pancake flavour *':('Order '+(index+1)+' flavour *'); return ''; }).join(''); return '
🥞 '+esc(title)+'
Each order can have its own pancake flavour.
'+rows+'
'; } function firstSelectedFlavour(category){ for(const item of categoryCart(category)){ if(!needsItemFlavours(category,item.name))continue; const first=itemFlavours(category,item.name,item.qty).find(Boolean); if(first)return first; } return ''; } function hasMissingItemFlavours(category){ return categoryCart(category).some(item=>needsItemFlavours(category,item.name)&&itemFlavours(category,item.name,item.qty).some(v=>!v)); } function needsBreakfastAddonTarget(name){return BREAKFAST_ADDON_TARGET_NAMES.has(name)} function breakfastTargetKey(name){return 'breakfast|'+name} function breakfastTargetOptions(){ const options=[]; categoryCart('breakfast').forEach(item=>{ if(!BREAKFAST_COMBO_NAMES.has(item.name))return; for(let i=0;ivalid.has(v)?v:''); breakfastAddonTargets[key]=next; return next; } function breakfastAddonAssignments(name,count){ return syncBreakfastAddonCount(name,count).slice(0,count); } function breakfastAddonSelectors(name,count){ if(!needsBreakfastAddonTarget(name)||count<1)return ''; const selected=breakfastAddonAssignments(name,count); const options=breakfastTargetOptions(); const title=name==='Extra Pancake' ? (count===1?'Which breakfast gets this extra pancake?':'Assign each extra pancake') : (count===1?'Which breakfast gets this add-on?':'Assign each add-on'); const emptyText=options.length?'Choose one':'Add a Breakfast combo first'; const rows=selected.map((value,index)=>{ const label=count===1?'Add to *':((name==='Extra Pancake'?'Pancake ':'Add-on ')+(index+1)+' *'); return ''; }).join(''); return '
🍳 '+esc(title)+'
Choose the exact Breakfast order this belongs with.
'+rows+'
'; } function hasMissingBreakfastAddonTargets(){ return categoryCart('breakfast').some(item=>needsBreakfastAddonTarget(item.name)&&breakfastAddonAssignments(item.name,item.qty).some(v=>!v)); } function needsMiniAddonTarget(name){return /^Extra (Fruit|Crunch|Sauce) - /.test(name)} function miniTargetKey(name){return 'mini_pancakes|'+name} function miniTargetOptions(){ const options=[]; categoryCart('mini_pancakes').forEach(item=>{ if(!MINI_COMBO_NAMES.has(item.name))return; for(let i=0;ivalid.has(v)?v:''); miniAddonTargets[key]=next; return next; } function miniAddonAssignments(name,count){ return syncMiniAddonCount(name,count).slice(0,count); } function miniAddonSelectors(name,count){ if(!needsMiniAddonTarget(name)||count<1)return ''; const selected=miniAddonAssignments(name,count); const options=miniTargetOptions(); const title=count===1?'Which Mini Pancake order gets this extra?':'Assign each Mini Pancake extra'; const emptyText=options.length?'Choose one':'Add a Mini Pancake combo first'; const rows=selected.map((value,index)=>{ const label=count===1?'Add to *':('Extra '+(index+1)+' *'); return ''; }).join(''); return '
🥞 '+esc(title)+'
Choose the exact Mini Pancake combo this extra belongs with.
'+rows+'
'; } function hasMissingMiniAddonTargets(){ return categoryCart('mini_pancakes').some(item=>needsMiniAddonTarget(item.name)&&miniAddonAssignments(item.name,item.qty).some(v=>!v)); } function compactItemCard([name,price]){ const amount=qty[type+'|'+name]||0; const flavourClass=(amount>0&&needsItemFlavours(type,name))?' item-with-flavour':''; const optionClass=(amount>0&&(needsItemFlavours(type,name)||needsBreakfastAddonTarget(name)))?' item-with-options':''; return '

'+esc(displayItemName(name))+'

'+money(price)+'
'+amount+'
'+itemFlavourSelectors(type,name,amount)+breakfastAddonSelectors(name,amount)+'
'; } function menuGroup(title,sub,items,note='',renderer=itemCard){ return ''; } function collapseBundleItems(items){ const grouped=[]; items.forEach(item=>{ const found=grouped.find(x=>x.name===item.name&&x.price===item.price); if(found)found.qty+=1;else grouped.push({name:item.name,price:item.price,qty:1}); }); return grouped; } function bundlePricing(c){ const subtotal=c.reduce((s,x)=>s+x.price*x.qty,0); if(type!=='baked') return {subtotal,discount:0,total:subtotal,bundles:[],singles:[]}; const units=[]; c.forEach(x=>{for(let i=0;ib.price-a.price||a.name.localeCompare(b.name)); let discount=0; const bundles=[]; const singles=[]; const bundleCount=Math.floor(units.length/3); for(let b=0;bsum+x.price,0); const nuttyCount=trio.filter(x=>x.name==='Nutty').length; const premiumCount=trio.filter(x=>PREMIUM_BREAD_NAMES.has(x.name)).length; const specialPrice=nuttyCount===3?80:[65,68,71,75][premiumCount]; const savings=Math.max(0,regularSubtotal-specialPrice); if(savings>0){ const name=nuttyCount===3 ? '3 Nutty' : ['3 Regular Minis','2 Regular + 1 Premium','1 Regular + 2 Premium','3 Premium Minis'][premiumCount]; discount+=savings; bundles.push({name,items:collapseBundleItems(trio),regularSubtotal,specialPrice,savings,premiumCount,nuttyOnly:nuttyCount===3}); }else{ singles.push(...trio); } } singles.push(...units.slice(bundleCount*3)); bundles.sort((a,b)=>{ const ar=a.nuttyOnly?99:a.premiumCount; const br=b.nuttyOnly?99:b.premiumCount; return ar-br; }); return {subtotal,discount,total:subtotal-discount,bundles,singles:collapseBundleItems(singles)}; } function summaryLine(x){ return '
'+esc(x.qty+'× '+displayItemName(x.name))+''+money(x.price*x.qty)+'
'; } function summarySection(title,items){ if(!items.length)return ''; return '
'+esc(title)+'
'+items.map(summaryLine).join('')+'
'; } function bakedBundleSummary(p,c){ if(!p.bundles.length)return c.length?summarySection('Banana Bread',c):'Your cart is empty.'; let html=''; p.bundles.forEach((bundle,index)=>{ html+='
' +'
'+esc(bundle.name)+'Bundle '+(index+1)+'
' +bundle.items.map(x=>'
'+esc(x.qty+'× '+displayItemName(x.name))+''+money(x.price*x.qty)+'
').join('') +'
🎀 '+esc(bundle.name)+' special'+money(bundle.specialPrice)+'Regular subtotal '+money(bundle.regularSubtotal)+' • You save '+money(bundle.savings)+'
' +'
'; }); if(p.singles.length){ const singleSubtotal=p.singles.reduce((sum,x)=>sum+x.price*x.qty,0); html+='
Single Minis
' +p.singles.map(x=>'
'+esc(x.qty+'× '+displayItemName(x.name))+''+money(x.price*x.qty)+'
').join('') +'
Singles subtotal'+money(singleSubtotal)+'
'; } return html; } function renderSummary(){ const c=cart(),p=bundlePricing(c); if(type==='baked'){ $('sumItems').innerHTML=bakedBundleSummary(p,c); }else{ const b=categoryCart('breakfast'),m=categoryCart('mini_pancakes'); $('sumItems').innerHTML=(b.length||m.length)?summarySection('Breakfast',b)+summarySection('Mini Pancakes',m):'Your morning cart is empty.'; } const subtotalLabel=$('subtotalRow').querySelector('span:first-child'); if(subtotalLabel)subtotalLabel.textContent=type==='baked'?'Items subtotal':'Subtotal'; $('sumSubtotal').textContent=money(p.subtotal); $('sumDiscount').textContent='−'+money(p.discount); $('bundleLabel').textContent='🎀 Bundle savings'; $('bundleRow').classList.toggle('hidden',p.discount<=0); $('sumTotal').textContent=money(p.total); } function formatDateValue(dt){const y=dt.getUTCFullYear(),m=String(dt.getUTCMonth()+1).padStart(2,'0'),d=String(dt.getUTCDate()).padStart(2,'0');return y+'-'+m+'-'+d} function ttTodayNoon(){ const parts=Object.fromEntries(new Intl.DateTimeFormat('en-CA',{timeZone:'America/Port_of_Spain',year:'numeric',month:'2-digit',day:'2-digit'}).formatToParts(new Date()).filter(p=>p.type!=='literal').map(p=>[p.type,p.value])); return new Date(Date.UTC(Number(parts.year),Number(parts.month)-1,Number(parts.day),12)); } function breakfastCutoffMs(value){return new Date(value+'T22:00:00-04:00').getTime()-86400000} function ttNowParts(){ const parts=Object.fromEntries(new Intl.DateTimeFormat('en-CA',{timeZone:'America/Port_of_Spain',year:'numeric',month:'2-digit',day:'2-digit',weekday:'short',hour:'2-digit',minute:'2-digit',hour12:false}).formatToParts(new Date()).filter(p=>p.type!=='literal').map(p=>[p.type,p.value])); return parts; } function currentMorningCutoffNotice(){ const p=ttNowParts(); const mins=Number(p.hour)*60+Number(p.minute); if(p.weekday==='Wed'&&mins>=22*60)return 'Thursday orders are closed for this week, but Friday orders are still open. 💕'; if(p.weekday==='Thu'&&mins>=22*60)return 'Friday orders are closed for this week. You can still preorder for next Thursday or Friday. 💕'; return 'Order anytime during the week. Thursday orders close Wednesday at 10:00 PM; Friday orders close Thursday at 10:00 PM.'; } function dateOptionsForDays(days,maxOptions=10,kind='baked'){ const today=ttTodayNoon(); const options=[]; for(let i=0;i<70&&options.length=breakfastCutoffMs(value))continue; options.push({value,label:new Intl.DateTimeFormat('en-TT',{timeZone:'UTC',weekday:'long',month:'short',day:'numeric',year:'numeric'}).format(dt)}); } return options; } function fillDateSelect(id,days,kind){ const select=$(id),current=select.value; const options=dateOptionsForDays(days,10,kind); select.innerHTML=options.length?options.map(o=>'').join(''):''; if(options.some(o=>o.value===current))select.value=current; } function loadPickupDates(){ fillDateSelect('bakedDate',[2],'baked'); fillDateSelect('morningDate',[4,5],'breakfast'); const n=$('morningCutoffNotice'); if(n)n.textContent=currentMorningCutoffNotice(); } function selectedFulfillmentDate(){return type==='baked'?$('bakedDate').value:$('morningDate').value} function timeOptionsForType(){ const startMinutes=((type==='breakfast'||type==='mini_pancakes')?6:8)*60; const endMinutes=((type==='breakfast'||type==='mini_pancakes')?11:15)*60; const out=[]; for(let total=startMinutes;total<=endMinutes;total+=15){ const h=Math.floor(total/60),m=total%60; const value=String(h).padStart(2,'0')+':'+String(m).padStart(2,'0'); const suffix=h<12?'AM':'PM'; const hour12=h===0?12:(h>12?h-12:h); out.push({value,label:hour12+':'+String(m).padStart(2,'0')+' '+suffix}); } return out; } function updateTimeOptions(){ const current=$('fulfillmentTime').value; const opts=timeOptionsForType(); $('fulfillmentTime').innerHTML=''+opts.map(o=>'').join(''); if(opts.some(o=>o.value===current))$('fulfillmentTime').value=current; $('timeNote').textContent=type==='baked'?'Banana bread pickup: every 15 minutes from 8:00 AM to 3:00 PM 💕':'Breakfast + Mini Pancakes pickup: every 15 minutes from 6:00 AM to 11:00 AM 💕'; } function updateDateControls(){ const morning=type==='breakfast'||type==='mini_pancakes'; $('bakedDateWrap').classList.toggle('hidden',type!=='baked'); $('morningDateWrap').classList.toggle('hidden',!morning); $('morningCartNote').classList.toggle('hidden',!morning); updateTimeOptions(); $('paymentDeadlineNote').textContent=morning ? '💳 A 50% online down payment is required to confirm your morning order. The remaining 50% is due at pickup. Breakfast + Mini Pancakes share one checkout and one total. Thursday orders close Wednesday at 10:00 PM. Friday orders close Thursday at 10:00 PM. The 50% down payment is due by the same cutoff.' : '💳 A 50% online down payment is required to confirm your order. The remaining 50% is due at pickup. Banana bread down payment must be received by Saturday at 6:00 PM.'; } function renderMenu(){ if(type==='baked'){ $('menu').classList.remove('grouped-menu','banana-vertical'); $('menu').classList.add('catalog-grid'); $('menu').innerHTML='

Banana Bread Specials

Mix and match your minis. Premium flavours are marked on the menu.

3 Regular MinisTT$65
2 Regular + 1 PremiumTT$68
1 Regular + 2 PremiumTT$71
3 Premium MinisTT$75
3 NuttyTT$80

Premium: Carrot Cream Cheese, Double Chocolate Fudge, Peanut Butter Chocolate & Nutty. Three Nutty minis use the TT$80 Nutty special.

'+MENU.baked.map(itemCard).join(''); }else if(type==='breakfast'){ $('menu').classList.remove('banana-vertical','catalog-grid'); $('menu').classList.add('grouped-menu'); $('menu').innerHTML= menuGroup('Breakfast Combos','Choose a combo. Its flavour option appears underneath only after you add it.',MENU.breakfastCombos)+ menuGroup('Breakfast Extras','Add a little extra to your breakfast.',MENU.breakfastExtras,'',compactItemCard)+ menuGroup('Drinks','Something fresh to sip with your breakfast.',MENU.breakfastDrinks); }else{ $('menu').classList.remove('banana-vertical','catalog-grid'); $('menu').classList.add('grouped-menu'); $('menu').innerHTML= menuGroup('Signature Combos','Choose a combo. Its flavour option appears underneath only after you add it.',MENU.miniCombos,'Please note: Every Signature Mini Pancake Combo includes 12 freshly made mini pancakes, finished with a light dusting of powdered sugar and the toppings and drizzle included with your chosen combo. Berry Bliss is available with blueberries, strawberries, or both.')+ menuGroup('Crunchy Extras','Extra crunch, sprinkled your way.',MENU.extraCrunch)+ menuGroup('Extra Fruit','Add a little more fruit for an additional charge.',MENU.extraFruit)+ menuGroup('Extra Sauces & Drizzles','Add an extra sauce or drizzle.',MENU.extraSauces); } document.querySelectorAll('.qbtn').forEach(b=>b.onclick=()=>{ const k=type+'|'+b.dataset.name; qty[k]=Math.max(0,Math.min(20,(qty[k]||0)+Number(b.dataset.delta))); syncItemFlavourCount(type,b.dataset.name,qty[k]); if(type==='breakfast')syncBreakfastAddonCount(b.dataset.name,qty[k]); if(type==='mini_pancakes')syncMiniAddonCount(b.dataset.name,qty[k]); renderMenu();renderSummary(); }); document.querySelectorAll('.item-flavour-select').forEach(select=>{ const saveSelection=()=>{ const category=select.dataset.category; const name=select.dataset.itemName; const index=Number(select.dataset.index); const key=itemFlavourKey(category,name); const count=qty[category+'|'+name]||0; const values=syncItemFlavourCount(category,name,count); values[index]=select.value; pancakeFlavorChoices[key]=values; }; select.addEventListener('input',saveSelection); select.addEventListener('change',saveSelection); select.addEventListener('pointerdown',e=>e.stopPropagation()); select.addEventListener('click',e=>e.stopPropagation()); }); document.querySelectorAll('.breakfast-addon-target-select').forEach(select=>{ const saveTarget=()=>{ const name=select.dataset.itemName; const index=Number(select.dataset.index); const key=breakfastTargetKey(name); const count=qty['breakfast|'+name]||0; const values=syncBreakfastAddonCount(name,count); values[index]=select.value; breakfastAddonTargets[key]=values; }; select.addEventListener('input',saveTarget); select.addEventListener('change',saveTarget); select.addEventListener('pointerdown',e=>e.stopPropagation()); select.addEventListener('click',e=>e.stopPropagation()); }); document.querySelectorAll('.mini-addon-target-select').forEach(select=>{ const saveTarget=()=>{ const name=select.dataset.itemName; const index=Number(select.dataset.index); const key=miniTargetKey(name); const count=qty['mini_pancakes|'+name]||0; const values=syncMiniAddonCount(name,count); values[index]=select.value; miniAddonTargets[key]=values; }; select.addEventListener('input',saveTarget); select.addEventListener('change',saveTarget); select.addEventListener('pointerdown',e=>e.stopPropagation()); select.addEventListener('click',e=>e.stopPropagation()); }); updateDateControls(); } document.querySelectorAll('.tab').forEach(t=>t.onclick=()=>{clearOrderError();type=t.dataset.type;document.querySelectorAll('.tab').forEach(x=>x.classList.toggle('active',x===t));renderMenu();renderSummary();checkStorefront(true)}); const requestedTab=new URLSearchParams(location.search).get('tab'); if(['baked','breakfast','mini_pancakes'].includes(requestedTab))type=requestedTab; document.querySelectorAll('.tab').forEach(x=>x.classList.toggle('active',x.dataset.type===type)); loadPickupDates(); async function checkStorefront(showClosed=true){ if(orderConfirmationOpen)return true; const isMorning=type==='breakfast'||type==='mini_pancakes'; if(isMorning){ $('closedSection').classList.add('hidden'); loadPickupDates(); const hasDate=Boolean($('morningDate').value); storefrontOpen=hasDate; const morningLabel=type==='breakfast'?'Breakfast':'Mini Pancake'; $('closedTitle').textContent=morningLabel+' orders are closed for now'; $('availabilityChip').textContent=hasDate?'💗 '+morningLabel+' Orders Open':'🔒 '+morningLabel+' Orders Closed'; $('availabilityChip').classList.toggle('open',hasDate); $('availabilityChip').classList.toggle('closed',!hasDate); $('closedMessage').textContent=hasDate?currentMorningCutoffNotice():'There are no '+morningLabel+' pickup dates currently available.'; $('closedSchedule').textContent='Breakfast + Mini Pancakes: Thursday orders close Wednesday at 10:00 PM; Friday orders close Thursday at 10:00 PM.'; $('closedSection').classList.toggle('hidden',hasDate||!showClosed); $('orderSection').classList.remove('hidden'); $('details').classList.toggle('hidden',!hasDate); if(hasDate)clearOrderError(); return hasDate; } try{ const r=await fetch(API_URL,{method:'GET',cache:'no-store'}); const data=await r.json(); if(!r.ok||!data.ok)throw new Error(data.error||'Could not check order availability.'); storefrontOpen=data.isOpen===true; $('closedTitle').textContent='Banana Bread orders are closed for now'; $('availabilityChip').textContent=storefrontOpen?'💗 Banana Bread Orders Open':'🔒 Banana Bread Orders Closed'; $('availabilityChip').classList.toggle('open',storefrontOpen); $('availabilityChip').classList.toggle('closed',!storefrontOpen); $('closedMessage').textContent=storefrontOpen ? (data.message||'Banana Bread ordering is available. 💕') : 'Banana Bread orders are closed right now. Breakfast + Mini Pancake preorders are still available. Tap the Breakfast or Mini Pancakes tab above to order. 💕'; $('closedSchedule').textContent='Banana Bread ordering window: '+(data.schedule||'Wednesday 6:00 AM to Saturday 6:00 PM'); $('closedSection').classList.toggle('hidden',storefrontOpen||!showClosed); $('orderSection').classList.remove('hidden'); $('details').classList.toggle('hidden',!storefrontOpen); if(storefrontOpen)clearOrderError(); return storefrontOpen; }catch(e){ storefrontOpen=false; $('closedTitle').textContent='Banana Bread orders are unavailable right now'; $('availabilityChip').textContent='🔒 Orders unavailable'; $('availabilityChip').classList.add('closed'); $('closedMessage').textContent='We could not check Banana Bread order availability right now. Please refresh the page in a moment. 💕'; $('closedSchedule').textContent='Banana Bread ordering window: Wednesday 6:00 AM to Saturday 6:00 PM'; if(showClosed)$('closedSection').classList.remove('hidden'); $('orderSection').classList.remove('hidden'); $('details').classList.add('hidden'); return false; } } function clearOrderError(){const box=$('errorBox');if(!box)return;box.textContent='';box.classList.add('hidden')} function fail(m){$('errorBox').textContent=m;$('errorBox').classList.remove('hidden');$('errorBox').scrollIntoView({behavior:'smooth',block:'center'})} async function submit(){ if(!(await checkStorefront(true)))return; const fulfillmentDate=selectedFulfillmentDate(); const customerEmail=$('email').value.trim().slice(0,160); const isMorning=type==='breakfast'||type==='mini_pancakes'; const breakfastItems=categoryCart('breakfast').map(x=>({name:x.name,qty:x.qty,...(needsItemFlavours('breakfast',x.name)?{flavors:itemFlavours('breakfast',x.name,x.qty)}:{}),...(needsBreakfastAddonTarget(x.name)?{targets:breakfastAddonAssignments(x.name,x.qty)}:{})})); const miniItems=categoryCart('mini_pancakes').map(x=>({name:x.name,qty:x.qty,...(needsItemFlavours('mini_pancakes',x.name)?{flavors:itemFlavours('mini_pancakes',x.name,x.qty)}:{}),...(needsMiniAddonTarget(x.name)?{targets:miniAddonAssignments(x.name,x.qty)}:{})})); const bakedItems=categoryCart('baked').map(x=>({name:x.name,qty:x.qty})); const activeItems=isMorning?[...breakfastItems,...miniItems]:bakedItems; if(!activeItems.length)return fail(isMorning?'Add at least one Breakfast or Mini Pancake item first.':'Add at least one menu item first.'); if(!$('customer').value.trim()||!$('phone').value.trim()||!fulfillmentDate)return fail('Please complete your name, phone number and pickup date.'); if(customerEmail&&!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(customerEmail))return fail('Please enter a valid email address or leave the email field blank.'); if(!$('fulfillmentTime').value)return fail('Please choose a pickup time.'); if(isMorning&&hasMiniExtras()&&!hasMiniCombo())return fail('Please add a Signature Mini Pancake Combo before choosing mini pancake extras.'); if(isMorning&&hasMissingItemFlavours('breakfast'))return fail('Please choose a pancake flavour for each selected Breakfast item.'); if(isMorning&&hasMissingItemFlavours('mini_pancakes'))return fail('Please choose a pancake flavour for each selected Mini Pancake combo.'); if(isMorning&&hasMissingBreakfastAddonTargets())return fail('Please add a Breakfast combo and choose which order gets each Extra Pancake, Eggs add-on or Sausages add-on.'); if(isMorning&&hasMissingMiniAddonTargets())return fail('Please choose which Mini Pancake combo gets each extra fruit, crunch topping, sauce or drizzle.'); if(type==='baked'){ const parts=fulfillmentDate.split('-').map(Number);const dow=new Date(parts[0],parts[1]-1,parts[2],12).getDay(); if(dow!==2)return fail('Banana bread pickup is available on Tuesdays only.'); }else{ const parts=fulfillmentDate.split('-').map(Number);const dow=new Date(parts[0],parts[1]-1,parts[2],12).getDay(); if(dow!==4&&dow!==5)return fail('Breakfast and Mini Pancake pickup is available on Thursdays or Fridays only.'); if(Date.now()>=breakfastCutoffMs(fulfillmentDate)){ loadPickupDates(); const day=dow===4?'Thursday':'Friday'; const next=$('morningDate').value; if(day==='Thursday'&&next)return fail('Thursday orders are closed for this week, but you can still place an order for Friday. Please choose Friday from the pickup date list.'); return fail(day+' orders are closed for this week. Please choose the next available pickup date.'); } } $('errorBox').classList.add('hidden');$('submitBtn').disabled=true;$('submitBtn').textContent='Sending your order…'; try{ const orderNotes=$('notes').value.trim(); const shared={customer:$('customer').value.trim(),phone:$('phone').value.trim(),email:customerEmail,fulfillmentType:'Pickup',fulfillmentDate,fulfillmentTime:$('fulfillmentTime').value,deliveryAddress:'',paymentMethod:$('paymentMethod').value,notes:orderNotes,website:$('website').value}; let payload; if(type==='baked'){ payload={...shared,type:'baked',items:bakedItems,pancakeFlavor:''}; }else if(breakfastItems.length&&miniItems.length){ payload={...shared,type:'morning_combo',breakfastItems,miniItems,breakfastPancakeFlavor:firstSelectedFlavour('breakfast'),miniPancakeFlavor:firstSelectedFlavour('mini_pancakes'),miniToppingsPreference:'On my pancakes'}; }else if(breakfastItems.length){ payload={...shared,type:'breakfast',items:breakfastItems,pancakeFlavor:firstSelectedFlavour('breakfast')}; }else{ payload={...shared,type:'mini_pancakes',items:miniItems,pancakeFlavor:firstSelectedFlavour('mini_pancakes'),notes:['Mini pancake toppings: On my pancakes',orderNotes].filter(Boolean).join(' • ')}; } const orderHeaders={'Content-Type':'application/json'}; const liveSession=(await sb.auth.getSession()).data.session; if(liveSession?.access_token)orderHeaders.Authorization='Bearer '+liveSession.access_token; const r=await fetch(API_URL,{method:'POST',headers:orderHeaders,body:JSON.stringify(payload)}); const data=await r.json();if(!r.ok||!data.ok)throw new Error(data.error||'Could not place order.'); $('orderNumber').textContent=data.orderNumber; $('successTotal').textContent=(Number(data.discount||0)>0?'Bundle savings: '+money(data.discount)+' • ':'')+'Order total: '+money(data.total)+(data.depositRequired!=null?' • 50% down payment: '+money(data.depositRequired):''); const paymentMessage=isMorning?'Your Breakfast + Mini Pancake selections were checked out together. Your order is PENDING PAYMENT. A 50% down payment must be received by 10:00 PM the night before pickup to confirm the order. The remaining 50% is due at pickup. 💕':'Your order is PENDING PAYMENT. A 50% down payment must be received by Saturday at 6:00 PM to confirm your order. The remaining 50% is due at pickup. 💕'; const emailMessage=customerEmail?(data.emailConfirmationSent?' 📩 A confirmation email was sent to '+customerEmail+'.':' Your order is saved, but we could not send the confirmation email. Please keep this confirmation screen and your order number handy.'):''; $('successPaymentNote').textContent=paymentMessage+emailMessage; orderConfirmationOpen=true; $('orderSection').classList.add('hidden');$('details').classList.add('hidden');$('closedSection').classList.add('hidden');$('success').classList.remove('hidden');window.scrollTo({top:0,behavior:'smooth'}); if(currentSession)loadCustomerOrders(); }catch(e){fail(e.message||'Could not place order. Please try again.')}finally{$('submitBtn').disabled=false;$('submitBtn').textContent='Place my order 💕'} } $('openAuthBtn').onclick=()=>{setAuthMode('login');openAccountModal()}; $('openAccountBtn').onclick=openAccountModal; $('closeAccountModal').onclick=closeAccountModal; $('accountModal').onclick=e=>{if(e.target===$('accountModal'))closeAccountModal()}; $('loginTab').onclick=()=>setAuthMode('login'); $('signupTab').onclick=()=>setAuthMode('signup'); $('authSubmitBtn').onclick=submitAuth; $('authPassword').addEventListener('keydown',e=>{if(e.key==='Enter')submitAuth()}); $('forgotPasswordBtn').onclick=showForgotPane; $('backToLoginBtn').onclick=()=>{setAuthMode('login');showAuthPane()}; $('sendResetBtn').onclick=requestPasswordReset; $('forgotEmail').addEventListener('keydown',e=>{if(e.key==='Enter')requestPasswordReset()}); $('saveNewPasswordBtn').onclick=saveNewPassword; $('confirmNewPassword').addEventListener('keydown',e=>{if(e.key==='Enter')saveNewPassword()}); $('saveProfileBtn').onclick=saveCustomerProfile; $('useSavedDetailsBtn').onclick=()=>{applySavedDetails($('profileName').value.trim(),$('profilePhone').value.trim(),currentSession?.user?.email||'');showAccountMessage('profileMessage','Saved details added to checkout. 💕');closeAccountModal()}; $('logoutBtn').onclick=logoutCustomer; sb.auth.onAuthStateChange((event,session)=>{ currentSession=session; if(event==='PASSWORD_RECOVERY'){ setTimeout(()=>showNewPasswordPane(),0); return; } setTimeout(()=>refreshAccountUi(),0); }); refreshAccountUi(); if(location.hash==='#account')setTimeout(()=>{setAuthMode('login');openAccountModal();},180); if(new URLSearchParams(location.search).get('reset')==='1'){ setTimeout(async()=>{ const {data:{session}}=await sb.auth.getSession(); if(session){currentSession=session;showNewPasswordPane();} },350); } $('submitBtn').onclick=submit; $('newOrderBtn').onclick=()=>{orderConfirmationOpen=false;location.reload()}; clearOrderError();renderMenu();renderSummary();checkStorefront(true); setInterval(()=>{if(orderConfirmationOpen)return;loadPickupDates();checkStorefront(true)},60000);