// app.jsx — Root del sitio MiPC y su Mundo // Maneja routing por hash, carrito persistente y tweaks en vivo. const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "primary": "#E30613", "accent": "#1E40AF", "dark": false, "showBanners": true, "homeVariant": "A", "density": 4, "fontFamily": "Inter" }/*EDITMODE-END*/; // ── Helpers de routing por hash ────────────────────────────────────── function parseHash() { const h = (window.location.hash || '#/').replace(/^#/, ''); const [pathRaw, queryRaw=''] = h.split('?'); const path = pathRaw || '/'; const query = Object.fromEntries(new URLSearchParams(queryRaw)); return { path, query }; } function useRoute() { const [r, setR] = React.useState(parseHash()); React.useEffect(() => { const onHash = () => setR(parseHash()); window.addEventListener('hashchange', onHash); return () => window.removeEventListener('hashchange', onHash); }, []); return r; } function navigate(path, q) { let h = '#' + path; if (q) h += '?' + new URLSearchParams(q).toString(); window.location.hash = h.slice(1); // setting hash triggers hashchange window.scrollTo({ top: 0, behavior: 'instant' }); } // ── Carrito persistente ───────────────────────────────────────────── function useCart() { const [cart, setCart] = React.useState(() => { try { return JSON.parse(localStorage.getItem('mipc-cart') || '[]'); } catch (e) { return []; } }); React.useEffect(() => { try { localStorage.setItem('mipc-cart', JSON.stringify(cart)); } catch (e) {} }, [cart]); const addToCart = (p, qty = 1) => { setCart(c => { const ix = c.findIndex(it => it.p.id === p.id); if (ix >= 0) { const next = [...c]; next[ix] = { ...next[ix], qty: Math.min((next[ix].qty || 0) + qty, p.stock || 99) }; return next; } return [...c, { p, qty }]; }); // Toast simple showToast(`✓ ${p.nombre.slice(0,42)}${p.nombre.length>42?'…':''} agregado al carrito`); }; const setQty = (id, qty) => { setCart(c => qty <= 0 ? c.filter(it => it.p.id !== id) : c.map(it => it.p.id === id ? { ...it, qty } : it)); }; const removeItem = (id) => setCart(c => c.filter(it => it.p.id !== id)); const clearCart = () => setCart([]); return { cart, addToCart, setQty, removeItem, clearCart }; } // Toast accesible y autoremovible function showToast(msg) { let el = document.getElementById('mipc-toast'); if (!el) { el = document.createElement('div'); el.id = 'mipc-toast'; el.className = 'toast'; document.body.appendChild(el); } el.textContent = msg; el.classList.add('show'); clearTimeout(el._t); el._t = setTimeout(() => el.classList.remove('show'), 2400); } // ── App ────────────────────────────────────────────────────────────── function App() { const route = useRoute(); const { cart, addToCart, setQty, removeItem, clearCart } = useCart(); const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); const [query, setQuery] = React.useState(route.query.q || ''); // Aplicar tweaks al :root como CSS vars React.useEffect(() => { const root = document.documentElement; root.style.setProperty('--brand-primary', t.primary); root.style.setProperty('--brand-accent', t.accent); root.dataset.theme = t.dark ? 'dark' : 'light'; root.style.setProperty('--font-family', t.fontFamily === 'Manrope' ? "'Manrope', system-ui, sans-serif" : t.fontFamily === 'DM Sans' ? "'DM Sans', system-ui, sans-serif" : t.fontFamily === 'Plus Jakarta' ? "'Plus Jakarta Sans', system-ui, sans-serif" : "'Inter', system-ui, sans-serif" ); }, [t.primary, t.accent, t.dark, t.fontFamily]); const onNavigate = (p) => navigate(p); const onSearch = (q) => { if (!q?.trim()) return; navigate('/buscar', { q }); }; React.useEffect(() => { setQuery(route.query.q || ''); }, [route.query.q]); const cartCount = cart.reduce((s, it) => s + it.qty, 0); // Routing let page; if (route.path === '/' || route.path === '') { page = ; } else if (route.path.startsWith('/categoria/')) { const cat = route.path.split('/')[2]; page = ; } else if (route.path.startsWith('/producto/')) { const id = route.path.split('/')[2]; page = ; } else if (route.path === '/carrito') { page = ; } else if (route.path === '/checkout') { page = ; } else if (route.path === '/login') { page = ; } else if (route.path === '/buscar') { page = ; } else { page =

Página no encontrada

; } return ( <>
{page}