Files
Academia-Front/src/components/buyer/CartDrawer.tsx
T
2026-08-21 22:53:15 -05:00

242 lines
9.9 KiB
TypeScript

import React, { useState } from 'react';
import {
FiX,
FiTrash2,
FiShoppingCart,
FiTag,
FiCheckCircle,
FiArrowRight,
FiLock,
FiShield
} from 'react-icons/fi';
import type { CartItem } from '../../types/course';
interface CartDrawerProps {
isOpen: boolean;
cartItems: CartItem[];
onClose: () => void;
onRemoveItem: (courseId: string) => void;
onClearCart: () => void;
}
export const CartDrawer: React.FC<CartDrawerProps> = ({
isOpen,
cartItems,
onClose,
onRemoveItem,
onClearCart,
}) => {
const [couponCode, setCouponCode] = useState('');
const [appliedDiscount, setAppliedDiscount] = useState<number>(0);
const [couponMessage, setCouponMessage] = useState<string | null>(null);
const [isCheckingOut, setIsCheckingOut] = useState(false);
const [checkoutSuccess, setCheckoutSuccess] = useState(false);
if (!isOpen) return null;
const rawSubtotal = cartItems.reduce((acc, item) => acc + item.course.price, 0);
const discountAmount = (rawSubtotal * appliedDiscount) / 100;
const finalTotal = Math.max(0, rawSubtotal - discountAmount);
const handleApplyCoupon = (e: React.FormEvent) => {
e.preventDefault();
const code = couponCode.trim().toUpperCase();
if (code === 'DESCUENTO20' || code === 'PROMO20') {
setAppliedDiscount(20);
setCouponMessage('¡Cupón del 20% aplicado con éxito!');
} else if (code === 'PROMO50' || code === 'RYNEXT50') {
setAppliedDiscount(50);
setCouponMessage('¡Cupón especial del 50% de descuento aplicado!');
} else if (code.length > 0) {
setCouponMessage('Cupón no válido. Prueba con "DESCUENTO20" o "RYNEXT50"');
}
};
const handleCheckout = () => {
setIsCheckingOut(true);
setTimeout(() => {
setIsCheckingOut(false);
setCheckoutSuccess(true);
setTimeout(() => {
setCheckoutSuccess(false);
onClearCart();
onClose();
}, 3000);
}, 1500);
};
return (
<div className="fixed inset-0 z-50 overflow-hidden bg-slate-950/70 backdrop-blur-sm animate-in fade-in duration-200">
<div className="absolute inset-y-0 right-0 max-w-full flex pl-10">
{/* Slide panel */}
<div className="w-screen max-w-md bg-slate-900 border-l border-slate-800 shadow-2xl flex flex-col justify-between">
{/* Top Header */}
<div className="p-6 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
<div className="flex items-center gap-2.5">
<div className="p-2 rounded-lg bg-cyan-500/10 text-cyan-400">
<FiShoppingCart className="text-lg" />
</div>
<div>
<h3 className="text-base font-bold text-white">Carrito de Compras</h3>
<p className="text-xs text-slate-400">
{cartItems.length} {cartItems.length === 1 ? 'curso seleccionado' : 'cursos seleccionados'}
</p>
</div>
</div>
<button
onClick={onClose}
className="p-2 rounded-xl text-slate-400 hover:text-white hover:bg-slate-800 transition"
aria-label="Cerrar carrito"
>
<FiX className="text-xl" />
</button>
</div>
{/* Cart Items List */}
<div className="flex-1 overflow-y-auto p-6 space-y-4">
{checkoutSuccess ? (
<div className="text-center py-12 space-y-4 animate-in zoom-in-95 duration-300">
<div className="w-16 h-16 rounded-full bg-emerald-500/20 text-emerald-400 flex items-center justify-center mx-auto text-3xl">
<FiCheckCircle />
</div>
<h4 className="text-xl font-bold text-white">¡Compra Realizada con Éxito!</h4>
<p className="text-xs text-slate-300 max-w-xs mx-auto">
Hemos enviado los accesos a tu correo. Tus cursos ya están disponibles en tu biblioteca de aprendizaje.
</p>
<div className="p-3 bg-slate-950 rounded-xl border border-slate-800 text-xs text-cyan-400 font-mono">
Orden #RY-{Math.floor(100000 + Math.random() * 900000)}
</div>
</div>
) : cartItems.length > 0 ? (
cartItems.map((item) => (
<div
key={item.course.id}
className="flex gap-3.5 p-3 rounded-xl bg-slate-950/60 border border-slate-800/80 hover:border-slate-700 transition"
>
<img
src={item.course.thumbnail}
alt={item.course.title}
className="w-20 h-14 object-cover rounded-lg bg-slate-800 shrink-0"
/>
<div className="flex-1 min-w-0 flex flex-col justify-between">
<div>
<h4 className="text-xs font-bold text-white line-clamp-1">
{item.course.title}
</h4>
<p className="text-[11px] text-slate-400 truncate">
Por {item.course.instructor.name}
</p>
</div>
<div className="flex items-center justify-between pt-1">
<span className="text-sm font-bold text-white font-mono">
${item.course.price.toFixed(2)}
</span>
<button
onClick={() => onRemoveItem(item.course.id)}
className="text-slate-500 hover:text-rose-400 text-xs flex items-center gap-1 transition"
title="Eliminar curso"
>
<FiTrash2 />
</button>
</div>
</div>
</div>
))
) : (
<div className="text-center py-16 space-y-4">
<div className="w-16 h-16 rounded-full bg-slate-800 text-slate-500 flex items-center justify-center mx-auto text-2xl">
<FiShoppingCart />
</div>
<h4 className="text-sm font-bold text-white">Tu carrito está vacío</h4>
<p className="text-xs text-slate-400 max-w-xs mx-auto">
Explora nuestro catálogo de cursos en desarrollo, IA y diseño para comenzar.
</p>
</div>
)}
</div>
{/* Bottom Summary & Checkout Form */}
{cartItems.length > 0 && !checkoutSuccess && (
<div className="p-6 border-t border-slate-800 bg-slate-950/90 space-y-4">
{/* Coupon Form */}
<form onSubmit={handleApplyCoupon} className="space-y-1.5">
<div className="flex gap-2">
<div className="relative flex-1">
<FiTag className="absolute left-3 top-3 text-slate-500 text-xs" />
<input
type="text"
value={couponCode}
onChange={(e) => setCouponCode(e.target.value)}
placeholder="Cupón (ej. DESCUENTO20)"
className="w-full pl-8 pr-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-xs text-white placeholder-slate-500 focus:outline-none focus:border-cyan-500 uppercase"
/>
</div>
<button
type="submit"
className="px-3 py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 font-semibold text-xs rounded-lg transition"
>
Aplicar
</button>
</div>
{couponMessage && (
<p className={`text-[11px] ${appliedDiscount > 0 ? 'text-emerald-400' : 'text-amber-400'}`}>
{couponMessage}
</p>
)}
</form>
{/* Price Breakdown */}
<div className="space-y-1.5 text-xs text-slate-400 pt-2 border-t border-slate-800">
<div className="flex justify-between">
<span>Subtotal</span>
<span className="font-mono text-slate-200">${rawSubtotal.toFixed(2)}</span>
</div>
{appliedDiscount > 0 && (
<div className="flex justify-between text-emerald-400">
<span>Descuento ({appliedDiscount}%)</span>
<span className="font-mono">-${discountAmount.toFixed(2)}</span>
</div>
)}
<div className="flex justify-between text-sm font-bold text-white pt-2 border-t border-slate-800">
<span>Total a pagar</span>
<span className="text-xl text-cyan-400 font-mono">${finalTotal.toFixed(2)}</span>
</div>
</div>
{/* Checkout Button */}
<button
onClick={handleCheckout}
disabled={isCheckingOut}
className="w-full py-3.5 bg-gradient-to-r from-blue-600 via-cyan-600 to-teal-500 hover:from-blue-500 hover:to-cyan-400 text-white font-bold rounded-xl text-sm shadow-xl shadow-cyan-500/25 transition-all flex items-center justify-center gap-2 cursor-pointer disabled:opacity-50"
>
{isCheckingOut ? (
<span>Procesando pago seguro...</span>
) : (
<>
<FiLock className="text-sm" />
<span>Completar Pago Seguro</span>
<FiArrowRight className="text-sm" />
</>
)}
</button>
<div className="flex items-center justify-center gap-2 text-[11px] text-slate-500">
<FiShield className="text-emerald-400" />
<span>Garantía total de devolución de 30 días</span>
</div>
</div>
)}
</div>
</div>
</div>
);
};