Files
apps-registry/workloads/ecommerce/app/page.tsx
devopsandClaude Sonnet 5 ff59618a2a fix: filtrar producto sin stock del banner y banco placeholder de PSE
Encontrado probando en vivo con navegador real (Docker + Playwright
contra shop.cruzcloud.net):

- app/page.tsx: un producto featured pero sin stock (available: false)
  salía primero en el hero banner con botón "Comprar ahora" hacia una
  página donde en realidad no se puede comprar.
- pse-banks/route.ts: el sandbox real de ePayco (bank.getBanks())
  incluye un primer elemento placeholder (bankCode "0", "A
  continuación seleccione su banco...") mezclado con los bancos reales,
  mostrado como opción seleccionable. Se filtra explícitamente.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01THZFwPzrwv6VAbuwmaf5vR
2026-09-10 17:09:36 +00:00

52 lines
4.7 KiB
TypeScript

import Image from "next/image";
import Link from "next/link";
import { ArrowRight } from "lucide-react";
import { CategoryTiles } from "@/components/home/category-tiles";
import { HeroCarousel } from "@/components/home/hero-carousel";
import { TrustBar } from "@/components/home/trust-bar";
import { ProductCarousel } from "@/components/product/product-carousel";
import { Button } from "@/components/ui/button";
import { getCategories, getProducts } from "@/lib/headless/client";
// Sin esto, Next.js prerenderiza "/" en build time dentro del Dockerfile,
// donde HEADLESS_PROVIDER/MEDUSA_* todavia no existen (solo se inyectan en
// runtime via el Deployment) -- cae al provider mock y ese HTML queda
// cacheado 1 año (visto en vivo: x-nextjs-prerender:1, s-maxage=31536000),
// violando el flujo Admin -> revalidate del ADR 0005 justo en el home.
export const dynamic = "force-dynamic";
export default async function HomePage() {
const [products, categories] = await Promise.all([getProducts(), getCategories()]);
const treasures = products.filter((product) => product.isNew || product.featured).slice(0, 8);
// ADR 0010: el banner toma primero productos destacados (curaduria
// explicita del admin via el flag "featured"); si no hay ninguno,
// cae a los mismos "tesoros" (nuevos/destacados) de la seccion de
// abajo para no repetir la logica de seleccion. Se exige ademas
// "available" -- encontrado probando en vivo con un navegador real
// (Playwright): un producto featured pero sin stock disponible salio
// primero en el carrusel con boton "Comprar ahora" hacia una pagina
// donde en realidad no se puede comprar.
const featuredProducts = products.filter((product) => product.featured && product.available);
const bannerProducts = featuredProducts.length > 0 ? featuredProducts : treasures.filter((product) => product.available);
return <><HeroCarousel products={bannerProducts} /><TrustBar /><div className="content-auto"><CategoryTiles categories={categories} /></div><section className="content-auto mx-auto max-w-7xl px-4 py-14"><div className="mb-8 flex items-end justify-between gap-5"><div><span className="text-xs font-black uppercase tracking-[.18em] text-ari-coral">Novedades</span><h2 className="mt-2 text-4xl font-black tracking-tight sm:text-5xl">Tesoros recién llegados</h2><p className="mt-3 text-slate-500">Desliza y encuentra nuevos favoritos.</p></div><Button asChild variant="outline" className="hidden sm:inline-flex"><Link href="/catalog">Ver catálogo <ArrowRight className="size-4" /></Link></Button></div><ProductCarousel products={treasures} /></section><section className="content-auto mx-auto max-w-7xl px-4 pb-20"><div className="grid overflow-hidden rounded-[2rem] bg-ari-marino text-white shadow-2xl lg:grid-cols-[1fr_.7fr]"><div className="p-8 sm:p-12"><span className="text-xs font-black uppercase tracking-[.18em] text-ari-coral-claro">Club ARI</span><h2 className="mt-3 text-4xl font-black tracking-tight sm:text-6xl">Promociones, lanzamientos y mucha diversión.</h2><p className="mt-5 max-w-xl text-white/80">Únete a nuestra comunidad y recibe primero las novedades del catálogo.</p><Button variant="yellow" size="lg" className="mt-7">Quiero unirme</Button></div>{/* No hay herramienta de generacion de imagenes disponible para crear un
asset nuevo -- se reconstruye el mismo efecto (fondo punteado +
fotos estilo polaroid) con CSS y fotos reales de producto ya
existentes, en azul/amarillo de marca en vez del violeta original
de promo-backpacks.webp. */}
<div className="relative min-h-64 overflow-hidden bg-ari-coral-claro">
<div className="absolute inset-0 [background-image:radial-gradient(rgba(0,85,184,0.35)_1.5px,transparent_1.5px)] [background-size:18px_18px]" />
<div className="absolute -bottom-12 -left-12 size-52 rounded-full bg-ari-marino" />
<div className="absolute inset-0 flex items-center justify-center gap-3 p-6">
<div className="relative size-28 -rotate-6 overflow-hidden rounded-xl border-4 border-white bg-white shadow-xl sm:size-32">
<Image src="/images/products/ari-011-mochila-escolar-de-spider-man-de-disney-.webp" alt="" fill sizes="128px" className="object-cover" />
</div>
<div className="relative size-28 rotate-3 overflow-hidden rounded-xl border-4 border-white bg-white shadow-xl sm:size-32">
<Image src="/images/products/ari-016-disney-mochila-iron-man-de-poliester-ult.webp" alt="" fill sizes="128px" className="object-cover" />
</div>
<div className="relative hidden size-28 -rotate-3 overflow-hidden rounded-xl border-4 border-white bg-white shadow-xl sm:block sm:size-32">
<Image src="/images/products/ari-009-mochilas-disney-coleccion-marvel.webp" alt="" fill sizes="128px" className="object-cover" />
</div>
</div>
</div></div></section></>;
}