Preparación para renombrar las categorías reales con tildes/eñes
("Figuras de acción", "Muñecas y peluches", etc.):
- Importador CSV (admin): categorías y colecciones se matchean ignorando
tildes/eñes, para que un CSV escrito sin tildes reuse la categoría
existente en vez de crear un duplicado. Títulos de producto sin cambios.
- Catálogo: ?category= se resuelve contra los nombres reales ignorando
tildes/mayúsculas, así los links viejos siguen funcionando.
- Plantilla CSV de ejemplo con "Figuras de acción".
Aprobado por devsecops-auditor (sin colisiones con los nombres actuales,
sin riesgo de inyección/XSS). CI guard local OK, tsc backend OK, build de
producción del storefront OK.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018r19s4H8MBGegqToqw5guP
67 lines
2.5 KiB
TypeScript
67 lines
2.5 KiB
TypeScript
import type { Metadata } from "next";
|
|
import { CatalogClient } from "@/components/catalog/catalog-client";
|
|
import { getCategories, getProducts } from "@/lib/headless/client";
|
|
|
|
export const metadata: Metadata = {
|
|
title: "Catálogo",
|
|
description: "Explora todos los productos de Ari Shopping.",
|
|
};
|
|
|
|
type CatalogPageProps = {
|
|
searchParams: Promise<Record<string, string | string[] | undefined>>;
|
|
};
|
|
|
|
// Sin tildes/eñes ni mayusculas: los links viejos (o escritos a mano)
|
|
// como ?category=Munecas y peluches siguen llevando a la categoria real
|
|
// "Muñecas y peluches" (renombrada con tildes el 2026-09-27).
|
|
function normalizeName(value: string): string {
|
|
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").trim().toLowerCase();
|
|
}
|
|
|
|
function single(value: string | string[] | undefined): string {
|
|
return Array.isArray(value) ? value[0] ?? "" : value ?? "";
|
|
}
|
|
|
|
export default async function CatalogPage({ searchParams }: CatalogPageProps) {
|
|
const params = await searchParams;
|
|
const [products, categories] = await Promise.all([getProducts(), getCategories()]);
|
|
|
|
const requestedCategory = single(params.category) || "Todos";
|
|
const category =
|
|
categories.find((node) => normalizeName(node.name) === normalizeName(requestedCategory))?.name ?? requestedCategory;
|
|
const collection = single(params.collection);
|
|
const query = single(params.q);
|
|
const taxonomy = {
|
|
franquicia: single(params.franquicia),
|
|
saga: single(params.saga),
|
|
personaje: single(params.personaje),
|
|
};
|
|
|
|
// Los links de navbar/tiles apuntan a categorias raiz (ej. "Mochilas y
|
|
// morrales"), pero ningun producto tiene esa categoria asignada
|
|
// directamente -- solo sus hijos ("Marvel", "Frozen", etc.). Este mapa
|
|
// expande cada raiz-con-hijos al nombre real de sus hijos para que el
|
|
// filtro siga funcionando por igualdad exacta de `product.category`.
|
|
const categoryGroups: Record<string, string[]> = {};
|
|
for (const node of categories) {
|
|
if (node.parentId) continue;
|
|
const children = categories.filter((child) => child.parentId === node.id);
|
|
if (children.length > 0) {
|
|
categoryGroups[node.name] = children.map((child) => child.name);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<CatalogClient
|
|
key={`${category}:${collection}:${query}:${taxonomy.franquicia}:${taxonomy.saga}:${taxonomy.personaje}`}
|
|
products={products}
|
|
categories={categories}
|
|
categoryGroups={categoryGroups}
|
|
initialCategory={category}
|
|
initialCollection={collection}
|
|
initialQuery={query}
|
|
initialTaxonomy={taxonomy}
|
|
/>
|
|
);
|
|
}
|