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`.
23 lines
2.2 KiB
TypeScript
23 lines
2.2 KiB
TypeScript
"use client";
|
|
|
|
import { CheckCircle2, ShoppingBag, TriangleAlert, Truck } from "lucide-react";
|
|
import { useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import type { Product } from "@/lib/headless/types";
|
|
import { formatMoney, LOW_STOCK_THRESHOLD } from "@/lib/utils";
|
|
import { useCartStore } from "@/store/cart-store";
|
|
|
|
export function ProductPurchase({ product }: { product: Product }) {
|
|
const [variantId, setVariantId] = useState(product.variants[0]?.id);
|
|
const addItem = useCartStore((state) => state.addItem);
|
|
const selectedVariant = product.variants.find((variant) => variant.id === variantId) ?? product.variants[0];
|
|
const lowStock =
|
|
selectedVariant?.inventoryQuantity !== null &&
|
|
selectedVariant?.inventoryQuantity !== undefined &&
|
|
selectedVariant.inventoryQuantity > 0 &&
|
|
selectedVariant.inventoryQuantity <= LOW_STOCK_THRESHOLD
|
|
? selectedVariant.inventoryQuantity
|
|
: null;
|
|
return <div><div className="mb-5 flex flex-wrap gap-2">{product.variants.map((variant) => <button key={variant.id} onClick={() => setVariantId(variant.id)} className={`rounded-full border px-4 py-2 text-sm font-bold ${variantId === variant.id ? "border-fuchsia-500 bg-fuchsia-50 text-fuchsia-700" : "border-slate-200"}`}>{variant.value}</button>)}</div><div className="mb-6 flex items-end gap-3"><strong className="text-3xl font-black">{formatMoney(product.price, product.currency)}</strong>{product.compareAtPrice && <del className="text-slate-400">{formatMoney(product.compareAtPrice, product.currency)}</del>}</div><Button variant="magenta" size="lg" className="w-full" onClick={() => addItem(product, variantId)}><ShoppingBag />Añadir al carrito</Button><div className="mt-5 grid gap-3 rounded-2xl bg-slate-50 p-4 text-sm">{lowStock !== null ? <span className="flex items-center gap-2 font-bold text-amber-600"><TriangleAlert className="size-4" />Quedan {lowStock} unidades</span> : <span className="flex items-center gap-2 font-bold"><CheckCircle2 className="size-4 text-emerald-500" />Disponibilidad por confirmar</span>}<span className="flex items-center gap-2 font-bold"><Truck className="size-4 text-cyan-600" />Envíos nacionales</span></div></div>;
|
|
}
|