feat(ecommerce): aprovechar mejor el espacio en la página de producto
- "Detalles del producto" (marca, edad, categoría, colección) pasa de
acordeón colapsado a una mini tabla siempre visible -- mismos datos
reales de siempre, solo que ya no hace falta un click para verlos.
- Buy-box: nueva franja de confianza real con enlaces a /envios y
/cambios (5 días hábiles, envío de vuelta gratis) -- mismas
políticas ya publicadas en el footer, no texto inventado.
- "También te puede interesar" ahora muestra hasta 2 sugerencias en
vez de 1, mismo cálculo real por categoría/colección que ya
alimentaba el carrusel de abajo.
Investigado de paso (sin fix): la discrepancia de stock reportada
("Quedan 1" vs available:false) no es un bug de ProductPurchase -- la
página de producto usa /store/products de Medusa (en vivo, correcto),
mientras que /store/search (Meilisearch) devuelve inventario
desactualizado para el mismo producto. Eso vive en commerce-backend,
fuera de alcance de este cambio de frontend.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012Y4df3Z7odr9Cg8F2nw6uV
This commit is contained in:
1 parent
9b545dd850
commit
c3f3857a92
2 files changed
+37
-12
No files matched your search
@@ -34,7 +34,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
if (!product) notFound();
|
||||
const products = await getProducts();
|
||||
const recommended = products.filter((candidate) => candidate.id !== product.id && (candidate.category === product.category || candidate.collection === product.collection)).slice(0, 8);
|
||||
const suggested = recommended[0];
|
||||
const suggestions = recommended.slice(0, 2);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -51,11 +51,18 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
<span className="text-xs font-black uppercase tracking-[.18em] text-ari-coral">{product.category} · {product.collection}</span>
|
||||
<h1 className="mt-3 text-4xl font-black tracking-tight sm:text-6xl">{product.name}</h1>
|
||||
<p className="mt-5 text-lg leading-8 text-slate-600">{product.description}</p>
|
||||
<Accordion type="single" collapsible className="mt-8">
|
||||
<AccordionItem value="details">
|
||||
<AccordionTrigger>Detalles del producto</AccordionTrigger>
|
||||
<AccordionContent>Marca: {product.brand}. Edad recomendada: {product.ageRange}. La disponibilidad y presentación final se confirman con Ari Shopping.</AccordionContent>
|
||||
</AccordionItem>
|
||||
{/* Antes escondido detrás de un click en el acordeón --
|
||||
siempre visible, estilo Amazon, con los mismos datos
|
||||
reales que ya existían (marca/edad/categoría/colección),
|
||||
sin inventar specs que el catálogo no tiene. */}
|
||||
<h2 className="mb-4 mt-8 text-sm font-black uppercase tracking-wide text-slate-500">Detalles del producto</h2>
|
||||
<dl className="grid grid-cols-2 gap-x-6 gap-y-4 rounded-2xl border border-slate-200 p-5 text-sm sm:grid-cols-4">
|
||||
<div><dt className="text-slate-400">Marca</dt><dd className="mt-0.5 font-bold text-slate-900">{product.brand}</dd></div>
|
||||
<div><dt className="text-slate-400">Edad recomendada</dt><dd className="mt-0.5 font-bold text-slate-900">{product.ageRange}</dd></div>
|
||||
<div><dt className="text-slate-400">Categoría</dt><dd className="mt-0.5 font-bold text-slate-900">{product.category}</dd></div>
|
||||
<div><dt className="text-slate-400">Colección</dt><dd className="mt-0.5 font-bold text-slate-900">{product.collection}</dd></div>
|
||||
</dl>
|
||||
<Accordion type="single" collapsible className="mt-6">
|
||||
<AccordionItem value="shipping">
|
||||
<AccordionTrigger>Envíos y entregas</AccordionTrigger>
|
||||
<AccordionContent>Coordinamos envíos nacionales y entrega según ubicación. Los tiempos se confirman durante la cotización.</AccordionContent>
|
||||
@@ -64,17 +71,19 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
</div>
|
||||
{/* Buy box estilo Amazon: precio/variantes/carrito en su propia
|
||||
tarjeta, sticky desde xl mientras el resto de la pagina
|
||||
scrollea. El sugerido usa el mismo cálculo de "recommended"
|
||||
que ya alimenta el carrusel de abajo -- mismo criterio real
|
||||
(categoría/colección), no un dato inventado. */}
|
||||
scrollea. Los sugeridos (hasta 2) usan el mismo cálculo de
|
||||
"recommended" que ya alimenta el carrusel de abajo -- mismo
|
||||
criterio real (categoría/colección), no un dato inventado. */}
|
||||
<div className="xl:sticky xl:top-24 xl:self-start">
|
||||
<div className="rounded-2xl border border-slate-200 p-6 shadow-sm">
|
||||
<ProductPurchase product={product} />
|
||||
</div>
|
||||
{suggested && (
|
||||
{suggestions.length > 0 && (
|
||||
<div className="mt-6">
|
||||
<h2 className="mb-4 text-xs font-black uppercase tracking-[.14em] text-slate-500">También te puede interesar</h2>
|
||||
<ProductCard product={suggested} />
|
||||
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-1">
|
||||
{suggestions.map((item) => <ProductCard key={item.id} product={item} />)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { CheckCircle2, ShoppingBag, TriangleAlert, Truck, XCircle } from "lucide-react";
|
||||
import { CheckCircle2, RotateCcw, ShoppingBag, TriangleAlert, Truck, XCircle } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { Product } from "@/lib/headless/types";
|
||||
@@ -79,6 +80,21 @@ export function ProductPurchase({ product }: { product: Product }) {
|
||||
Envíos nacionales
|
||||
</span>
|
||||
</div>
|
||||
{/* Franja de confianza real (estilo Walmart) -- enlaza a las
|
||||
páginas reales de /envios y /cambios en vez de repetir texto
|
||||
suelto, con los datos reales que confirmó el usuario (5 días
|
||||
hábiles, envío de vuelta a cargo de ARI Shopping). Nada
|
||||
inventado: mismas políticas ya publicadas en el footer. */}
|
||||
<div className="mt-3 grid gap-2 text-sm">
|
||||
<Link href="/envios" className="flex items-center gap-2 font-bold text-slate-600 hover:text-ari-marino hover:underline">
|
||||
<Truck className="size-4 text-cyan-600" />
|
||||
Envíos calculados en el checkout · ver detalles
|
||||
</Link>
|
||||
<Link href="/cambios" className="flex items-center gap-2 font-bold text-slate-600 hover:text-ari-marino hover:underline">
|
||||
<RotateCcw className="size-4 text-ari-marino" />
|
||||
Cambios en 5 días hábiles, envío de vuelta gratis
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user