/* 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 (
);
}
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.
) : (
)}
);
}
/* 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}
);
}
Object.assign(window, { Header, Banner, Catalog, ProductDetail });