const { useState, useEffect, useRef, useContext, createContext } = React; const CFG = window.CRAVE_CONFIG; const PRICE = CFG.price; const FLAVORS = CFG.flavors; const FL = Object.fromEntries(FLAVORS.map(f => [f.id, f])); const SIZES = CFG.sizes; const MAX_BOX = CFG.maxBox; const DAYS = CFG.days; const SLOTS = CFG.slots; const FULL = {}; const PRESETS = CFG.presets; const layoutOf = n => LAYOUT[n] || [n <= 12 ? Math.ceil(n / 2) : 6]; const LAYOUT = { 3: [3, 1], 4: [2, 2], 5: [5, 1], 6: [3, 2], 8: [4, 2], 12: [4, 3] }; const ShopCtx = createContext(null); const useShop = () => useContext(ShopCtx); const euro = n => n.toFixed(2).replace('.', ',') + ' €'; const boxLabel = n => 'Box ' + n; const unitPrice = n => PRICE; const sizePrice = n => n * unitPrice(n); const boxPrice = b => b.price ?? sizePrice(b.size); const euroShort = v => (Number.isInteger(v) ? v : v.toFixed(2).replace('.', ',')) + '€'; const summarize = items => { const c = {}; items.forEach(id => { c[id] = (c[id] || 0) + 1; }); return Object.entries(c).map(([id, n]) => n + '× ' + FL[id].name); }; function Cookie({ f, size, style, className }) { const [cx, cy, d] = f.crop; const s = size / d; return
; } function Title({ children, size = 40, className = '', style, lg }) { const flat = c => typeof c === 'string' || typeof c === 'number' ? String(c) : Array.isArray(c) ? c.map(flat).join('') : (c && c.props ? flat(c.props.children) : ''); const lines = flat(children).split('\n'); return
{lines.map((l, i) =>
{l}
)}
; } function BoxView({ size, items, width, onRemove, onDropCookie }) { const [cols] = layoutOf(size); const bag = false; const pad = Math.round(width * 0.06), gap = Math.round(width * 0.028); const slot = Math.floor((width - pad * 2 - gap * (cols - 1)) / cols); const [over, setOver] = useState(false); const drop = onDropCookie ? { onDragOver: e => { e.preventDefault(); setOver(true); }, onDragLeave: () => setOver(false), onDrop: e => { e.preventDefault(); setOver(false); const id = e.dataTransfer.getData('text/plain'); if (id) onDropCookie(id); }, } : {}; const cells = Array.from({ length: size }, (_, i) => items[i]); const fs = Math.max(8, width * 0.02); return (
{cells.map((id, i) => (
{id ? ( ) : {i + 1}}
))}
); } function Steps({ at }) { const { go, m, cart } = useShop(); const s = [['compose', 'Ta box'], ['cart', 'Panier'], ['checkout', 'Créneau & infos']]; const idx = s.findIndex(x => x[0] === at); return (
{s.map(([id, l], i) => ( {i ? : null} ))}
); } function SizeIcon({ n }) { const [cols] = layoutOf(n); return {Array.from({ length: n }).map((_, i) => )}; } function Marquee() { const { open, soon } = useShop(); const items = open ? ['Pre-orders open', 'Commande en semaine', 'Cookies le week-end', 'Dès ' + CFG.minPieces + ' cookies', euroShort(PRICE) + ' le cookie', 'Livraison disponible dès ' + CFG.deliveryMin + ' pièces*', 'Paiement à la remise', ...(soon ? ['Pastries coming soon'] : [])] : ['Pre-orders closed', 'Réouverture annoncée sur Instagram', CFG.instagram]; const row = [...items, ...items, ...items]; return (
{[...row, ...row].map((t, i) => {t}·)}
); } function Header() { const { go, page, m, count } = useShop(); const [menu, setMenu] = useState(false); const links = [['menu', 'Menu'], ['about', 'À propos'], ['faq', 'FAQ']]; const { open } = useShop(); const nav = id => { setMenu(false); go(id); }; return (
{!m ? : null}
{!m ? : null}
); } function TabBar() { const { go, page, count, open } = useShop(); const I = { home: , menu: , faq: , cart: , }; const tab = (id, label, on) => ( ); return ( ); } function Footer() { const { go, m } = useShop(); return ( ); } Object.assign(window, { MAX_BOX, layoutOf, unitPrice, sizePrice, euroShort, boxPrice, TabBar, PRESETS, Steps, SizeIcon, PRICE, FLAVORS, FL, SIZES, DAYS, SLOTS, FULL, LAYOUT, ShopCtx, useShop, euro, boxLabel, summarize, Cookie, Title, BoxView, Header, Footer });