diff --git a/workloads/ecommerce/integration/app-catalog-page.example.tsx b/workloads/ecommerce/integration/app-catalog-page.example.tsx new file mode 100644 index 0000000..8e5503c --- /dev/null +++ b/workloads/ecommerce/integration/app-catalog-page.example.tsx @@ -0,0 +1,39 @@ +import { Suspense } from "react"; +import CatalogRouteBoundary from "@/components/catalog/CatalogRouteBoundary"; + +// Conserva aquí los imports actuales de tu página: +// import CatalogClient from "..."; +// import { getProducts } from "..."; + +type CatalogPageProps = { + searchParams: Promise>; +}; + +export default async function CatalogPage({ + searchParams, +}: CatalogPageProps) { + const params = await searchParams; + + // Conserva aquí la carga de datos actual de tu proyecto. + // const products = await getProducts(); + + return ( + + + {/* + Sustituye este comentario por el contenido actual del catálogo. + + Ejemplo: + + */} +
+          {JSON.stringify(params)}
+        
+
+
+ ); +} diff --git a/workloads/ecommerce/src/components/catalog/CatalogRouteBoundary.tsx b/workloads/ecommerce/src/components/catalog/CatalogRouteBoundary.tsx new file mode 100644 index 0000000..0beef24 --- /dev/null +++ b/workloads/ecommerce/src/components/catalog/CatalogRouteBoundary.tsx @@ -0,0 +1,29 @@ +"use client"; + +import type { ReactNode } from "react"; +import { usePathname, useSearchParams } from "next/navigation"; + +type CatalogRouteBoundaryProps = { + children: ReactNode; +}; + +/** + * Fuerza el remontaje del árbol del catálogo cuando cambia: + * - pathname + * - category + * - collection + * - section + * - búsqueda, orden u otros parámetros + * + * Esto evita que useState/useMemo conserven filtros de una navegación anterior. + */ +export default function CatalogRouteBoundary({ + children, +}: CatalogRouteBoundaryProps) { + const pathname = usePathname(); + const searchParams = useSearchParams(); + + const navigationKey = `${pathname}?${searchParams.toString()}`; + + return
{children}
; +} diff --git a/workloads/ecommerce/src/components/navigation/CategoryMenu.module.css b/workloads/ecommerce/src/components/navigation/CategoryMenu.module.css new file mode 100644 index 0000000..18159ca --- /dev/null +++ b/workloads/ecommerce/src/components/navigation/CategoryMenu.module.css @@ -0,0 +1,89 @@ +.navigation { + align-items: center; + background: #020617; + border-bottom: 3px solid #cbd5e1; + box-sizing: border-box; + display: flex; + gap: 30px; + min-height: 72px; + overflow-x: auto; + padding: 0 32px; + scrollbar-width: none; + width: 100%; +} + +.navigation::-webkit-scrollbar { + display: none; +} + +.menuButton { + align-items: center; + display: inline-flex; + flex: 0 0 auto; + flex-direction: column; + gap: 5px; + justify-content: center; + min-height: 44px; + min-width: 44px; + text-decoration: none; +} + +.menuButton span { + background: #f8fafc; + border-radius: 999px; + display: block; + height: 3px; + width: 22px; +} + +.links { + align-items: stretch; + display: flex; + gap: clamp(28px, 4vw, 62px); + min-width: max-content; +} + +.link { + align-items: center; + border-bottom: 3px solid transparent; + color: #f8fafc; + display: inline-flex; + font-size: 16px; + font-weight: 800; + min-height: 69px; + padding: 0 2px; + text-decoration: none; + transition: + border-color 160ms ease, + color 160ms ease, + opacity 160ms ease; + white-space: nowrap; +} + +.link:hover, +.link:focus-visible { + color: #ffffff; + opacity: 0.82; + outline: none; +} + +.active { + border-bottom-color: #ffffff; +} + +@media (max-width: 900px) { + .navigation { + gap: 18px; + min-height: 64px; + padding: 0 18px; + } + + .links { + gap: 28px; + } + + .link { + font-size: 14px; + min-height: 61px; + } +} diff --git a/workloads/ecommerce/src/components/navigation/CategoryMenu.tsx b/workloads/ecommerce/src/components/navigation/CategoryMenu.tsx new file mode 100644 index 0000000..ac81c74 --- /dev/null +++ b/workloads/ecommerce/src/components/navigation/CategoryMenu.tsx @@ -0,0 +1,87 @@ +"use client"; + +import { usePathname, useSearchParams } from "next/navigation"; +import styles from "./CategoryMenu.module.css"; + +type MenuItem = { + label: string; + href: string; +}; + +/** + * Menú principal ARI Shopping. + * + * Hotfix de estabilidad: + * Se utilizan enlaces HTML nativos para que cada cambio de categoría + * realice una navegación completa. Esto evita conservar estados obsoletos + * del catálogo cuando únicamente cambia el query string. + */ +const MENU_ITEMS: readonly MenuItem[] = [ + { + label: "Juguetes para bebés", + href: "/catalog?category=Figuras", + }, + { + label: "Educativos", + href: "/catalog?category=Figuras§ion=educativos", + }, + { + label: "Muñecas y peluches", + href: "/catalog?category=Peluches", + }, + { + label: "Acción y aventura", + href: "/catalog?collection=Marvel", + }, + { + label: "Mochilas", + href: "/catalog?category=Mochilas", + }, + { + label: "Variedades", + href: "/catalog", + }, +]; + +function normalizeUrl(pathname: string, query: string): string { + return query ? `${pathname}?${query}` : pathname; +} + +export default function CategoryMenu() { + const pathname = usePathname(); + const searchParams = useSearchParams(); + const currentUrl = normalizeUrl(pathname, searchParams.toString()); + + return ( + + ); +}