// app.jsx, Root app with routing, cart state, tweaks const { useState, useEffect, useRef, useMemo } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "heroVariant": "b", "accent": "#c8392e", "bgTone": "cream", "headlineFont": "Plus Jakarta", "cardRadius": 14, "density": "airy" }/*EDITMODE-END*/; const BG_TONES = { cream: { bg: '#f4ede0', card: '#ebe3d4' }, offwhite: { bg: '#f8f5ee', card: '#ece6d8' }, cool: { bg: '#eeece8', card: '#e1ddd5' }, warm: { bg: '#f0e6d3', card: '#e3d7be' }, }; const ACCENT_PRESETS = ['#c8392e', '#1a1a1a', '#3a5a40', '#8b5cf6']; const FONT_PRESETS = { 'Geist': "'Geist', system-ui, sans-serif", 'Plus Jakarta': "'Plus Jakarta Sans', system-ui, sans-serif", 'DM Sans': "'DM Sans', system-ui, sans-serif", }; function App() { const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); const [page, setPage] = useState('home'); const [productId, setProductId] = useState('lite-1500'); const [openMega, setOpenMega] = useState(null); const [cartOpen, setCartOpen] = useState(false); const [cart, setCart] = useState([]); const [toast, setToast] = useState({ show: false, msg: '' }); // Apply tweaks to :root useEffect(() => { const root = document.documentElement; const tone = BG_TONES[t.bgTone] || BG_TONES.cream; root.style.setProperty('--bg', tone.bg); root.style.setProperty('--bg-card', tone.card); root.style.setProperty('--accent', t.accent); root.style.setProperty('--radius-card', t.cardRadius + 'px'); root.style.setProperty('--font-display', FONT_PRESETS[t.headlineFont] || FONT_PRESETS.Geist); root.style.setProperty('--font-body', FONT_PRESETS[t.headlineFont] || FONT_PRESETS.Geist); document.body.setAttribute('data-density', t.density); }, [t]); // Close mega on page change useEffect(() => { setOpenMega(null); }, [page]); // Scroll to top on page change useEffect(() => { window.scrollTo({ top: 0, behavior: 'instant' }); }, [page, productId]); // Nav handler const nav = (to, payload) => { if (to === 'product' && payload) setProductId(payload); if (to === 'panels' || to === 'mask' || to === 'blanket' || to === 'beds' || to === 'best') { setPage('shop'); } else { setPage(to); } setOpenMega(null); }; // Cart handlers const addToCart = (product) => { setCart((prev) => { const existing = prev.find(i => i.product.id === product.id); if (existing) { return prev.map(i => i.product.id === product.id ? { ...i, qty: i.qty + 1 } : i); } return [...prev, { product, qty: 1 }]; }); setToast({ show: true, msg: `${product.name} toegevoegd aan je winkelmand` }); setTimeout(() => setToast(t => ({ ...t, show: false })), 2200); }; const changeQty = (id, delta) => { setCart(prev => prev .map(i => i.product.id === id ? { ...i, qty: i.qty + delta } : i) .filter(i => i.qty > 0)); }; const removeItem = (id) => setCart(prev => prev.filter(i => i.product.id !== id)); const cartCount = cart.reduce((s, i) => s + i.qty, 0); let PageEl; if (page === 'home') PageEl = ; else if (page === 'shop') PageEl = ; else if (page === 'product') PageEl = ; else if (page === 'about') PageEl = ; else PageEl = ; return ( <> setOpenMega(id)} onCloseMega={() => setOpenMega(null)} openMega={openMega} onOpenCart={() => setCartOpen(true)} cartCount={cartCount} currentPage={page} /> setOpenMega(null)} onNav={nav} /> {PageEl} setCartOpen(false)} items={cart} onChangeQty={changeQty} onRemove={removeItem} onCheckout={() => { setToast({ show: true, msg: 'Afrekenen is een prototype, zou doorverwijzen naar Shopify' }); setTimeout(() => setToast(t => ({ ...t, show: false })), 2400); }} /> { setTweak('heroVariant', v); setPage('home'); }} /> setTweak('accent', v)} /> setTweak('bgTone', v)} /> setTweak('headlineFont', v)} /> setTweak('cardRadius', v)} /> setTweak('density', v)} /> setPage('home')} /> setPage('shop')} /> { setProductId('lite-1500'); setPage('product'); }} /> setPage('about')} /> setCartOpen(true)} /> > ); } ReactDOM.createRoot(document.getElementById('root')).render();