guardia-mall/frontend/src/shop/Cart.tsx

68 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useQuery, useQueryClient } from '@tanstack/react-query'
import { Link, useNavigate } from 'react-router-dom'
import { Trash2, Flower2, ShoppingCart } from 'lucide-react'
import { getCart, updateCartItem, removeCartItem } from '../api/client'
import { money, useShop } from '../store/shop'
export default function Cart() {
const qc = useQueryClient()
const nav = useNavigate()
const { custToken, setCartCount } = useShop()
const { data: items } = useQuery({ queryKey: ['cart'], queryFn: getCart, enabled: !!custToken })
const refresh = () => qc.invalidateQueries({ queryKey: ['cart'] })
const setQty = async (id: number, q: number) => { if (q < 1) return; await updateCartItem(id, q); refresh() }
const remove = async (id: number) => { await removeCartItem(id); refresh(); setCartCount((c: number) => Math.max(0, c - 1)) }
if (!custToken) return (
<div className="max-w-3xl mx-auto px-4 py-20 text-center">
<ShoppingCart className="mx-auto text-bloom/40 mb-3" size={48} />
<p className="text-gray-500 mb-4">Please sign in to view your cart.</p>
<Link to="/account" className="bg-bloom text-white px-6 py-2.5 rounded-full font-semibold">Sign In / Register</Link>
</div>
)
const list = items || []
const subtotal = list.reduce((s: number, i: any) => s + (i.price || (i.unitPrice || 0) * (i.quantity || 1)), 0)
return (
<div className="max-w-4xl mx-auto px-4 py-8">
<h1 className="font-serif text-2xl font-bold mb-6">Cart</h1>
{!list.length ? (
<div className="text-center text-gray-400 py-16">Your cart is empty. <Link to="/category" className="text-bloom">Start Shopping </Link></div>
) : (
<div className="grid md:grid-cols-3 gap-6">
<div className="md:col-span-2 space-y-3">
{list.map((i: any) => (
<div key={i.id} className="bg-white rounded-2xl border border-blush-100/60 p-4 flex gap-4">
<div className="w-20 h-20 bg-petal rounded-xl flex items-center justify-center overflow-hidden shrink-0">
{i.thumbnail ? <img src={i.thumbnail} className="w-full h-full object-cover" /> : <Flower2 className="text-bloom/30" size={32} />}
</div>
<div className="flex-1">
<div className="font-medium text-sm">{i.productName || `Product #${i.productId}`}</div>
<div className="text-xs text-gray-500">{i.sizeCode}{i.cardMessage ? ` · Card: ${i.cardMessage.slice(0, 20)}` : ''}</div>
<div className="flex items-center gap-3 mt-2">
<div className="flex items-center border border-blush-100 rounded-full text-sm">
<button onClick={() => setQty(i.id, (i.quantity || 1) - 1)} className="px-2.5 py-1 text-bloom2"></button>
<span className="px-1 w-6 text-center">{i.quantity || 1}</span>
<button onClick={() => setQty(i.id, (i.quantity || 1) + 1)} className="px-2.5 py-1 text-bloom2">+</button>
</div>
<button onClick={() => remove(i.id)} className="text-blush-400 hover:text-blush-600"><Trash2 size={16} /></button>
</div>
</div>
<div className="font-bold text-bloom2 text-sm">{money(i.price || (i.unitPrice || 0) * (i.quantity || 1))}</div>
</div>
))}
</div>
<div className="bg-white rounded-2xl border border-blush-100/60 p-5 h-fit">
<div className="flex justify-between text-sm mb-2"><span className="text-gray-500">Subtotal</span><span className="font-medium">{money(subtotal)}</span></div>
<div className="flex justify-between text-sm mb-3"><span className="text-gray-500">Shipping/Tax</span><span className="text-gray-400">Calculated at checkout</span></div>
<div className="border-t border-blush-100/60 pt-3 flex justify-between font-bold"><span>Total</span><span className="text-bloom2">{money(subtotal)}</span></div>
<button onClick={() => nav('/checkout')} className="w-full mt-4 bg-bloom text-white font-semibold py-3 rounded-full hover:bg-bloom2">Checkout</button>
</div>
</div>
)}
</div>
)
}