// components.jsx, shared building blocks (NL) const { useState, useEffect, useRef, useMemo } = React; // ───────────────────────────────────────────────────────────────────────────── // Icons (inline SVG, small + clean) // ───────────────────────────────────────────────────────────────────────────── const Icon = { Menu: () => , Search: () => , User: () => , Bag: () => , ArrowR: () => , ArrowL: () => , X: () => , Star: ({ filled = true, half = false }) => , Check: () => , Plus: () => , Minus: () => }; // ───────────────────────────────────────────────────────────────────────────── // Data // ───────────────────────────────────────────────────────────────────────────── const PRODUCTS = [ { id: 'lite-300', name: 'LITE 300', sub: 'Desktop Rood Licht Paneel', cat: 'panel', tag: 'Bestseller', price: 279, was: 399, rating: 4.8, reviews: 220, desc: 'Een compact desktoppaneel voor gezicht, hoofdhuid en gerichte herstelsessies. 660nm rood + 850nm nabij-infrarood in één armatuur.', badges: ['Gericht', 'Beste prijs'], images: ['img/lite300-2.png', 'img/lite300-1.png', 'img/lite300-3.png', 'img/lite300-4.png'] }, { id: 'lite-1500', name: 'LITE 1500', sub: 'Half-Body Rood Licht Paneel', cat: 'panel', tag: 'Sale', price: 1044, was: 1499, rating: 4.9, reviews: 383, desc: 'Half-body bedekking voor huid, herstel en circadiaanse ondersteuning. Dagelijkse sessies in minder dan 15 minuten.', badges: ['Half-body', 'Premium'], images: ['img/lite1500-2.png', 'img/lite1500-1.png', 'img/lite1500-stand.jpg', 'img/lite1500-3.png'] }, { id: 'core-300', name: 'CORE 300', sub: 'Gericht Rood Licht Paneel', cat: 'panel', tag: 'Premium', price: 349, was: 499, rating: 4.9, reviews: 142, desc: 'Hogere irradiantie voor gerichte behandelingen. Puls- en flikkervrije werking.', badges: ['Gericht', 'Premium'], images: ['img/core300-2.png', 'img/core300-1.png', 'img/core300-3.png', 'img/core300-4.png'] }, { id: 'core-1500', name: 'CORE 1500', sub: 'Full-Body Rood Licht Paneel', cat: 'panel', tag: 'Premium', price: 1179, was: 1699, rating: 4.9, reviews: 96, desc: 'Klinische full-body irradiantie met premium afwerking. 4 golflengtes, lage EMF, modulaire bevestiging.', badges: ['Full-body', 'Premium'], images: ['img/duo-labels.png', 'img/hero-duo-lifestyle.png', 'img/lite1500-2.png', 'img/duo-labels-2.png'] }, { id: 'core-duo', name: 'CORE 1500 DUO', sub: 'Full-Body Combi Set', cat: 'bundle', tag: 'Combi', price: 2199, was: 3797, rating: 5.0, reviews: 44, desc: 'Twee CORE 1500 panelen voor volledige lichaamsbedekking. Inclusief muurmontage.', badges: ['Bundel', 'Beste deal'], images: ['img/duo-labels.png', 'img/hero-duo-lifestyle.png', 'img/hero-duo-lifestyle-2.png', 'img/duo-labels-2.png'] }, { id: 'aura', name: 'AURA+', sub: 'LED Gezichts- & Décolletémasker', cat: 'mask', tag: 'Nieuw', price: 344.95, was: 599.90, rating: 4.8, reviews: 312, desc: 'Flexibel siliconen LED-masker voor gezicht en décolleté. 4 golflengtes, handsfree, sessies van 10 minuten.', badges: ['Gezicht', 'Premium'], images: ['img/aura-product.png', 'img/aura-lifestyle.png', 'img/aura-product-2.png', 'img/aura-lifestyle-2.png'] }, { id: 'blanket', name: 'Infrarood Sauna Deken', sub: 'Detox & Herstel', cat: 'sauna', tag: 'Beste deal', price: 399, was: 499, rating: 4.7, reviews: 261, desc: 'Infrarooddeken met lage EMF en niet-toxische binnenkant. Warmt op tot 75°C in minder dan 15 minuten.', badges: ['Herstel', 'Detox'], images: ['img/blanket-1.png', 'img/blanket-2.png', 'img/blanket-3.png', 'img/blanket-4.png'] }, { id: 'lite-bed', name: 'LITE Bed', sub: 'Full-Body Therapiebed', cat: 'bed', tag: 'Pro', price: 6499, was: 7999, rating: 4.9, reviews: 18, desc: 'Horizontaal therapiebed voor het hele lichaam. Voor klinieken, studio’s en serieuze thuissetups.', badges: ['Full-body', 'Pro'], images: ['img/lite1500-stand.jpg', 'img/lite1500-stand-alt.jpg', 'img/lite1500-2.png', 'img/hero-duo-lifestyle.png'] }]; const CATEGORIES = [ { id: 'all', label: 'Alles' }, { id: 'panel', label: 'Panelen' }, { id: 'mask', label: 'Masker' }, { id: 'sauna', label: 'Sauna' }, { id: 'bed', label: 'Bedden' }, { id: 'bundle', label: 'Bundels' }]; const NAV = [ { id: 'shop', label: 'Alle producten' }, { id: 'panels', label: 'Panelen', cat: 'panel' }, { id: 'mask', label: 'Masker', cat: 'mask' }, { id: 'blanket', label: 'Deken', cat: 'sauna' }, { id: 'beds', label: 'Bedden', cat: 'bed' }, { id: 'best', label: 'Bestsellers' }]; const MEGA = { shop: { title: 'Shop', links: ['Panelen', 'AURA+ Masker', 'Saunadeken', 'Therapiebedden', 'Accessoires', 'Alles bekijken'], products: [ { id: 'core-1500', label: 'CORE 1500\nFull-Body Paneel' }, { id: 'aura', label: 'AURA+\nLED Masker' }] }, panels: { title: 'Rood Licht Panelen', links: ['LITE 300', 'LITE 1500', 'CORE 300', 'CORE 1500', 'CORE 1500 DUO', 'Alle panelen'], products: [ { id: 'lite-1500', label: 'LITE 1500\nHalf-Body' }, { id: 'core-1500', label: 'CORE 1500\nFull-Body' }] }, mask: { title: 'AURA+ Masker', links: ['AURA+ Gezichtsmasker', 'AURA+ Décolleté', 'AURA+ Bundel', 'Accessoires'], products: [ { id: 'aura', label: 'AURA+\nGezichtsmasker' }, { id: 'aura', label: 'AURA+ Décolleté\nBundel' }] }, blanket: { title: 'Saunadeken', links: ['Infrarooddeken', 'Inlegvellen', 'Reistas', 'Accessoires'], products: [ { id: 'blanket', label: 'Infrarood\nSaunadeken' }, { id: 'blanket', label: 'Deken\n+ Inlegvel set' }] }, beds: { title: 'Therapiebedden', links: ['LITE Bed', 'CORE Bed', 'PRO Bed', 'Voor klinieken', 'Financiering'], products: [ { id: 'lite-bed', label: 'LITE Bed\nFull-Body' }, { id: 'lite-bed', label: 'PRO Bed\nKlinieken' }] }, best: { title: 'Bestsellers', links: ['Meest geliefd', 'Nieuw binnen', 'In de uitverkoop', 'Alles bekijken'], products: [ { id: 'lite-300', label: 'LITE 300\nDesktop Paneel' }, { id: 'aura', label: 'AURA+\nLED Masker' }, { id: 'blanket', label: 'Infrarood\nDeken' }] } }; const findProduct = (id) => PRODUCTS.find((p) => p.id === id) || PRODUCTS[0]; // ───────────────────────────────────────────────────────────────────────────── // Promo bar // ───────────────────────────────────────────────────────────────────────────── function PromoBar() { const items = [ '120 Dagen Risicovrij Thuis Uitproberen', 'LENTE SALE: TIJDELIJK TOT 40% KORTING', 'Gratis Verzekerde Verzending', '★★★★★ 4,8/5 UIT 185+ RECENSIES', 'Achteraf betalen met Klarna']; const loop = [...items, ...items, ...items]; return (
{loop.map((m, i) => {m} )}
); } // ───────────────────────────────────────────────────────────────────────────── // Header (sticky, centered wordmark) // ───────────────────────────────────────────────────────────────────────────── function Header({ onNav, onOpenMega, onCloseMega, openMega, onOpenCart, cartCount, currentPage }) { return (
{NAV.map((it) => )}
{onCloseMega();onNav('home');}}> Nuvibody
); } // ───────────────────────────────────────────────────────────────────────────── // Mega menu overlay // ───────────────────────────────────────────────────────────────────────────── function MegaMenu({ open, onClose, onNav }) { const data = open ? MEGA[open] : null; const isOpen = !!data; useEffect(() => { if (!isOpen) return; const onKey = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [isOpen, onClose]); return ( <>
{data && <>
{data.title}
{data.links.map((l, i) => { const isFirst = i === 0; return ( {onNav('shop');onClose();}}> {l}); })}
Nederland EUR
{data.products.map((p, i) => { const prod = findProduct(p.id); return ( {onNav('product', p.id);onClose();}}>
{prod.name}
{p.label}
); })}
}
); } // ───────────────────────────────────────────────────────────────────────────── // Star rating bit // ───────────────────────────────────────────────────────────────────────────── function Stars({ rating, reviews }) { return (
{[1, 2, 3, 4, 5].map((i) => )} ({reviews})
); } // ───────────────────────────────────────────────────────────────────────────── // Price formatter // ───────────────────────────────────────────────────────────────────────────── const fmt = (n) => '€' + n.toLocaleString('nl-NL', { minimumFractionDigits: n % 1 ? 2 : 0, maximumFractionDigits: 2 }); // ───────────────────────────────────────────────────────────────────────────── // Product card // ───────────────────────────────────────────────────────────────────────────── function ProductCard({ product, onView, onAdd, slotPrefix = 'card' }) { const [imgIdx, setImgIdx] = useState(0); const images = product.images || []; const total = images.length || 1; const cycle = (delta) => setImgIdx((imgIdx + total + delta) % total); return (
onView(product.id)}> {product.tag && {product.tag}} {images.map((src, i) => {`${product.name} )}
{Array.from({ length: total }).map((_, i) => )}
onView(product.id)}>
Nuvibody {product.name}
{product.sub}
{product.was && {fmt(product.was)}} {fmt(product.price)}
); } // ───────────────────────────────────────────────────────────────────────────── // Mini cart // ───────────────────────────────────────────────────────────────────────────── function MiniCart({ open, onClose, items, onChangeQty, onRemove, onCheckout }) { const total = items.reduce((s, i) => s + i.product.price * i.qty, 0); return ( <>
); } // ───────────────────────────────────────────────────────────────────────────── // Toast (added to bag confirmation) // ───────────────────────────────────────────────────────────────────────────── function Toast({ msg, show }) { return
{msg}
; } // ───────────────────────────────────────────────────────────────────────────── // Footer // ───────────────────────────────────────────────────────────────────────────── function Footer({ onNav }) { return ( ); } // ───────────────────────────────────────────────────────────────────────────── // Trust strip (4-up) // ───────────────────────────────────────────────────────────────────────────── function TrustStrip() { const items = [ { label: '120 Dagen op Proef', sub: 'Risicovrij thuis uitproberen' }, { label: 'Achteraf betalen', sub: 'Shop nu, betaal later met Klarna' }, { label: 'Tot 5 Jaar Garantie', sub: 'Op uitgekozen modellen' }, { label: 'Gratis Verzending', sub: 'Verzekerd, in 2–4 dagen' }]; return (
{items.map((it, i) =>
{it.sub} {it.label}
)}
); } // ───────────────────────────────────────────────────────────────────────────── // Press strip (marquee) // ───────────────────────────────────────────────────────────────────────────── function PressStrip() { const items = [ { logo: 'Noor, 44', quote: 'Ik gebruik het nu drie maanden en mijn huid is rustiger dan ooit. Minder roodheid, gladder.' }, { logo: 'James, 28', quote: 'Eerste 2 weken voelde ik me moe, daarna ineens lichter, energieker. Alsof er iets "aan" ging.' }, { logo: 'Lisette, 52', quote: 'Na jaren fibromyalgie voelde ik voor het eerst verlichting die niet uit een potje kwam. Geen wondermiddel, maar wél echt effect.' }, { logo: 'Holly D., 34', quote: 'Wat mij verraste, was hoeveel rust het in m\u2019n hoofd bracht. Ik slaap beter, maar voel me vooral helderder overdag.' }, { logo: 'Marijke, 49', quote: 'Ik gebruik het met mijn dochter. Zij tegen acne, ik tegen pigmentvlekken. We zien allebei resultaat.' }, { logo: 'Petra, 48', quote: 'Sinds ik dit gebruik, heb ik overdag meer focus, \u2019s avonds meer rust. Ik voel me gewoon stabieler.' }]; const loop = [...items, ...items]; return (
{loop.map((it, i) =>
{it.logo}
{it.quote}
)}
); } // Expose to global scope Object.assign(window, { Icon, PRODUCTS, CATEGORIES, NAV, MEGA, findProduct, fmt, PromoBar, Header, MegaMenu, ProductCard, MiniCart, Toast, Footer, TrustStrip, PressStrip, Stars });