chore: add client/src/components/layout/CartDrawer.tsx
This commit is contained in:
parent
ab05edf3ea
commit
eee8e16ca0
1 changed files with 90 additions and 0 deletions
90
client/src/components/layout/CartDrawer.tsx
Normal file
90
client/src/components/layout/CartDrawer.tsx
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { X, Trash2, ShoppingBag } from "lucide-react";
|
||||||
|
import { Link } from "wouter";
|
||||||
|
import { useCart } from "../../hooks/use-cart";
|
||||||
|
|
||||||
|
export default function CartDrawer() {
|
||||||
|
const { items, isOpen, closeCart, updateQuantity, removeItem, subtotal } = useCart();
|
||||||
|
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 bg-black/40 z-40"
|
||||||
|
onClick={closeCart}
|
||||||
|
aria-hidden
|
||||||
|
/>
|
||||||
|
<div className="fixed right-0 top-0 h-full w-full max-w-sm bg-background shadow-xl z-50 flex flex-col" data-testid="cart-drawer">
|
||||||
|
<div className="flex items-center justify-between p-4 border-b border-border">
|
||||||
|
<h2 className="font-semibold text-lg">Your Cart</h2>
|
||||||
|
<button onClick={closeCart} className="p-1 text-muted-foreground hover:text-foreground" data-testid="button-close-cart">
|
||||||
|
<X size={20} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{items.length === 0 ? (
|
||||||
|
<div className="flex-1 flex flex-col items-center justify-center gap-4 p-8 text-center">
|
||||||
|
<ShoppingBag size={40} className="text-muted-foreground/40" />
|
||||||
|
<p className="text-muted-foreground">Your cart is empty</p>
|
||||||
|
<button onClick={closeCart} className="text-sm text-primary hover:underline">Continue shopping</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="flex-1 overflow-y-auto p-4 flex flex-col gap-4">
|
||||||
|
{items.map((item) => (
|
||||||
|
<div key={item.id} className="flex gap-3" data-testid={`cart-item-${item.id}`}>
|
||||||
|
{item.imageUrl && (
|
||||||
|
<img src={item.imageUrl} alt={item.title} className="w-16 h-16 object-cover rounded-md border border-border flex-shrink-0" />
|
||||||
|
)}
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<p className="font-medium text-sm truncate">{item.title}</p>
|
||||||
|
{item.variantTitle && item.variantTitle !== "Default Title" && (
|
||||||
|
<p className="text-xs text-muted-foreground">{item.variantTitle}</p>
|
||||||
|
)}
|
||||||
|
<p className="text-sm font-medium mt-1">${parseFloat(item.price).toFixed(2)}</p>
|
||||||
|
<div className="flex items-center gap-2 mt-2">
|
||||||
|
<button
|
||||||
|
className="w-6 h-6 rounded border border-border text-sm hover:bg-muted flex items-center justify-center"
|
||||||
|
onClick={() => updateQuantity(item.id, item.quantity - 1)}
|
||||||
|
data-testid={`cart-item-decrease-${item.id}`}
|
||||||
|
>−</button>
|
||||||
|
<span className="text-sm w-4 text-center" data-testid={`cart-item-qty-${item.id}`}>{item.quantity}</span>
|
||||||
|
<button
|
||||||
|
className="w-6 h-6 rounded border border-border text-sm hover:bg-muted flex items-center justify-center"
|
||||||
|
onClick={() => updateQuantity(item.id, item.quantity + 1)}
|
||||||
|
data-testid={`cart-item-increase-${item.id}`}
|
||||||
|
>+</button>
|
||||||
|
<button
|
||||||
|
className="ml-auto text-muted-foreground hover:text-destructive"
|
||||||
|
onClick={() => removeItem(item.id)}
|
||||||
|
data-testid={`cart-item-remove-${item.id}`}
|
||||||
|
>
|
||||||
|
<Trash2 size={14} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-t border-border p-4 space-y-3">
|
||||||
|
<div className="flex justify-between text-sm">
|
||||||
|
<span className="text-muted-foreground">Subtotal</span>
|
||||||
|
<span className="font-semibold" data-testid="cart-subtotal">${subtotal.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">Taxes and shipping calculated at checkout</p>
|
||||||
|
<Link
|
||||||
|
href="/checkout"
|
||||||
|
onClick={closeCart}
|
||||||
|
className="w-full bg-primary text-primary-foreground py-3 rounded-md text-sm font-semibold text-center block hover:bg-primary/90 transition-colors"
|
||||||
|
data-testid="link-checkout"
|
||||||
|
>
|
||||||
|
Checkout · ${subtotal.toFixed(2)}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue