/* Header + Banner + Catálogo (toolbar + grilla de productos, datos reales) */ const { Ic } = window; const D = window.DianeryData; function scrollTop() { window.scrollTo({ top: 0, behavior: "smooth" }); } function scrollToCatalog() { const el = document.getElementById("catalogo"); if (el) el.scrollIntoView({ behavior: "smooth" }); else scrollTop(); } /* Comportamiento de navegación: sin enlaces muertos */ function navAction(e, item) { e.preventDefault(); if (item.label === "Inicio") scrollTop(); else if (item.label === "Productos") scrollToCatalog(); else window.shopToast && window.shopToast("Próximamente"); } function Header({ brand, query, onQuery, cartCount, onCartClick }) { const [menuOpen, setMenuOpen] = React.useState(false); const onNav = (e, item) => { navAction(e, item); setMenuOpen(false); }; return (
{ e.preventDefault(); scrollTop(); }}> {brand.name} {brand.tagline}
onQuery(e.target.value)} placeholder={brand.searchPlaceholder} aria-label="Buscar productos" />
{menuOpen && ( )}
); } function Banner({ catalog }) { const img = catalog.bannerImage; return (
{img ?
:
banner · foto lifestyle de productos
}
{catalog.bannerKicker}

{catalog.bannerTitle}

); } /* Dropdown reutilizable para los filtros / orden de la toolbar */ function Dropdown({ label, value, options, onChange, disabled }) { const [open, setOpen] = React.useState(false); const ref = React.useRef(null); React.useEffect(() => { if (!open) return; const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }; document.addEventListener("mousedown", onDoc); return () => document.removeEventListener("mousedown", onDoc); }, [open]); const current = options.find(o => o.value === value); return (
{open && (
{options.map(o => ( ))}
)}
); } function ProductImage({ images, tag, className }) { const main = (images || [])[0]; if (main) { return (
{tag && {tag}}
); } return (
{tag && {tag}} sin imagen
); } function ProductCard({ p, onOpen, onAdd }) { const out = p.stock <= 0; return (
onOpen(p)} role="button" tabIndex={0} onKeyDown={e => { if (e.key === "Enter") onOpen(p); }}> {out && Agotado}

{p.name}

{p.desc}

{D.formatCOP(p.price)}
); } const PRICE_RANGES = [ { value: "all", label: "Todos los precios", test: () => true }, { value: "lt30", label: "Hasta $30.000", test: p => p.price <= 30000 }, { value: "30-60", label: "$30.000 – $60.000", test: p => p.price > 30000 && p.price <= 60000 }, { value: "gt60", label: "Más de $60.000", test: p => p.price > 60000 } ]; const SORTS = { recent: { label: "Más recientes", cmp: (a, b) => numId(b.id) - numId(a.id) }, priceAsc: { label: "Precio: menor a mayor", cmp: (a, b) => a.price - b.price }, priceDesc: { label: "Precio: mayor a menor", cmp: (a, b) => b.price - a.price }, nameAsc: { label: "Nombre: A-Z", cmp: (a, b) => a.name.localeCompare(b.name, "es") }, nameDesc: { label: "Nombre: Z-A", cmp: (a, b) => b.name.localeCompare(a.name, "es") } }; function numId(id) { return Number(String(id).replace(/\D/g, "")) || 0; } function Toolbar({ category, setCategory, priceRange, setPriceRange, sort, setSort, count, categories }) { return (
({ value: c, label: c }))]} onChange={setCategory} /> {}} /> ({ value: r.value, label: r.label }))} onChange={setPriceRange} /> {count} {count === 1 ? "producto" : "productos"} ({ value: k, label: SORTS[k].label }))} onChange={setSort} />
); } function Catalog({ query, onOpenDetail, onAddToCart }) { const [category, setCategory] = React.useState("all"); const [priceRange, setPriceRange] = React.useState("all"); const [sort, setSort] = React.useState("recent"); const all = D.getProducts().filter(p => p.active); const categories = D.getCategories(true); const range = PRICE_RANGES.find(r => r.value === priceRange) || PRICE_RANGES[0]; const needle = query.trim().toLowerCase(); let products = all.filter(p => { const okCat = category === "all" || p.tag === category; const okPrice = range.test(p); const okQ = !needle || p.name.toLowerCase().includes(needle) || (p.tag || "").toLowerCase().includes(needle) || (p.sku || "").toLowerCase().includes(needle) || (p.desc || "").toLowerCase().includes(needle); return okCat && okPrice && okQ; }); products = products.slice().sort(SORTS[sort].cmp); return (
{products.length === 0 ? (

No encontramos productos con esa búsqueda.

) : (
{products.map(p => )}
)}
); } /* Detalle de producto con galería simple */ function ProductDetail({ product, onClose, onAddToCart }) { const [active, setActive] = React.useState(0); React.useEffect(() => { setActive(0); }, [product && product.id]); React.useEffect(() => { const onKey = (e) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", onKey); return () => document.removeEventListener("keydown", onKey); }, []); if (!product) return null; const images = product.images || []; const out = product.stock <= 0; const main = images[active]; return (
e.stopPropagation()}>
{main ?
:
sin imagen
} {images.length > 1 && (
{images.map((src, i) => (
)}
{product.tag}

{product.name}

SKU: {product.sku}
{D.formatCOP(product.price)}

{product.desc}

{out ? Producto sin stock disponible. : {product.stock} unidades disponibles}
Consultar por WhatsApp
); } Object.assign(window, { Header, Banner, Catalog, ProductDetail });