almeira-web/dripzone-app/src/components/store/CartDrawer.tsx

270 lines
13 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import React from 'react';
import Link from 'next/link';
import { X, Trash2, ShoppingBag, ArrowRight, Zap, Truck, MessageCircle } from 'lucide-react';
import { CartItem, City } from '@/types';
import { formatCOP, calculateSeparationBreakdown } from '@/lib/pricing-engine';
import { useBrand } from '@/context/BrandContext';
import { renderClientTemplate } from '@/lib/templates-client';
import { OptimizedImage } from '@/components/ui/OptimizedImage';
interface CartDrawerProps {
isOpen: boolean;
onClose: () => void;
cartItems: CartItem[];
onRemoveItem: (productId: string, size: string) => void;
onUpdateQuantity: (productId: string, size: string, delta: number) => void;
selectedCity: City;
onOpenCitySelector: () => void;
}
export const CartDrawer: React.FC<CartDrawerProps> = ({
isOpen,
onClose,
cartItems,
onRemoveItem,
onUpdateQuantity,
selectedCity,
onOpenCitySelector,
}) => {
const { currentBrand } = useBrand();
const [officialPhone, setOfficialPhone] = React.useState('573106390296');
React.useEffect(() => {
fetch('/api/settings')
.then((r) => r.json())
.then((d) => {
if (d.success && d.settings?.whatsappNumber) {
setOfficialPhone(d.settings.whatsappNumber);
}
})
.catch(() => {});
}, []);
if (!isOpen) return null;
const totalQuantity = cartItems.reduce((acc, item) => acc + item.quantity, 0);
const subtotal = cartItems.reduce((acc, item) => acc + item.product.retailPrice * item.quantity, 0);
const shippingCost = selectedCity.shippingCost;
const totalAmount = subtotal + shippingCost;
const separation = calculateSeparationBreakdown(totalAmount);
const handleWhatsAppCheckout = () => {
const itemsList = cartItems
.map((item) => `👟 *${item.product.name}* (Talla: *${item.selectedSize}*, Cant: ${item.quantity}) - ${formatCOP(item.product.retailPrice * item.quantity)}`)
.join('\n');
const paymentCondition = `⚡ *Condición de Pago:* Pago Contraentrega (Separo con $10.000 COP y cancelo saldo de ${formatCOP(separation.balanceDue)} al recibir).`;
const rawText = renderClientTemplate('order_checkout_wa', {
tienda_nombre: currentBrand.name,
orden_id: `WEB-${Date.now().toString().slice(-4)}`,
cliente_nombre: 'Cliente',
cliente_telefono: '',
cliente_direccion: '',
cliente_ciudad: selectedCity.name,
articulos_resumen: itemsList,
total: `${formatCOP(totalAmount)} COP`,
condicion_pago_resumen: paymentCondition,
metodo_pago: 'Pago Contraentrega',
saldo_contraentrega: formatCOP(separation.balanceDue),
});
window.open(`https://wa.me/${officialPhone}?text=${encodeURIComponent(rawText)}`, '_blank');
};
return (
<div className="fixed inset-0 z-50 overflow-hidden animate-in fade-in duration-200">
{/* Backdrop */}
<div onClick={onClose} className="absolute inset-0 bg-black/80 backdrop-blur-xs transition-opacity" />
<div className="fixed inset-y-0 right-0 max-w-full flex pl-10">
<div className={`w-screen max-w-md bg-slate-900 border-l ${currentBrand.colors.border} text-white shadow-2xl flex flex-col justify-between`}>
{/* Header */}
<div className="p-4 border-b border-slate-800 flex items-center justify-between bg-slate-950">
<div className="flex items-center space-x-2">
<div className={`w-8 h-8 rounded-full bg-gradient-to-r ${currentBrand.colors.primaryGradient} flex items-center justify-center text-white font-bold shadow-sm`}>
<ShoppingBag className="w-4 h-4" />
</div>
<div>
<h2 className="font-black text-white text-base">Tu Carrito • {currentBrand.shortName}</h2>
<p className="text-xs text-slate-400">{cartItems.length} {cartItems.length === 1 ? 'artículo' : 'artículos'}</p>
</div>
</div>
<button
onClick={onClose}
className="w-8 h-8 rounded-full bg-slate-800 hover:bg-slate-700 text-slate-300 flex items-center justify-center transition-all cursor-pointer"
>
<X className="w-4 h-4" />
</button>
</div>
{/* Items List */}
<div className="flex-1 overflow-y-auto p-4 space-y-3">
{cartItems.length === 0 ? (
<div className="h-full flex flex-col items-center justify-center text-center p-6 space-y-3">
<div className="w-16 h-16 rounded-full bg-white/5 border border-white/10 flex items-center justify-center text-slate-400">
<ShoppingBag className="w-8 h-8" />
</div>
<h3 className="font-bold text-white text-base">Tu carrito está vacío</h3>
<p className="text-xs text-slate-400 max-w-xs">
Aprovecha las ofertas de calzado con pago contraentrega en Cali y despachos nacionales.
</p>
<button
onClick={onClose}
className={`bg-gradient-to-r ${currentBrand.colors.primaryGradient} text-white text-xs font-bold px-4 py-2 rounded-full mt-2 cursor-pointer shadow-md`}
>
Explorar Zapatillas
</button>
</div>
) : (
cartItems.map((item, idx) => (
<div
key={`${item.product.id}-${item.selectedSize}-${idx}`}
className="flex gap-3 bg-slate-950 p-3 rounded-2xl border border-slate-800 relative group"
>
<OptimizedImage
src={item.product.images[0]}
alt={item.product.name}
width={160}
height={160}
quality={80}
className="w-20 h-20 rounded-xl object-cover bg-slate-900 shrink-0 border border-slate-700"
containerClassName="w-20 h-20 rounded-xl shrink-0"
/>
<div className="flex-1 flex flex-col justify-between">
<div>
<div className="flex items-start justify-between gap-1">
<h4 className="font-bold text-white text-xs line-clamp-2 leading-snug">
{item.product.name}
</h4>
<button
onClick={() => onRemoveItem(item.product.id, item.selectedSize)}
className="text-slate-500 hover:text-rose-400 transition-colors p-1 cursor-pointer"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
</div>
<div className="flex items-center gap-2 mt-1">
<span className="text-[11px] font-bold bg-slate-900 text-white px-2 py-0.5 rounded border border-slate-700">
Talla {item.selectedSize}
</span>
<span className="text-[11px] font-semibold text-slate-400">
{item.product.brand}
</span>
</div>
</div>
<div className="flex items-center justify-between pt-2">
<span className={`font-black text-sm text-transparent bg-clip-text bg-gradient-to-r ${currentBrand.colors.primaryGradient}`}>
{formatCOP(item.product.retailPrice * item.quantity)}
</span>
<div className="flex items-center border border-slate-700 rounded-lg bg-slate-900 overflow-hidden">
<button
onClick={() => onUpdateQuantity(item.product.id, item.selectedSize, -1)}
className="px-2 py-0.5 text-xs text-slate-300 hover:bg-slate-800 cursor-pointer font-bold"
>
-
</button>
<span className="px-2 py-0.5 text-xs font-bold text-white">{item.quantity}</span>
<button
onClick={() => onUpdateQuantity(item.product.id, item.selectedSize, 1)}
className="px-2 py-0.5 text-xs text-slate-300 hover:bg-slate-800 cursor-pointer font-bold"
>
+
</button>
</div>
</div>
</div>
</div>
))
)}
</div>
{/* Footer & Checkout Breakdown */}
{cartItems.length > 0 && (
<div className="p-4 border-t border-slate-800 bg-slate-950 space-y-3">
{/* City Delivery selector pill */}
<div className="bg-slate-900 p-2.5 rounded-xl border border-slate-800 flex items-center justify-between text-xs">
<div className="flex items-center space-x-1.5">
<Truck className="w-4 h-4 text-white" />
<span>Destino: <strong className="text-white">{selectedCity.name}</strong></span>
</div>
<button
onClick={onOpenCitySelector}
className="text-white hover:underline font-bold text-[11px] cursor-pointer"
>
Cambiar
</button>
</div>
{/* Price Calculations */}
<div className="space-y-1.5 text-xs text-slate-400">
<div className="flex justify-between">
<span>Subtotal:</span>
<span className="font-bold text-white">{formatCOP(subtotal)}</span>
</div>
<div className="flex justify-between">
<span>Costo de Envío:</span>
<span className="font-bold text-white">
{shippingCost === 0 ? <span className="text-emerald-400 uppercase font-black">Gratis</span> : formatCOP(shippingCost)}
</span>
</div>
<div className="flex justify-between text-sm font-bold text-white pt-1 border-t border-slate-800">
<span>Total de la Orden:</span>
<span className="font-bold text-white">
{formatCOP(totalAmount)}
</span>
</div>
<div className="flex justify-between text-xs text-purple-300 font-semibold pt-1">
<span>⚡ Separación (1er Pedido):</span>
<span className="font-bold font-mono">$10.000 COP</span>
</div>
<div className="flex justify-between text-xs text-emerald-400 font-black pt-1 border-t border-white/5">
<span>🤝 Saldo Restante Contraentrega:</span>
<span className="font-mono">{formatCOP(Math.max(0, totalAmount - 10000))}</span>
</div>
</div>
{/* Separation Highlight Banner */}
<div className="bg-white/5 border border-white/10 rounded-xl p-2.5 text-xs space-y-1">
<p className="font-bold text-white flex items-center gap-1">
<Zap className="w-3.5 h-3.5 fill-white" />
Separación fija de $10.000 COP (1er Pedido)
</p>
<p className="text-slate-300 text-[11px]">
Separas tu pedido completo ({totalQuantity} {totalQuantity > 1 ? 'productos' : 'producto'}) con una tarifa única de <strong>$10.000 COP</strong> y cancelas el saldo restante (<strong>{formatCOP(Math.max(0, totalAmount - 10000))}</strong>) contraentrega al recibir en tu puerta.
</p>
<p className="text-[10px] text-emerald-400 font-semibold pt-0.5 border-t border-white/5">
🛡️ {selectedCity.firstExchangeFree
? `1er Cambio de Talla 100% GRATIS en ${selectedCity.name}`
: `Garantía de cambio en ${selectedCity.name} (flete asumido por cliente)`}
</p>
</div>
{/* Action Button */}
<div className="pt-1 space-y-2">
<div className="flex items-center justify-center gap-1.5 text-[11px] text-purple-300 font-semibold bg-purple-950/40 border border-purple-500/20 py-1.5 px-3 rounded-xl">
<span>🎟️ ¿Tienes cupón de descuento? Podrás aplicarlo al pagar</span>
</div>
<Link
href="/checkout"
onClick={onClose}
className={`w-full bg-gradient-to-r ${currentBrand.colors.primaryGradient} text-white font-black text-sm py-3.5 px-4 rounded-xl flex items-center justify-center space-x-2 shadow-lg active:scale-95 transition-all text-center`}
>
<span>Continuar al Pago Contraentrega</span>
<ArrowRight className="w-4 h-4" />
</Link>
</div>
</div>
)}
</div>
</div>
</div>
);
};