// === Shared UI components for Tour Eiffel 2000 ===
const { useState, useEffect, useMemo, useRef } = React;
// SVG icons
const Icon = {
Search: () => (),
Cart: () => (),
Sun: () => (),
Moon: () => (),
Arrow: () => (),
Close: () => (),
Plus: () => (),
Minus: () => (),
};
// === Cart store (localStorage) ===
const CART_KEY = 'te2000_cart';
function readCart() {
try { return JSON.parse(localStorage.getItem(CART_KEY) || '[]'); } catch { return []; }
}
function writeCart(items) {
localStorage.setItem(CART_KEY, JSON.stringify(items));
window.dispatchEvent(new CustomEvent('cart-updated'));
}
function useCart() {
const [items, setItems] = useState(readCart());
useEffect(() => {
const sync = () => setItems(readCart());
window.addEventListener('cart-updated', sync);
window.addEventListener('storage', sync);
return () => {
window.removeEventListener('cart-updated', sync);
window.removeEventListener('storage', sync);
};
}, []);
return {
items,
add: (item) => {
const existing = items.find(i => i.key === item.key);
const next = existing
? items.map(i => i.key === item.key ? { ...i, qty: i.qty + 1 } : i)
: [...items, { ...item, qty: 1 }];
writeCart(next);
},
remove: (key) => writeCart(items.filter(i => i.key !== key)),
setQty: (key, qty) => writeCart(items.map(i => i.key === key ? { ...i, qty } : i)),
clear: () => writeCart([]),
total: items.reduce((s, i) => s + i.price * i.qty, 0),
count: items.reduce((s, i) => s + i.qty, 0),
};
}
// === Theme ===
function useTheme() {
const [theme, setTheme] = useState(() => localStorage.getItem('te2000_theme') || 'light');
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('te2000_theme', theme);
}, [theme]);
return [theme, setTheme];
}
// === Header ===
function Header({ active }) {
const cart = useCart();
const [theme, setTheme] = useTheme();
return (