Files
apps-registry/workloads/ecommerce/components/cart/cart-drawer.tsx
devops a01ab55ebe feat(ecommerce): ADR 0007 Opcion 2 "Atelier calido" - Sub-fase 1+3
Identidad visual propia premium (ver docs-portal/docs/decisiones/0007-identidad-visual-propia.md,
revision 2). Correccion de proceso respecto al intento anterior
(v1.0.176, revertido): en vez de un token aislado en un solo boton,
esta pasada toca de punta a punta los componentes que se ven juntos en
la misma pantalla para que ninguna card quede a mitad de camino.

- globals.css: paleta ciruela/carbon-oscuro/cobre/hueso (reemplaza
  cyan/magenta/yellow/blue, confirmado sin uso previo). Domine
  (titulares/precio) + Inter (UI/cuerpo) via next/font/google -- antes
  "Inter" era solo un fallback de CSS sin garantia real de carga.
- button.tsx: variant "magenta" renombrada a "primary" (ya no es
  magenta, es ciruela solida) y su color redefinido -- renombrada en
  los 8 archivos que la usaban para no dejar un nombre enganoso.
  Radio base rounded-full -> rounded-lg, ring de foco retintado.
- product-card.tsx: badges NUEVO/TOP/descuento en la paleta nueva (no
  mas fucsia/amarillo neon), precio en Domine, radio y sombra
  consistentes con el boton.
- catalog-client.tsx: solo clases de color/radio en breadcrumb, filtro
  de precio, pills de filtro y boton de reset -- cero cambios de
  logica.

Header, footer, home y pagina de producto/drawers quedan con la
paleta anterior a proposito -- Sub-fases 2, 4 y 5 pendientes.

Validado con `docker build --target builder` (npm ci + next build con
typecheck completo) -- compila limpio.
2026-08-27 01:18:16 -05:00

59 lines
4.3 KiB
TypeScript

"use client";
import Image from "next/image";
import { Minus, Plus, ShoppingBag, Trash2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Progress } from "@/components/ui/progress";
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { formatMoney } from "@/lib/utils";
import { useCartStore } from "@/store/cart-store";
const FREE_SHIPPING_THRESHOLD = 250_000;
export function CartDrawer() {
const { items, isOpen, closeCart, updateQuantity, removeItem, clearCart } = useCartStore();
const subtotal = items.reduce((sum, item) => sum + (item.product.price ?? 0) * item.quantity, 0);
const pricedItems = items.some((item) => item.product.price !== null);
const progress = pricedItems ? Math.min(100, (subtotal / FREE_SHIPPING_THRESHOLD) * 100) : 0;
const count = items.reduce((sum, item) => sum + item.quantity, 0);
const checkout = () => {
const lines = items.map((item) => `• ${item.quantity} x ${item.product.name}`).join("\n");
const message = `Hola Ari Shopping 👋\nQuiero consultar disponibilidad para:\n\n${lines}\n\nNombre:\nCiudad:\nForma de entrega:`;
void navigator.clipboard?.writeText(message);
window.open(process.env.NEXT_PUBLIC_WHATSAPP_URL ?? "https://wa.link/rvcqqc", "_blank", "noopener,noreferrer");
};
return (
<Sheet open={isOpen} onOpenChange={(open) => { if (!open) closeCart(); }}>
<SheetContent>
<SheetHeader>
<SheetTitle>Tu carrito</SheetTitle>
<SheetDescription>{count} producto{count === 1 ? "" : "s"} seleccionado{count === 1 ? "" : "s"}.</SheetDescription>
</SheetHeader>
<div className="flex-1 space-y-5 overflow-y-auto p-6">
{items.length === 0 ? (
<div className="grid min-h-72 place-items-center rounded-3xl border border-dashed border-slate-300 bg-slate-50 text-center">
<div><ShoppingBag className="mx-auto mb-3 size-10 text-cyan-500" /><p className="font-black">Tu carrito está vacío</p><p className="mt-1 text-sm text-slate-500">Agrega un tesoro para comenzar.</p></div>
</div>
) : items.map((item) => (
<article key={`${item.product.id}-${item.variantId ?? "default"}`} className="grid grid-cols-[82px_1fr_auto] gap-4 rounded-2xl border border-slate-100 p-3 shadow-sm">
<div className="relative aspect-square overflow-hidden rounded-xl bg-slate-100"><Image src={item.product.thumbnail} alt={item.product.name} fill sizes="82px" className="object-cover" /></div>
<div><h3 className="text-sm font-black leading-5">{item.product.name}</h3><p className="mt-1 text-xs text-slate-500">{formatMoney(item.product.price, item.product.currency)}</p><div className="mt-3 inline-flex items-center rounded-full border border-slate-200"><button aria-label="Restar" className="p-2" onClick={() => updateQuantity(item.product.id, item.quantity - 1)}><Minus className="size-3" /></button><span className="min-w-7 text-center text-xs font-black">{item.quantity}</span><button aria-label="Sumar" className="p-2" onClick={() => updateQuantity(item.product.id, item.quantity + 1)}><Plus className="size-3" /></button></div></div>
<button className="self-start rounded-full p-2 text-slate-400 hover:bg-rose-50 hover:text-rose-600" onClick={() => removeItem(item.product.id)} aria-label="Eliminar"><Trash2 className="size-4" /></button>
</article>
))}
</div>
<div className="space-y-4 border-t border-slate-100 p-6">
<div><div className="mb-2 flex justify-between text-xs font-bold text-slate-600"><span>{pricedItems ? "Progreso para envío gratis" : "Cotización personalizada"}</span><span>{pricedItems ? `${Math.round(progress)}%` : "Por confirmar"}</span></div><Progress value={progress} /></div>
<div className="flex items-center justify-between"><span className="font-bold text-slate-600">Subtotal</span><strong className="text-xl font-black">{pricedItems ? formatMoney(subtotal) : "Por confirmar"}</strong></div>
<Button variant="primary" size="lg" className="w-full" disabled={!items.length} onClick={checkout}>Continuar por WhatsApp</Button>
<Button variant="ghost" className="w-full" disabled={!items.length} onClick={clearCart}>Vaciar carrito</Button>
</div>
</SheetContent>
</Sheet>
);
}