/* global React, CATALOGO, CAT_LABELS, CAT_TOTALS, PATRONES, detectarPatron, ICONOS_INFO, Placeholder, Eyebrow, Arr, HeartIcon, Reveal, useWishlist, useUI, waLink */ const { useState, useMemo, useEffect } = React; /* ─── Portada del catálogo — polaroids por categoría ─── */ function CatalogPortada({ onSelect }) { const isMob = typeof window !== "undefined" && window.innerWidth < 760; const CATS = [ { id: "mecedoras", bg: "#ede4d4" }, { id: "sillas", bg: "#dde8e0" }, { id: "comedores", bg: "#dde4ee" }, { id: "combos", bg: "#f0e4d4" }, { id: "mesas", bg: "#e8e4da" }, { id: "sofas", bg: "#ead8e4" }, { id: "percheros", bg: "#d4e8d4" }, { id: "sillas-bar", bg: "#f0ddd8" }, ].filter(c => (CATALOGO||[]).some(p => p.categoria === c.id)); const CAT_HERO_LOCAL_P = { sofas: "guajira-lineal", "sillas-bar": "diamante-precolombino-3" }; const getPhoto = (catId) => { const ovr = CAT_HERO_LOCAL_P[catId] || (window.CAT_HERO_OVERRIDES||{})[catId]; if (ovr) { const p = (CATALOGO||[]).find(x => (x.id === ovr || x.slug === ovr) && x.foto); if (p) return p.foto; } return (CATALOGO||[]).find(p => p.categoria === catId && p.foto)?.foto || null; }; const minPrecio = (catId) => { const ps = (CATALOGO||[]).filter(p => p.categoria === catId && p.precio).map(p => p.precio); return ps.length ? Math.min(...ps).toLocaleString("es-CO") : null; }; return (
{/* Header */}
El catálogo · DAVA'S · 2026

¿Qué espacio vas
a transformar?

Elige tu categoría y encuentra la pieza que va a vivir contigo.

{/* Grid de polaroids */}
{CATS.map((c) => { const photo = getPhoto(c.id); const label = (CAT_LABELS?.combos && c.id === 'combos' ? 'Conjuntos' : null) || CAT_LABELS[c.id] || c.id; const count = (CATALOGO||[]).filter(p => p.categoria === c.id).length; const desde = minPrecio(c.id); const pad = isMob ? 9 : 13; const bot = isMob ? 36 : 48; return (
onSelect(c.id)} style={{ boxShadow:"0 20px 56px rgba(0,0,0,.5)" }}>
{photo ? {label} :
}
{label}
{count} piezas{desde ? ` · desde $${desde}` : ""}
); })}
{/* Ver todo */}
); } /* ─── Tarjeta de producto — estilo polaroid ─── */ // Productos sin foto no se muestran al público function CatProd({ p, onOpen, onModelo }) { if (!p.foto) return null; const wl = useWishlist(); const isOn = wl.has(p.id); return (
onOpen(p)} style={{ background:"#fff", padding:"10px 10px 44px", boxShadow:"0 12px 32px rgba(0,0,0,.13)", cursor:"pointer", position:"relative", transition:"transform .32s cubic-bezier(.2,.7,.2,1), box-shadow .32s", borderRadius:2 }}> {/* Foto */}
{p.nombre}
{/* Nombre a mano + precio */}
{p.nombre}
$ {p.precio.toLocaleString("es-CO")}
); } function Catalog({ initialFilter, setPage, openModelo, openFicha }) { const initial = { cat: "all", modelo: "all", patron: "all", q: "", sort: "destacado", ...(initialFilter || {}) }; const [f, setF] = useState(initial); const [drawerOpen, setDrawerOpen] = useState(false); // "portada" = grid de polaroids | "list" = catálogo filtrado const startMode = (initialFilter?.cat && initialFilter.cat !== "all") ? "list" : "portada"; const [viewMode, setViewMode] = useState(startMode); const set = (k, v) => { setF((x) => ({ ...x, [k]: v })); setViewMode("list"); }; const clearAll = () => { setF({ cat: "all", modelo: "all", patron: "all", q: "", sort: "destacado" }); setViewMode("portada"); }; // ── Todos los hooks SIEMPRE (reglas de React) ── // Bloqueo scroll cuando drawer abierto useEffect(() => { document.body.style.overflow = drawerOpen ? "hidden" : ""; return () => { document.body.style.overflow = ""; }; }, [drawerOpen]); useEffect(() => { window.scrollTo({ top: 0, behavior: "instant" }); }, [initialFilter?.cat]); // Reporta al header cuánto mide el banner de categoría (con foto), para que // sepa hasta dónde ir en blanco/transparente y desde dónde pasar a oscuro. // Si no hay categoría activa (f.cat === "all") no hay banner → 0. const bannerRef = React.useRef(null); useEffect(() => { if (f.cat === "all") { document.documentElement.style.setProperty("--dark-zone-h", "0px"); return; } const el = bannerRef.current; if (!el) { document.documentElement.style.setProperty("--dark-zone-h", "0px"); return; } const update = () => document.documentElement.style.setProperty("--dark-zone-h", el.offsetHeight + "px"); update(); const ro = new ResizeObserver(update); ro.observe(el); window.addEventListener("resize", update); return () => { ro.disconnect(); window.removeEventListener("resize", update); }; }, [f.cat]); // Sync cuando cambia initialFilter desde afuera (ej. clic en categoría del home) useEffect(() => { if (initialFilter?.cat && initialFilter.cat !== "all") { setF(prev => ({ ...prev, ...(initialFilter||{}) })); setViewMode("list"); } }, [initialFilter?.cat]); // Modelos derivados del catálogo const modelos = useMemo(() => { const counts = {}; CATALOGO.forEach((p) => { counts[p.nombre] = (counts[p.nombre] || 0) + 1; }); return Object.entries(counts).sort((a, b) => b[1] - a[1]).map(([name, n]) => ({ name, n })); }, []); // Filtrado const filtered = useMemo(() => { let out = CATALOGO.slice(); if (f.cat !== "all") out = out.filter((p) => p.categoria === f.cat); if (f.modelo !== "all") out = out.filter((p) => p.nombre === f.modelo); if (f.patron !== "all") out = out.filter((p) => detectarPatron(p.variante) === f.patron); if (f.q) { const q = f.q.toLowerCase(); out = out.filter((p) => p.nombre.toLowerCase().includes(q) || (p.variante || "").toLowerCase().includes(q) || p.id.toLowerCase().includes(q) ); } if (f.sort === "destacado") out.sort((a, b) => (b.destacado - a.destacado)); if (f.sort === "precio-asc") out.sort((a, b) => a.precio - b.precio); if (f.sort === "precio-desc") out.sort((a, b) => b.precio - a.precio); if (f.sort === "nombre") out.sort((a, b) => a.nombre.localeCompare(b.nombre)); return out; }, [f]); const activeChips = []; if (f.cat !== "all") activeChips.push({ key: "cat", label: CAT_LABELS[f.cat], kind: "Categoría" }); if (f.modelo !== "all") activeChips.push({ key: "modelo", label: f.modelo, kind: "Modelo" }); if (f.patron !== "all") activeChips.push({ key: "patron", label: f.patron, kind: "Patrón" }); if (f.q) activeChips.push({ key: "q", label: `"${f.q}"`, kind: "Buscar" }); // ── Portada de categorías (early return DESPUÉS de todos los hooks) ── if (viewMode === "portada") { return ( { if (catId === "__all__") { setViewMode("list"); } else { set("cat", catId); } }} /> ); } // ── Vista de lista/catálogo ── // Banners diseñados por categoría (desktop + mobile) // Banners horizontales — funcionan para desktop y mobile con object-fit:cover const CAT_BANNERS = { sillas: { desk: "/banner_sillas_desk.jpg", mob: "/banner_sillas_desk.jpg" }, mecedoras: { desk: "/banner_mecedoras_desk.jpg", mob: "/banner_mecedoras_desk.jpg" }, combos: { desk: "/banner_combos_desk.jpg", mob: "/banner_combos_desk.jpg" }, comedores: { desk: "/banner_comedores_desk.jpg", mob: "/banner_comedores_desk.jpg" }, mesas: { desk: "/banner_mesas_desk.jpg", mob: "/banner_mesas_desk.jpg" }, sofas: { desk: "/banner_sofas_desk.jpg", mob: "/banner_sofas_desk.jpg" }, percheros: { desk: "/banner_percheros_desk.jpg", mob: "/banner_percheros_desk.jpg" }, "sillas-bar": { desk: "/banner_sillabar_desk.jpg", mob: "/banner_sillabar_desk.jpg" }, }; // Display names override (combos → Conjuntos) const CAT_DISPLAY = { ...CAT_LABELS, combos: "Conjuntos" }; // Local hero overrides por slug (sofás y sillas-bar) const CAT_HERO_LOCAL = { sofas: "guajira-lineal", "sillas-bar": "diamante-precolombino-3" }; const isMob2 = typeof window !== "undefined" && window.innerWidth < 760; const getCatHero = (catId) => { const ovr = CAT_HERO_LOCAL[catId] || (window.CAT_HERO_OVERRIDES||{})[catId]; if (ovr) { const p = CATALOGO.find(x => (x.id === ovr || x.slug === ovr) && x.foto); if (p) return p; } return CATALOGO.find(p => p.categoria === catId && p.foto) || null; }; const CAT_COPIES = { mecedoras: { copy:"Hecha para quedarte.\nHecha para ti.", sub:"Hierro forjado, madera noble y tejido a mano. Una mecedora que es tuya desde el primer movimiento." }, sillas: { copy:"La silla que\ndefine el espacio.", sub:"Cada silla lleva horas de tejido artesanal y una estructura en hierro fundida en nuestro taller." }, comedores: { copy:"Alrededor de esta\nmesa pasan cosas.", sub:"Comedores tejidos a mano para las reuniones que importan. Hierro y madera noble, sin atajos." }, combos: { copy:"El conjunto\nque transforma el espacio.", sub:"Dos sillas, una mesa y un tejido que las une. Diseñados para vivir juntos, tejidos a mano en nuestro taller." }, comedores: { copy:"La mesa que hace\nque nadie quiera irse.", sub:"Comedores tejidos a mano para las reuniones que importan. Hierro y madera noble, sin atajos." }, mesas: { copy:"La base de\ncada historia.", sub:"Hierro forjado en nuestro taller, integrado con madera noble. Sin tornillos escondidos." }, sofas: { copy:"Donde la tarde\nse vuelve larga.", sub:"Sofás tejidos hilo a hilo, construidos para el uso real. No para la foto." }, percheros: { copy:"Lo primero que\nves al llegar a casa.", sub:"Hierro y tejido artesanal en un solo objeto. El detalle que eleva cualquier entrada." }, "sillas-bar": { copy:"Para las noches\nque no tienen prisa.", sub:"Sillas de bar con tejido artesanal y hierro. Para bares, restaurantes y casas con barra." }, }; return (
{/* ── Banner editorial cuando hay categoría activa ── */} {f.cat !== "all" && (() => { const banners = CAT_BANNERS[f.cat]; const hero = getCatHero(f.cat); const { copy, sub } = CAT_COPIES[f.cat] || { copy: CAT_LABELS[f.cat] || f.cat, sub:"" }; const count = CATALOGO.filter(p => p.categoria === f.cat).length; // Si hay banner diseñado, usarlo con overlay de copie if (banners) { const { copy: banCopy } = CAT_COPIES[f.cat] || {}; return (
{CAT_LABELS[f.cat]} {/* Gradiente + overlay texto */}
{CAT_DISPLAY[f.cat] || CAT_LABELS[f.cat]} · {count} piezas
{banCopy && (

{banCopy}

)}
); } // Banner generado por código (fallback) return (
{CAT_DISPLAY[f.cat] || CAT_LABELS[f.cat]} · {count} piezas

{copy}

{sub}

{hero?.foto && {hero.nombre}}
); // end fallback banner })()} {/* ── Tabs de categoría (scroll normal con la página, sin recortarse) ── */} {f.cat !== "all" && (
{Object.entries(CAT_LABELS).map(([k, label]) => ( ))}
)} {/* Hero original solo cuando no hay categoría */} {f.cat === "all" &&
Inicio · Catálogo

{filtered.length}
piezas.

Tejido a mano · entrega en 15 días

Filtra por categoría, modelo o patrón. Toca cualquier pieza para verla en grande y pedirla por WhatsApp.

Filtros activos: {activeChips.length === 0 ? ( — ninguno · mostrando todo el catálogo ) : (<> {activeChips.map((c) => ( ))} )}
}
{filtered.length} resultados Ordenar
{filtered.length === 0 ? (
— Sin resultados con esos filtros.
) : (
{filtered.map((p) => ( ))}
)}
{/* MOBILE: FAB de filtros */} {/* MOBILE: drawer de filtros */}
setDrawerOpen(false)} />
); } window.Catalog = Catalog; window.CatProd = CatProd;