// App principal — orquesta todo const { useState, useEffect, useRef, useCallback } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "heroVariant": "warm", "seasonalAtmosphereEnabled": true, "rippleEnabled": true, "defaultCurrency": "ARS", "accentTone": "sage" }/*EDITMODE-END*/; function App() { const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS); const data = window.SHOP_DATA; const [products, setProducts] = useState(data.products); const [view, setView] = useState({ name: "tab", tab: "home" }); const [history, setHistory] = useState([]); const [cart, setCart] = useState([]); const [wish, setWish] = useState(new Set(["p02", "p04"])); const [cartOpen, setCartOpen] = useState(false); const [coupon, setCoupon] = useState(""); const [currency, setCurrency] = useState(tweaks.defaultCurrency || "ARS"); const [adminOpen, setAdminOpen] = useState(false); const [orderId, setOrderId] = useState(null); const [toast, setToast] = useState(""); const [filter, setFilter] = useState("all"); const [flyDots, setFlyDots] = useState([]); const cartCount = cart.reduce((s, i) => s + i.qty, 0); const points = 612; // Apply theme accent useEffect(() => { document.documentElement.style.setProperty( "--accent", tweaks.accentTone === "terra" ? "var(--terra-500)" : "var(--sage-700)" ); }, [tweaks.accentTone]); // Cart actions const addToCart = useCallback((product, anchor, qty = 1) => { setCart(c => { const existing = c.find(i => i.id === product.id); if (existing) return c.map(i => i.id === product.id ? { ...i, qty: i.qty + qty } : i); return [...c, { id: product.id, qty }]; }); // Fly-to-cart animation if (anchor) { const start = anchor.getBoundingClientRect(); const cartEl = document.getElementById("cart-anchor"); const end = cartEl ? cartEl.getBoundingClientRect() : { left: window.innerWidth - 50, top: 30, width: 38, height: 38 }; const id = Date.now() + Math.random(); setFlyDots(d => [...d, { id, startX: start.left + start.width / 2 - 13, startY: start.top + start.height / 2 - 13, endX: end.left + end.width / 2 - 13, endY: end.top + end.height / 2 - 13, tone: product.tones[0], }]); setTimeout(() => setFlyDots(d => d.filter(x => x.id !== id)), 700); } setToast("Agregado al carrito"); }, []); const updateQty = (id, qty) => { if (qty <= 0) return setCart(c => c.filter(i => i.id !== id)); setCart(c => c.map(i => i.id === id ? { ...i, qty } : i)); }; const removeFromCart = (id) => setCart(c => c.filter(i => i.id !== id)); const toggleFav = (id) => setWish(w => { const n = new Set(w); n.has(id) ? n.delete(id) : n.add(id); return n; }); // Navigation const goTab = (tab) => setView({ name: "tab", tab }); const openProduct = (p) => { setHistory(h => [...h, view]); setView({ name: "product", id: p.id }); }; const goBack = () => { setHistory(h => { if (h.length === 0) { setView({ name: "tab", tab: "home" }); return []; } const prev = h[h.length - 1]; setView(prev); return h.slice(0, -1); }); }; const openCheckout = () => { setCartOpen(false); setHistory(h => [...h, view]); setView({ name: "checkout" }); }; const placeOrder = ({ form, total }) => { const id = "AR-2026-" + (1000 + Math.floor(Math.random() * 8999)); setOrderId(id); setCart([]); setHistory([]); setView({ name: "confirmation" }); }; // Touch ripple useEffect(() => { if (!tweaks.rippleEnabled) return; const handler = (e) => { const target = e.target.closest("button, .product-card, .cat-chip, .filter-pill, .option-card, .cart-item, .admin-row"); if (!target) return; const rect = target.getBoundingClientRect(); const r = document.createElement("span"); r.style.cssText = `position:absolute;left:${e.clientX - rect.left}px;top:${e.clientY - rect.top}px;width:6px;height:6px;border-radius:50%;background:currentColor;opacity:0.18;pointer-events:none;transform:translate(-50%,-50%) scale(0);animation:ripple 0.6s ease-out;`; const cs = getComputedStyle(target); if (cs.position === "static") target.style.position = "relative"; if (cs.overflow !== "hidden") target.style.overflow = "hidden"; target.appendChild(r); setTimeout(() => r.remove(), 600); }; document.addEventListener("click", handler); return () => document.removeEventListener("click", handler); }, [tweaks.rippleEnabled]); // Render current page const currentProduct = view.name === "product" ? products.find(p => p.id === view.id) : null; const isHome = view.name === "tab" && view.tab === "home"; return (
{tweaks.seasonalAtmosphereEnabled && } {view.name === "product" && currentProduct && ( )} {view.name === "checkout" && ( )} {view.name === "confirmation" && ( goTab("home")} /> )} {view.name === "tab" && ( <> goTab("shop")} onOpenCart={() => setCartOpen(true)} onOpenWish={() => goTab("wish")} />
{view.tab === "home" && ( goTab("shop")} points={points} /> )} {view.tab === "shop" && ( )} {view.tab === "wish" && ( )} {view.tab === "account" && ( setAdminOpen(true)} currency={currency} setCurrency={setCurrency} /> )}
)} {cartOpen && ( setCartOpen(false)} onQty={updateQty} onRemove={removeFromCart} onCheckout={openCheckout} coupon={coupon} setCoupon={setCoupon} /> )} {adminOpen && (
setAdminOpen(false)} />
)} setToast("")} /> {/* Fly-to-cart dots */} {flyDots.map(d => (
))} {/* Tweaks panel */} setTweak("heroVariant", v)} /> setTweak("seasonalAtmosphereEnabled", v)} /> setTweak("rippleEnabled", v)} /> setTweak("accentTone", v)} /> { setCurrency(v); setTweak("defaultCurrency", v); }} /> setAdminOpen(true)} /> setCart([])} /> setProducts(data.products)} />
); } ReactDOM.createRoot(document.getElementById("app-root")).render( );