- Implemented CartContext for managing cart state, including adding, removing, and updating items. - Added CartDrawer component to display cart items. - Updated AppHeader to include cart item count. - Enhanced welcome page with new layout, improved product image handling, and updated call-to-action buttons. - Refactored homepage data types to accommodate new features and removed unused properties.
114 lines
6.8 KiB
TypeScript
114 lines
6.8 KiB
TypeScript
import { Button } from '@/components/ui/button';
|
|
import { useCart } from '@/contexts/cart-context';
|
|
import { formatRupiah } from '@/lib/rupiah';
|
|
import { formatWhatsAppPhone } from '@/lib/format';
|
|
import { Minus, Plus, ShoppingBag, ShoppingCart, Trash2, X } from 'lucide-react';
|
|
|
|
type CartDrawerProps = {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
contactPhone?: string | null;
|
|
};
|
|
|
|
export default function CartDrawer({ open, onClose, contactPhone }: CartDrawerProps) {
|
|
const { items, removeItem, updateQuantity, clearCart, totalItems, totalPrice } = useCart();
|
|
|
|
if (!open) return null;
|
|
|
|
const handleCheckout = () => {
|
|
if (items.length === 0 || !contactPhone) return;
|
|
|
|
const lines = items.map((item) => {
|
|
const subtotal = item.price * item.quantity;
|
|
return `*${item.productName}*\n* Varian: ${item.variantName}\n* Harga: Rp ${formatRupiah(item.price)}\n* QTY : ${item.quantity}\n* Subtotal : Rp ${formatRupiah(subtotal)}`;
|
|
});
|
|
const message = `Halo min, saya mau order, berikut pesanannya.\n\n${lines.join('\n\n')}\n\nTotal: Rp ${formatRupiah(totalPrice)}\n\nMohon diinformasikan detail pembayaran dan proses selanjutnya ya, Kak.\n\nTerimakasih`;
|
|
const waUrl = `https://wa.me/${formatWhatsAppPhone(contactPhone || '')}?text=${encodeURIComponent(message)}`;
|
|
window.open(waUrl, '_blank');
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex justify-end" onClick={onClose}>
|
|
<div className="absolute inset-0 bg-black/40 backdrop-blur-sm" />
|
|
<div
|
|
className="relative w-full max-w-md bg-[#FDFDFC] shadow-2xl flex flex-col animate-in slide-in-from-right duration-300"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between p-5 border-b border-amber-100/30">
|
|
<div className="flex items-center gap-2">
|
|
<ShoppingBag className="w-5 h-5 text-amber-600" />
|
|
<h3 className="text-lg font-serif text-slate-800">Keranjang</h3>
|
|
<span className="text-xs text-slate-400">({totalItems} item)</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
{items.length > 0 && (
|
|
<Button variant="ghost" size="sm" onClick={clearCart} className="text-xs text-red-500 hover:text-red-600 hover:bg-red-50">
|
|
<Trash2 className="w-3.5 h-3.5 mr-1" />
|
|
Hapus Semua
|
|
</Button>
|
|
)}
|
|
<Button variant="ghost" size="icon" onClick={onClose} className="rounded-full hover:bg-slate-100">
|
|
<X className="w-5 h-5 text-slate-500" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Items */}
|
|
<div className="flex-1 overflow-y-auto p-5 space-y-4">
|
|
{items.length === 0 ? (
|
|
<div className="flex flex-col items-center justify-center h-full text-center space-y-3">
|
|
<ShoppingBag className="w-12 h-12 text-slate-200" />
|
|
<p className="text-sm text-slate-400">Keranjang kosong</p>
|
|
<p className="text-xs text-slate-300">Tambahkan produk untuk mulai berbelanja</p>
|
|
</div>
|
|
) : (
|
|
items.map((item) => (
|
|
<div key={item.variantId} className="flex gap-3 p-3 bg-white rounded-xl border border-slate-100 shadow-sm">
|
|
<div className="w-16 h-20 rounded-lg overflow-hidden shrink-0">
|
|
<img src={item.imageUrl} alt={item.productName} className="w-full h-full object-cover" />
|
|
</div>
|
|
<div className="flex-1 min-w-0 space-y-1">
|
|
<p className="text-sm font-semibold text-slate-800 truncate">{item.productName}</p>
|
|
<p className="text-[10px] text-slate-400">{item.variantName} · {item.priceType === 'retail' ? 'Retail' : 'Grosir'}</p>
|
|
<p className="text-sm font-bold text-amber-600">Rp {formatRupiah(item.price)}</p>
|
|
<div className="flex items-center justify-between pt-1">
|
|
<div className="flex items-center gap-2 border border-slate-200 rounded-lg px-2 py-0.5">
|
|
<Button variant="ghost" size="icon-xs" onClick={() => updateQuantity(item.variantId, item.quantity - 1)}>
|
|
<Minus className="w-3 h-3" />
|
|
</Button>
|
|
<span className="text-xs font-semibold w-5 text-center">{item.quantity}</span>
|
|
<Button variant="ghost" size="icon-xs" onClick={() => updateQuantity(item.variantId, item.quantity + 1)}>
|
|
<Plus className="w-3 h-3" />
|
|
</Button>
|
|
</div>
|
|
<Button variant="ghost" size="icon-xs" onClick={() => removeItem(item.variantId)} className="text-red-400 hover:text-red-500 hover:bg-red-50">
|
|
<Trash2 className="w-3.5 h-3.5" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
{items.length > 0 && (
|
|
<div className="border-t border-amber-100/30 p-5 space-y-4">
|
|
<div className="flex justify-between items-center">
|
|
<span className="text-sm text-slate-500">Total</span>
|
|
<span className="text-xl font-bold text-amber-600">Rp {formatRupiah(totalPrice)}</span>
|
|
</div>
|
|
<Button asChild className="w-full bg-amber-600 text-white hover:bg-amber-500 py-3.5 text-xs font-bold uppercase tracking-widest rounded-xl transition-all shadow-md">
|
|
<a href="#" onClick={(e) => { e.preventDefault(); handleCheckout(); }}>
|
|
<ShoppingCart className="w-4 h-4 mr-2" />
|
|
Beli Sekarang
|
|
</a>
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|