// ============================================================ // App — router + global state (cart, drawer, toast) // ============================================================ const { useState, useEffect, useCallback, useRef } = React; function App() { const [route, setRoute] = useState(window.location.hash.slice(1) || 'home'); const [cart, setCart] = useState(() => { try { return JSON.parse(localStorage.getItem('vpx_cart') || '[]'); } catch { return []; } }); const [cartOpen, setCartOpen] = useState(false); const [toast, setToast] = useState({ show: false, message: '' }); const toastTimer = useRef(null); // hash routing useEffect(() => { const onHash = () => { setRoute(window.location.hash.slice(1) || 'home'); window.scrollTo({ top: 0, behavior: 'instant' }); }; window.addEventListener('hashchange', onHash); return () => window.removeEventListener('hashchange', onHash); }, []); // persist cart useEffect(() => { localStorage.setItem('vpx_cart', JSON.stringify(cart)); }, [cart]); const navigate = useCallback((r) => { window.location.hash = r; setRoute(r); window.scrollTo({ top: 0, behavior: 'instant' }); }, []); const showToast = useCallback((message) => { setToast({ show: true, message }); if (toastTimer.current) clearTimeout(toastTimer.current); toastTimer.current = setTimeout(() => setToast({ show: false, message: '' }), 2400); }, []); const addToCart = useCallback((product, qty = 1) => { setCart(prev => { const existing = prev.find(it => it.id === product.id); if (existing) { return prev.map(it => it.id === product.id ? { ...it, qty: it.qty + qty } : it); } return [...prev, { id: product.id, name: product.name, sku: product.sku, price: product.price, image: product.image, qty }]; }); showToast(`Added ${product.name} to cart`); }, [showToast]); const updateQty = useCallback((id, qty) => { if (qty < 1) { setCart(prev => prev.filter(it => it.id !== id)); return; } setCart(prev => prev.map(it => it.id === id ? { ...it, qty } : it)); }, []); const removeItem = useCallback((id) => { setCart(prev => prev.filter(it => it.id !== id)); }, []); const cartCount = cart.reduce((s, it) => s + it.qty, 0); // route -> page let page; if (route === 'home') page = ; else if (route.startsWith('series-')) page = ; else if (route === 'shop') page = ; else if (route.startsWith('product-')) page = ; else if (route === 'app') page = ; else if (route === 'about') page = ; else page = ; // nav active state: collapse all series-* / product-* into matching link let navActive = route; if (route.startsWith('series-')) navActive = route; else if (route.startsWith('product-')) { const p = getProduct(route.slice(8)); if (p) navActive = `series-${p.series}`; } return ( <>