variant.available (calculado en lib/headless/providers/medusa.ts a partir de manage_inventory/allow_backorder/inventory_quantity) ya llegaba correcto hasta el componente, pero ningun boton de compra lo usaba -- se podia seguir agregando al carrito un producto con stock 0. Afecta product-purchase.tsx (pagina de producto), mobile-add-to-cart.tsx (barra fija mobile) y product-card.tsx (tarjetas de catalogo): boton deshabilitado + label "Agotado" en los tres, mas badge/overlay en la tarjeta de catalogo.
79 lines
3.1 KiB
TypeScript
79 lines
3.1 KiB
TypeScript
"use client";
|
|
|
|
import { CheckCircle2, ShoppingBag, TriangleAlert, Truck, XCircle } 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 outOfStock = selectedVariant?.available === false;
|
|
const lowStock =
|
|
!outOfStock &&
|
|
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-ari-marino bg-ari-marino text-white" : "border-slate-200"
|
|
} ${variant.available === false ? "opacity-40" : ""}`}
|
|
>
|
|
{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>
|
|
{/* oculto en mobile: MobileAddToCart (barra fija abajo, lg:hidden) ya cubre esa misma accion ahi -- sin el hidden aqui, ambos se mostraban a la vez. */}
|
|
<Button
|
|
variant="primary"
|
|
size="lg"
|
|
className="hidden w-full lg:inline-flex"
|
|
disabled={outOfStock}
|
|
onClick={() => addItem(product, variantId)}
|
|
>
|
|
<ShoppingBag />
|
|
{outOfStock ? "Agotado" : "Añadir al carrito"}
|
|
</Button>
|
|
<div className="mt-5 grid gap-3 rounded-2xl bg-slate-50 p-4 text-sm">
|
|
{outOfStock ? (
|
|
<span className="flex items-center gap-2 font-bold text-rose-600">
|
|
<XCircle className="size-4" />
|
|
Agotado
|
|
</span>
|
|
) : 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>
|
|
);
|
|
}
|