From 474ac3795b0117af6f175b548ce272f89dbf6843 Mon Sep 17 00:00:00 2001 From: Cristian Felipe Cruz Buitron Date: Sat, 29 Aug 2026 20:22:55 +0000 Subject: [PATCH] fix(ecommerce): deshabilitar compra y mostrar "Agotado" sin stock real 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. --- .../components/product/mobile-add-to-cart.tsx | 16 ++++- .../components/product/product-card.tsx | 10 +++- .../components/product/product-purchase.tsx | 60 ++++++++++++++++++- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/workloads/ecommerce/components/product/mobile-add-to-cart.tsx b/workloads/ecommerce/components/product/mobile-add-to-cart.tsx index 9ea2d4d..22bb946 100644 --- a/workloads/ecommerce/components/product/mobile-add-to-cart.tsx +++ b/workloads/ecommerce/components/product/mobile-add-to-cart.tsx @@ -7,5 +7,19 @@ import { useCartStore } from "@/store/cart-store"; export function MobileAddToCart({ product }: { product: Product }) { const addItem = useCartStore((state) => state.addItem); - return
; + const outOfStock = product.variants[0]?.available === false; + return ( +
+ +
+ ); } diff --git a/workloads/ecommerce/components/product/product-card.tsx b/workloads/ecommerce/components/product/product-card.tsx index 2893dec..8fd4dd2 100644 --- a/workloads/ecommerce/components/product/product-card.tsx +++ b/workloads/ecommerce/components/product/product-card.tsx @@ -24,7 +24,9 @@ export function ProductCard({ product, priority = false }: { product: Product; p const [selectedVariantId, setSelectedVariantId] = useState(product.variants[0]?.id); const selectedVariant = product.variants.find((variant) => variant.id === selectedVariantId) ?? product.variants[0]; + const outOfStock = selectedVariant?.available === false; const lowStock = + !outOfStock && selectedVariant?.inventoryQuantity !== null && selectedVariant?.inventoryQuantity !== undefined && selectedVariant.inventoryQuantity > 0 && @@ -36,7 +38,8 @@ export function ProductCard({ product, priority = false }: { product: Product; p
{product.name} -
{discountPercent && discountPercent > 0 && -{discountPercent}%}{product.isNew && Nuevo}{product.featured && Top}
+
{outOfStock && Agotado}{discountPercent && discountPercent > 0 && -{discountPercent}%}{product.isNew && Nuevo}{product.featured && Top}
+ {outOfStock &&
}
@@ -67,12 +70,15 @@ export function ProductCard({ product, priority = false }: { product: Product; p {lowStock !== null && ( Quedan {lowStock} )} + {outOfStock && ( + Sin stock + )}
{product.compareAtPrice && {formatMoney(product.compareAtPrice, product.currency)}} {formatMoney(product.price, product.currency)}
- +
diff --git a/workloads/ecommerce/components/product/product-purchase.tsx b/workloads/ecommerce/components/product/product-purchase.tsx index bd993c8..a9a24ba 100644 --- a/workloads/ecommerce/components/product/product-purchase.tsx +++ b/workloads/ecommerce/components/product/product-purchase.tsx @@ -1,6 +1,6 @@ "use client"; -import { CheckCircle2, ShoppingBag, TriangleAlert, Truck } from "lucide-react"; +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"; @@ -11,12 +11,68 @@ 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
{product.variants.map((variant) => )}
{formatMoney(product.price, product.currency)}{product.compareAtPrice && {formatMoney(product.compareAtPrice, product.currency)}}
{/* 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. */}
{lowStock !== null ? Quedan {lowStock} unidades : Disponibilidad por confirmar}Envíos nacionales
; + + return ( +
+
+ {product.variants.map((variant) => ( + + ))} +
+
+ {formatMoney(product.price, product.currency)} + {product.compareAtPrice && {formatMoney(product.compareAtPrice, product.currency)}} +
+ {/* 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. */} + +
+ {outOfStock ? ( + + + Agotado + + ) : lowStock !== null ? ( + + + Quedan {lowStock} unidades + + ) : ( + + + Disponibilidad por confirmar + + )} + + + Envíos nacionales + +
+
+ ); }