tsconfig tiene noUncheckedIndexedAccess -- promoRoots[promoIndex %
promoRoots.length] tipa como Category | undefined aunque en runtime
siempre es valido (promoRoots.length > 0 ya se verifico antes de este
punto). Extraido a una constante con guard explicito para que TS
angoste el tipo. Sin cambio de comportamiento.
Detectado por el build real en main tras el merge de #41 -- no se
puede correr tsc localmente en esta maquina (ver notas de sesiones
anteriores), asi que esto solo lo valida el CI post-merge.
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.
ADR 0005 seccion 3 (patron Amazon), primer item de "Filtros facetados
en sidebar con breadcrumb de categoria":
- Breadcrumb (Inicio > raiz > hija) armado caminando parentId sobre la
jerarquia real de categorias (misma que ya consume el mega-menu de
navbar.tsx), no un dato nuevo -- CatalogClient ahora recibe el
arreglo `categories: Category[]` completo ademas del
`categoryGroups` ya existente
- Conteo por valor en cada filtro de la sidebar (Categoria/Marca/Edad),
ej. "Marca (12)": se calcula client-side sobre los productos que ya
pasan el resto de filtros activos, cada dimension excluyendose a si
misma (patron faceted-count estandar). Sin Meilisearch por ahora,
como preveia el ADR ("server-side simple por ahora")
Filtrado existente refactorizado a funciones matchesX() puras y
reutilizadas tanto por `filtered` como por los conteos, sin cambiar el
comportamiento de filtrado ya validado.
Expone ProductVariant.inventoryQuantity (ya calculado desde
variant.inventory_quantity en Medusa, hoy colapsado al booleano
available) y lo usa para mostrar "Quedan N" cuando el stock real es
bajo (<=5), tanto en la tarjeta de producto como en la ficha. null
indica inventario no gestionado (backorder / manage_inventory=false),
donde nunca se muestra un numero. ADR 0005 seccion 3 (patron eBay).
Catalogo mock actualizado (data/products.json) con
inventoryQuantity=null en todas las variantes para seguir tipando
contra Product[] via `satisfies`.
Reutiliza Product.variants[].{name,value,available}, ya expuesto por el
provider de Medusa (variant.options[0]), para swatches clicables en la
tarjeta del catalogo/home -- no solo en la ficha de producto. Solo se
muestra cuando el producto tiene mas de una variante; la variante
seleccionada se usa al agregar al carrito. ADR 0005 seccion 3 (patron
eBay), cero cambio de modelo de datos.
Reutiliza Product.compareAtPrice (ya calculado en el provider de Medusa
via original_amount > calculated_amount) para mostrar precio tachado +
badge de descuento en la grilla/catalogo, no solo en la ficha de
producto. ADR 0005 seccion 3, patron Walmart -- cero cambio de modelo
de datos.
Reemplaza la fila horizontal de pildoras por categoria (ADR 0005 seccion
3, patron Walmart) por un trigger unico "Departamentos" que abre un
panel multi-columna con las 6 categorias raiz y sus hijos, activado por
hover (mouse) o tap (touch), con cierre por click-fuera y Escape.
Bug preexistente (no introducido en esta sesion, solo expuesto): "/" se
prerenderizaba en build time dentro del Dockerfile, donde
HEADLESS_PROVIDER/MEDUSA_* no existen todavia (solo se inyectan en runtime
via el Deployment de k8s) -- el provider caia a "mock" y ese HTML quedaba
cacheado 1 año (confirmado en vivo: x-nextjs-prerender:1,
Cache-Control: s-maxage=31536000, x-nextjs-cache:HIT). El carrusel de
"Novedades" del home ya mostraba productos mock desde antes; recien se
noto con el nav data-driven de la sub-fase 2 porque las categorias mock
("Figuras"/"Peluches"/"Mochilas") son obviamente distintas a las reales.
/catalog y /product/[slug] no sufren esto porque ya son dinamicos
(searchParams / params fuerzan SSR).
export const dynamic = "force-dynamic" en app/page.tsx y app/layout.tsx
(el layout comparte el fetch del Navbar con TODAS las rutas -- forzarlo
ahi tambien protege contra que una pagina futura reintroduzca el mismo
bug). Decision del usuario: priorizar datos reales sobre el cacheo
estatico de 1 año.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013dunTcReWMKhWgCPkvAVHD
next build fallaba en tsc: defaultTile no tenia la propiedad opcional
image, asi que la union de tipos entre tileContent[handle] y defaultTile
perdia esa propiedad y content.image no compilaba. Se extrae el tipo
TileContent y se anota defaultTile con el, igual que tileContent.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013dunTcReWMKhWgCPkvAVHD
El guard de "Validar navegación real del catálogo" en build.yaml seguia
pinneado a la implementacion vieja de navbar.tsx: greppeaba literalmente
"collection=Marvel", "category=Mochilas" y "currentCollection", que eran
exactamente los valores hardcodeados que PR #32 elimino por estar rotos
(ya no correspondian a ningun dato real desde que se quito
legacyProductMeta en PR #29). El guard quedo validando el bug, no la
correccion, y bloqueo el build post-merge de PR #32.
Ahora valida el comportamiento real nuevo: que el nav arma su arbol
desde datos reales (buildCategoryTree), que linkea por nombre real de
categoria (encodeURIComponent(root.name)) y que expone el item activo
(aria-current={isActive(...)}). El check de remontaje en
app/catalog/page.tsx tambien se actualizo: la key ya no incluye
":${section}" porque ese query param nunca se usaba (dead code
eliminado en PR #32).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013dunTcReWMKhWgCPkvAVHD
Sub-fase 2 de Etapa 2 (ADR 0005 seccion 6). Navbar y CategoryTiles usaban
arrays hardcodeados (Figuras/Peluches/Mochilas/Marvel) que ya no coinciden
con los nombres reales de categoria en Medusa desde que se elimino
legacyProductMeta (PR #29) -- todos esos links de navegacion quedaron
rotos silenciosamente (0 productos al filtrar).
- lib/headless: nuevo tipo Category + metodo getCategories() en
CatalogProvider (medusa via /store/product-categories, mock deriva de
su catalogo plano, shopify sin implementar como el resto de sus metodos).
- Navbar: arbol real de categorias (6 raices/21 nodos), dropdown de
hijos por raiz en desktop, lista anidada en el sheet movil. Colecciones
del sheet ahora se derivan de los productos reales en vez de un array
hardcodeado con 2 nombres obsoletos (Marvel/Frozen vs. los reales
Universo Marvel/Reino de Frozen).
- CategoryTiles: tiles de home por categoria raiz real; imagen solo donde
ya existe asset, gradiente honesto donde no (no hay fotografia para las
6 raices todavia, deuda explicita del ADR).
- catalog-client/catalog page: cuando el query param `category` es una
raiz con hijos, se expande a los nombres reales de sus hijos para que
el filtro (igualdad exacta de producto.category) siga funcionando --
sin esto, tiles/nav apuntando a una raiz mostrarian 0 productos porque
ningun producto tiene la raiz asignada directamente.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013dunTcReWMKhWgCPkvAVHD
- Trivy IaC escaneaba solo el patch aislado de Kustomize
(workloads/ecommerce/frontend/patches/deployment.yaml), no lo que
realmente corre en el clúster. Eso generaba falsos positivos en todo
lo heredado del base remoto de platform-templates (runAsNonRoot,
seccompProfile) porque el patch nunca los repite. Se instala kubectl,
se renderiza el kustomization completo y se escanea ese output.
- readOnlyRootFilesystem pasa de false a true en el contenedor del
frontend: revisando next.config.ts (optimización de imágenes) y
app/api/revalidate/route.ts (revalidateTag sin cache handler custom),
la imagen sí escribe a ./.next/cache en runtime. Se agregan volúmenes
emptyDir para .next/cache y /tmp.
El default de Trivy (5m) dejó de alcanzar para escanear la imagen del
frontend, igual que ya le había pasado a commerce-backend el
2026-08-16 ("context deadline exceeded" ~4m45s, justo tras terminar
el docker build). Se aplica el mismo fix ya validado en
build-medusa.yaml: --timeout 15m0s en ambos scans de imagen (CRITICAL
y HIGH), y se sube timeout-minutes del job de 20 a 30 para que quepan.
Los 17 productos legacy ya tienen category, collection, brand, ageRange,
imagenes y featured/isNew reales en Medusa (migrados en fix #2/#2b de
Etapa 2 sub-fase 1), por lo que el fallback hardcodeado en mapProduct()
ya no aporta nada y solo duplica datos que pueden desincronizarse.
+categories sin .* no expande la relacion (Medusa la omite en
silencio del response); +categories.* si trae los sub-campos.
Confirmado en vivo via kubectl exec antes de este commit: demo
con categoria real asignada ahora la trae, legacy sigue en []
(pendiente fix#2, fuera de alcance de este cambio).
/store/products descarta en silencio la relacion +categories aunque
/admin/products si la expone (bug real, no teorico, confirmado en la
auditoria de Fase 5 con las 21 categorias y 9 productos [DEMO]
creados en Etapa 1 -- todos caian a "Variedades" pese a tener
categoria real asignada). Middleware global sobre req.allowed, patron
oficial de Medusa v2 para ampliar campos de una ruta core restringida.
Etapa 2, sub-fase 1 (categorias/colecciones reales) de
docs/decisiones/0005-rediseno-storefront.md.
Fase 5 Etapa 1: auditoria del catalogo actual (categorias/colecciones
de Medusa sin uso real, nav hardcodeado, bug de +categories en Store
API), decision de evolucionar el stack en vez de reescribirlo,
patrones UX de Amazon/eBay/Walmart y taxonomia de 21 categorias ya
creada en Medusa junto con 9 productos [DEMO] validados en vivo en
shop.cruzcloud.net como linea base pre-rediseno.
Con el runner corriendo 1 job a la vez (capacity: 1), cuando 2+
pipelines comparten el mismo commit disparador, el primero en terminar
deja su propio commit de GitOps en main -- los siguientes veian 'main
ya no es igual a mi commit' y se saltaban su propia promocion, aunque
era seguro promocionar. Visto en vivo: Frontend construyo, escaneo y
firmo v1.0.119 pero el manifiesto seguia en v1.0.104 (Promocion GitOps:
false). Fix en los 3 workflows: git merge-base --is-ancestor en vez de
comparacion exacta de SHA. Bump manual de frontend a v1.0.119 (la
version que ya estaba lista, sin desplegar). Documentado el limite
conocido de este fix en el playbook de la migracion.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011FqYP3Wf1W63Qmh7qgXAAT