feat(ecommerce): tiles promocionales intercalados en el grid del catalogo
ADR 0005 seccion 3 (patron Walmart), ultimo item de la sub-fase de componentes visuales: "grid de catalogo con tiles promocionales intercalados, no solo tarjetas de producto homogeneas". `category-tiles.tsx` ya era ese mismo primitivo visual (usado en el home) -- se extrajo el tile individual a un componente exportado `PromoTile` (mismo look, sin cambio visual en el home) para reutilizarlo en `catalog-client.tsx`: un tile cada PROMO_TILE_INTERVAL=8 productos, ciclando por las categorias raiz (excluye la raiz que el usuario ya esta viendo via el breadcrumb de #40). Si no hay jerarquia de categorias disponible, la grilla queda igual que antes (solo productos) -- degradacion sin romper nada. Depende de #40 (breadcrumb + categoryNodes en CatalogClient) -- mismo archivo, se apila sobre esa rama en vez de main.
This commit is contained in:
1 parent
bc566b1fc0
commit
7a67ba0743
2 files changed
+75
-30
No files matched your search
@@ -3,11 +3,19 @@
|
||||
import Link from "next/link";
|
||||
import { ChevronRight, SlidersHorizontal, X } from "lucide-react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { PromoTile } from "@/components/home/category-tiles";
|
||||
import { ProductCard } from "@/components/product/product-card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import type { Category, Product } from "@/lib/headless/types";
|
||||
|
||||
// Cada cuantas tarjetas de producto se intercala un tile promocional en
|
||||
// la grilla (patron Walmart, ADR 0005 seccion 3): grid con tiles
|
||||
// promocionales intercalados, no solo tarjetas homogeneas.
|
||||
const PROMO_TILE_INTERVAL = 8;
|
||||
|
||||
type GridItem = { type: "product"; product: Product } | { type: "promo"; category: Category };
|
||||
|
||||
type CatalogClientProps = {
|
||||
products: Product[];
|
||||
categories?: Category[];
|
||||
@@ -96,6 +104,11 @@ export function CatalogClient({
|
||||
// trata como el nombre de categoria exacto de siempre.
|
||||
const allowedCategories = categoryGroups[category] ?? [category];
|
||||
const breadcrumb = useMemo(() => (category === "Todos" ? [] : buildBreadcrumb(categoryNodes, category)), [categoryNodes, category]);
|
||||
// No promociona la misma categoria raiz que el usuario ya esta viendo.
|
||||
const promoRoots = useMemo(() => {
|
||||
const excludeRootId = breadcrumb[0]?.id;
|
||||
return categoryNodes.filter((node) => !node.parentId && node.id !== excludeRootId);
|
||||
}, [categoryNodes, breadcrumb]);
|
||||
|
||||
const filtered = useMemo(
|
||||
() =>
|
||||
@@ -119,6 +132,26 @@ export function CatalogClient({
|
||||
[age, allowedCategories, brand, category, collection, maxPrice, products, query, sort],
|
||||
);
|
||||
|
||||
// Intercala un tile promocional cada PROMO_TILE_INTERVAL productos,
|
||||
// ciclando por las categorias raiz -- si no hay jerarquia de
|
||||
// categorias disponible, la grilla queda igual que antes (solo
|
||||
// productos).
|
||||
const gridItems = useMemo<GridItem[]>(() => {
|
||||
if (promoRoots.length === 0) return filtered.map((product) => ({ type: "product", product }));
|
||||
|
||||
const items: GridItem[] = [];
|
||||
let promoIndex = 0;
|
||||
filtered.forEach((product, index) => {
|
||||
items.push({ type: "product", product });
|
||||
const isLast = index === filtered.length - 1;
|
||||
if (!isLast && (index + 1) % PROMO_TILE_INTERVAL === 0) {
|
||||
items.push({ type: "promo", category: promoRoots[promoIndex % promoRoots.length] });
|
||||
promoIndex += 1;
|
||||
}
|
||||
});
|
||||
return items;
|
||||
}, [filtered, promoRoots]);
|
||||
|
||||
// Conteos por valor de filtro (patron Amazon: "Marca (12)"), calculados
|
||||
// sobre los productos que ya pasan el resto de filtros activos -- cada
|
||||
// dimension se cuenta excluyendose a si misma para reflejar cuantos
|
||||
@@ -284,9 +317,13 @@ export function CatalogClient({
|
||||
|
||||
{filtered.length ? (
|
||||
<div className="grid gap-5 sm:grid-cols-2 xl:grid-cols-3">
|
||||
{filtered.map((product) => (
|
||||
<ProductCard key={product.id} product={product} />
|
||||
))}
|
||||
{gridItems.map((item, index) =>
|
||||
item.type === "product" ? (
|
||||
<ProductCard key={item.product.id} product={item.product} />
|
||||
) : (
|
||||
<PromoTile key={`promo-${index}`} category={item.category} className="min-h-[22rem]" />
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid min-h-80 place-items-center rounded-3xl border border-dashed border-slate-300 text-center">
|
||||
|
||||
Reference in new issue
Block a user