Merge fix/adr0008-navy-coral-subfase3-product-card-grid into main
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (push) Successful in 1m28s
Build and Push Frontend / Construir y Subir Imagen (push) Successful in 16m53s

Merge directo sin PR de Gitea: crear un PR via API requiere leer
credenciales git guardadas, bloqueado por el clasificador de auto-mode
(no es una limitacion corregible del lado de la infra). Flujo aprobado
por el usuario como estandar para cierres de sub-fase de este proyecto
el 2026-08-27 (ver memoria del proyecto). Diff mostrado y aprobado
explicitamente antes de este merge.
This commit is contained in:
devops committed 2026-08-27 14:31:00 -05:00
commit b123a5a9c9
2 files changed
+19 -19

No files matched your search

@@ -323,18 +323,18 @@ export function CatalogClient({
<div className="mb-8"> <div className="mb-8">
{breadcrumb.length > 0 && ( {breadcrumb.length > 0 && (
<nav aria-label="Breadcrumb" className="mb-4 flex flex-wrap items-center gap-1.5 text-xs font-bold text-slate-500"> <nav aria-label="Breadcrumb" className="mb-4 flex flex-wrap items-center gap-1.5 text-xs font-bold text-slate-500">
<Link href="/catalog" prefetch={false} className="hover:text-ari-ciruela"> <Link href="/catalog" prefetch={false} className="hover:text-ari-coral">
Inicio Inicio
</Link> </Link>
{breadcrumb.map((node, index) => ( {breadcrumb.map((node, index) => (
<span key={node.id} className="flex items-center gap-1.5"> <span key={node.id} className="flex items-center gap-1.5">
<ChevronRight className="size-3" /> <ChevronRight className="size-3" />
{index === breadcrumb.length - 1 ? ( {index === breadcrumb.length - 1 ? (
<span aria-current="page" className="text-ari-carbon"> <span aria-current="page" className="text-ari-marino">
{node.name} {node.name}
</span> </span>
) : ( ) : (
<Link href={`/catalog?category=${encodeURIComponent(node.name)}`} prefetch={false} className="hover:text-ari-ciruela"> <Link href={`/catalog?category=${encodeURIComponent(node.name)}`} prefetch={false} className="hover:text-ari-coral">
{node.name} {node.name}
</Link> </Link>
)} )}
@@ -342,23 +342,23 @@ export function CatalogClient({
))} ))}
</nav> </nav>
)} )}
<span className="text-xs font-black uppercase tracking-[.18em] text-ari-ciruela"> <span className="text-xs font-black uppercase tracking-[.18em] text-ari-coral">
Catálogo completo Catálogo completo
</span> </span>
<h1 className="font-display mt-2 text-5xl font-bold tracking-tight text-ari-carbon">Todos los tesoros</h1> <h1 className="font-display mt-2 text-5xl font-bold tracking-tight text-ari-marino">Todos los tesoros</h1>
<p className="mt-3 text-slate-500"> <p className="mt-3 text-slate-500">
Filtra por categoría, marca o edad sin recargar la página. Filtra por categoría, marca o edad sin recargar la página.
</p> </p>
</div> </div>
<div className="grid gap-8 lg:grid-cols-[260px_1fr]"> <div className="grid gap-8 lg:grid-cols-[260px_1fr]">
<aside className="h-fit space-y-6 rounded-lg border border-slate-100 bg-white p-6 shadow-lg shadow-ari-carbon-oscuro/5 lg:sticky lg:top-44"> <aside className="h-fit space-y-6 rounded-lg border border-slate-100 bg-white p-6 shadow-lg shadow-ari-marino/5 lg:sticky lg:top-44">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<strong className="flex items-center gap-2 text-lg font-black"> <strong className="flex items-center gap-2 text-lg font-black">
<SlidersHorizontal className="size-5 text-ari-cobre" /> <SlidersHorizontal className="size-5 text-ari-coral-claro" />
Filtros Filtros
</strong> </strong>
<button onClick={reset} className="text-xs font-black text-ari-ciruela"> <button onClick={reset} className="text-xs font-black text-ari-coral">
<X className="mr-1 inline size-3" /> <X className="mr-1 inline size-3" />
Limpiar Limpiar
</button> </button>
@@ -392,7 +392,7 @@ export function CatalogClient({
step={5000} step={5000}
value={maxPrice} value={maxPrice}
onChange={(event) => setMaxPrice(Number(event.target.value))} onChange={(event) => setMaxPrice(Number(event.target.value))}
className="w-full accent-ari-ciruela" className="w-full accent-ari-coral"
/> />
<p className="mt-2 text-xs font-bold text-slate-500"> <p className="mt-2 text-xs font-bold text-slate-500">
Hasta ${maxPrice.toLocaleString("es-CO")} Hasta ${maxPrice.toLocaleString("es-CO")}
@@ -470,8 +470,8 @@ function Filter({
onClick={() => onChange(value)} onClick={() => onChange(value)}
className={`rounded-md px-3 py-2 text-xs font-bold transition ${ className={`rounded-md px-3 py-2 text-xs font-bold transition ${
selected === value selected === value
? "bg-ari-carbon-oscuro text-white" ? "bg-ari-marino text-white"
: "bg-slate-100 text-slate-600 hover:bg-ari-cobre/10" : "bg-slate-100 text-slate-600 hover:bg-ari-coral-claro/10"
}`} }`}
> >
{value} {value}
@@ -33,15 +33,15 @@ export function ProductCard({ product, priority = false }: { product: Product; p
: null; : null;
return ( return (
<article className="group flex h-full flex-col overflow-hidden rounded-lg border border-slate-100 bg-white shadow-lg shadow-ari-carbon-oscuro/5 transition duration-300 hover:-translate-y-1.5 hover:shadow-2xl hover:shadow-ari-ciruela/10"> <article className="group flex h-full flex-col overflow-hidden rounded-lg border border-slate-100 bg-white shadow-lg shadow-ari-marino/5 transition duration-300 hover:-translate-y-1.5 hover:shadow-2xl hover:shadow-ari-coral/10">
<div className="relative aspect-square overflow-hidden bg-ari-hueso"> <div className="relative aspect-square overflow-hidden bg-white">
<Link href={`/product/${product.slug}`} prefetch={false}><Image src={product.thumbnail} alt={product.name} fill priority={priority} sizes="(max-width: 640px) 82vw, (max-width: 1024px) 44vw, 280px" className="object-cover transition duration-500 group-hover:scale-[1.035]" /></Link> <Link href={`/product/${product.slug}`} prefetch={false}><Image src={product.thumbnail} alt={product.name} fill priority={priority} sizes="(max-width: 640px) 82vw, (max-width: 1024px) 44vw, 280px" className="object-cover transition duration-500 group-hover:scale-[1.035]" /></Link>
<div className="absolute left-3 top-3 flex gap-2">{discountPercent && discountPercent > 0 && <Badge className="bg-ari-terracota-oscuro text-white">-{discountPercent}%</Badge>}{product.isNew && <Badge className="bg-ari-carbon-oscuro text-white">Nuevo</Badge>}{product.featured && <Badge className="bg-ari-cobre text-white"><Sparkles className="mr-1 size-3" />Top</Badge>}</div> <div className="absolute left-3 top-3 flex gap-2">{discountPercent && discountPercent > 0 && <Badge className="bg-ari-coral text-white">-{discountPercent}%</Badge>}{product.isNew && <Badge className="bg-ari-marino text-white">Nuevo</Badge>}{product.featured && <Badge className="bg-ari-coral-claro text-white"><Sparkles className="mr-1 size-3" />Top</Badge>}</div>
<button type="button" aria-label={isFavorite ? "Quitar de favoritos" : "Agregar a favoritos"} aria-pressed={isFavorite} className={`absolute right-3 top-3 rounded-full p-2.5 shadow-lg backdrop-blur transition ${isFavorite ? "bg-ari-ciruela text-white" : "bg-white/95 text-slate-900 hover:text-ari-ciruela"}`} onClick={() => toggleProduct(product)}><Heart className={`size-4 ${isFavorite ? "fill-current" : ""}`} /></button> <button type="button" aria-label={isFavorite ? "Quitar de favoritos" : "Agregar a favoritos"} aria-pressed={isFavorite} className={`absolute right-3 top-3 rounded-full p-2.5 shadow-lg backdrop-blur transition ${isFavorite ? "bg-ari-coral text-white" : "bg-white/95 text-slate-900 hover:text-ari-coral"}`} onClick={() => toggleProduct(product)}><Heart className={`size-4 ${isFavorite ? "fill-current" : ""}`} /></button>
</div> </div>
<div className="flex flex-1 flex-col space-y-3 p-5"> <div className="flex flex-1 flex-col space-y-3 p-5">
<div className="flex items-center justify-between gap-2 text-xs font-bold text-slate-500"><span>{product.category}</span><span className="rounded-md bg-ari-cobre/10 px-2 py-1 text-ari-cobre">{product.collection}</span></div> <div className="flex items-center justify-between gap-2 text-xs font-bold text-slate-500"><span>{product.category}</span><span className="rounded-md bg-ari-coral-claro/10 px-2 py-1 text-ari-coral-claro">{product.collection}</span></div>
<Link href={`/product/${product.slug}`} prefetch={false}><h3 className="line-clamp-2 min-h-12 text-lg font-black leading-6 text-ari-carbon group-hover:text-ari-ciruela">{product.name}</h3></Link> <Link href={`/product/${product.slug}`} prefetch={false}><h3 className="line-clamp-2 min-h-12 text-lg font-black leading-6 text-ari-marino group-hover:text-ari-coral">{product.name}</h3></Link>
<p className="line-clamp-2 min-h-10 text-sm leading-5 text-slate-500">{product.shortDescription}</p> <p className="line-clamp-2 min-h-10 text-sm leading-5 text-slate-500">{product.shortDescription}</p>
{product.variants.length > 1 && ( {product.variants.length > 1 && (
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-1.5">
@@ -55,7 +55,7 @@ export function ProductCard({ product, priority = false }: { product: Product; p
onClick={() => setSelectedVariantId(variant.id)} onClick={() => setSelectedVariantId(variant.id)}
className={`rounded-md border px-2.5 py-1 text-xs font-bold transition ${ className={`rounded-md border px-2.5 py-1 text-xs font-bold transition ${
selectedVariantId === variant.id selectedVariantId === variant.id
? "border-ari-ciruela bg-ari-ciruela/10 text-ari-ciruela" ? "border-ari-coral bg-ari-coral/10 text-ari-coral"
: "border-slate-200 text-slate-600 hover:border-slate-400" : "border-slate-200 text-slate-600 hover:border-slate-400"
} ${!variant.available ? "cursor-not-allowed opacity-40" : ""}`} } ${!variant.available ? "cursor-not-allowed opacity-40" : ""}`}
> >
@@ -70,7 +70,7 @@ export function ProductCard({ product, priority = false }: { product: Product; p
<div className="mt-auto flex items-center justify-between gap-3 pt-1"> <div className="mt-auto flex items-center justify-between gap-3 pt-1">
<div className="flex flex-col"> <div className="flex flex-col">
{product.compareAtPrice && <span className="text-xs font-bold text-slate-400 line-through">{formatMoney(product.compareAtPrice, product.currency)}</span>} {product.compareAtPrice && <span className="text-xs font-bold text-slate-400 line-through">{formatMoney(product.compareAtPrice, product.currency)}</span>}
<strong className="font-display text-base font-bold text-ari-carbon">{formatMoney(product.price, product.currency)}</strong> <strong className="font-display text-base font-bold text-ari-marino">{formatMoney(product.price, product.currency)}</strong>
</div> </div>
<Button variant="primary" size="sm" onClick={() => addItem(product, selectedVariantId)}><ShoppingBag className="size-4" />Agregar</Button> <Button variant="primary" size="sm" onClick={() => addItem(product, selectedVariantId)}><ShoppingBag className="size-4" />Agregar</Button>
</div> </div>