Files
apps-registry/workloads/ecommerce/app/catalog/page.tsx
T
devopsandClaude Opus 5.5 472c12ec0a feat(storefront): franquicias, sagas y personajes (Disney > Toy Story > Rex)
Taxonomía desde tags de Medusa con prefijo (franquicia:/saga:/personaje:),
muchos-a-muchos (ej. "Minnie y Mickey" tiene dos personajes). Pixar va
dentro de Disney.
- lib/taxonomy.ts: helpers + árbol franquicia > saga > personaje.
- Navbar: "Personajes" pasa a mega panel por franquicia (con totales,
  sagas y personajes); el menú lateral mobile muestra franquicias y sagas.
  Fallback a colecciones si el provider no trae tags.
- Catálogo: filtros "Franquicia" y "Personaje" (acotado a la franquicia),
  chip removible de "Saga", ?franquicia/?saga/?personaje en la URL, título
  según la taxonomía de la URL, búsqueda normalizada ("spiderman" encuentra
  "Spider-Man"). Conteos client-side (no son facetas de Meilisearch).
- allowedCategories memoizado (elimina warnings exhaustive-deps).

Revisado por frontend-critic (contraste de personajes /80, H1 solo desde la
URL, Escape devuelve el foco al trigger) + Playwright: check-taxonomy
17/17 + extras, shot-navbar sin regresiones.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018r19s4H8MBGegqToqw5guP
2026-09-27 07:06:32 +00:00

58 lines
2.0 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>>;
};
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 category = single(params.category) || "Todos";
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}
/>
);
}