Deploy
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
'use client'
|
||||
import { useState, useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { ArrowRight, Minus, Package, Plus, ShoppingCart, Trash2, X } from 'lucide-react'
|
||||
import { useVulaCart } from '@/hooks/useVulaCart'
|
||||
|
||||
const fmt = (n: number) => `R${n.toFixed(2)}`
|
||||
|
||||
export default function CartDrawer() {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const { items, subtotal, itemCount, updateQty, removeFromCart } = useVulaCart()
|
||||
|
||||
useEffect(() => {
|
||||
const open = () => setIsOpen(true)
|
||||
window.addEventListener('cart-drawer-open', open)
|
||||
return () => window.removeEventListener('cart-drawer-open', open)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
document.body.style.overflow = isOpen ? 'hidden' : ''
|
||||
return () => { document.body.style.overflow = '' }
|
||||
}, [isOpen])
|
||||
|
||||
const close = () => setIsOpen(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
{isOpen && (
|
||||
<div className="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm" onClick={close} aria-hidden="true" />
|
||||
)}
|
||||
<div
|
||||
className={`fixed right-0 top-0 z-50 h-full w-full max-w-md bg-background shadow-2xl transition-transform duration-300 ease-in-out flex flex-col ${isOpen ? 'translate-x-0' : 'translate-x-full'}`}
|
||||
role="dialog" aria-label="Shopping cart"
|
||||
>
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<div className="flex items-center gap-2">
|
||||
<ShoppingCart className="h-5 w-5 text-foreground" />
|
||||
<h2 className="text-lg font-semibold text-foreground">Your Cart</h2>
|
||||
{itemCount > 0 && (
|
||||
<span className="text-xs bg-primary text-primary-foreground rounded-full px-2 py-0.5 font-medium">{itemCount}</span>
|
||||
)}
|
||||
</div>
|
||||
<button onClick={close} className="p-2 rounded-lg hover:bg-muted transition-colors" aria-label="Close cart">
|
||||
<X className="h-5 w-5 text-foreground" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto px-6 py-4">
|
||||
{items.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-16 text-center">
|
||||
<ShoppingCart className="h-12 w-12 text-muted-foreground/40 mb-4" />
|
||||
<p className="text-muted-foreground text-sm mb-4">Your cart is empty.</p>
|
||||
<button onClick={close} className="text-sm text-primary hover:underline">Continue shopping</button>
|
||||
</div>
|
||||
) : (
|
||||
<ul className="space-y-4">
|
||||
{items.map(item => (
|
||||
<li key={item.variantId} className="flex gap-3">
|
||||
<div className="w-16 h-16 rounded-lg bg-muted overflow-hidden flex-shrink-0">
|
||||
{item.thumbnail ? (
|
||||
<img src={item.thumbnail} alt={item.title} className="w-full h-full object-cover" />
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<Package className="h-6 w-6 text-muted-foreground/40" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-foreground line-clamp-1">{item.title}</p>
|
||||
{item.variant_title && item.variant_title !== 'Default' && (
|
||||
<p className="text-xs text-muted-foreground">{item.variant_title}</p>
|
||||
)}
|
||||
<p className="text-sm font-bold text-primary mt-0.5">{fmt(item.unit_price)}</p>
|
||||
<div className="flex items-center gap-1 mt-1.5">
|
||||
<button onClick={() => updateQty(item.variantId, item.quantity - 1)} className="w-6 h-6 rounded border border-border flex items-center justify-center hover:bg-muted transition-colors">
|
||||
<Minus className="h-3 w-3" />
|
||||
</button>
|
||||
<span className="w-8 text-center text-sm font-medium">{item.quantity}</span>
|
||||
<button onClick={() => updateQty(item.variantId, item.quantity + 1)} className="w-6 h-6 rounded border border-border flex items-center justify-center hover:bg-muted transition-colors">
|
||||
<Plus className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button onClick={() => removeFromCart(item.variantId)} className="p-1 text-muted-foreground hover:text-destructive transition-colors flex-shrink-0" aria-label="Remove item">
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{items.length > 0 && (
|
||||
<div className="px-6 py-4 border-t border-border bg-muted/30 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-muted-foreground">Subtotal</span>
|
||||
<span className="text-lg font-bold text-foreground">{fmt(subtotal)}</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">Shipping calculated at checkout</p>
|
||||
<Link href="/checkout" onClick={close}
|
||||
className="flex items-center justify-center gap-2 w-full bg-primary text-primary-foreground py-3 rounded-lg hover:bg-primary/90 transition-colors font-semibold text-sm">
|
||||
Checkout <ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
<Link href="/cart" onClick={close}
|
||||
className="block text-center text-sm text-muted-foreground hover:text-foreground transition-colors">
|
||||
View full cart
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user