/** * @page Full Shopping Cart (`app/cart/page.tsx`) * @purpose Customer full cart page with quantity modification, line totals, * free-shipping progress, promo input, and checkout CTA. */ 'use client'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { useCartStore } from '@/store/cartStore'; import { formatCurrency, getImageUrl } from '@/lib/utils'; import { requireLoginForCheckout } from '@/lib/requireLoginForCheckout'; import { Header } from '@/layout/Header'; import { Footer } from '@/layout/Footer'; import { StickyHeaderSpacer } from '@/components/StickyHeaderSpacer'; import BlurHashImage from '@/components/ui/BlurHashImage'; import { Trash2, Plus, Minus, ArrowRight, ShoppingBag, Truck, Tag, } from 'lucide-react'; const FREE_SHIPPING_THRESHOLD = 999; function resolveItemImage(item: { product: { images?: { image_url?: string }[]; name?: string }; selectedVariant: { images?: { image_url?: string }[] }; }): string { const fromVariant = item.selectedVariant?.images?.[0]?.image_url; const fromProduct = item.product?.images?.[0]?.image_url; return getImageUrl(fromVariant || fromProduct || ''); } export default function CartPage() { const router = useRouter(); const cart = useCartStore((state) => state.cart); const removeFromCart = useCartStore((state) => state.removeFromCart); const updateQuantity = useCartStore((state) => state.updateQuantity); const getCartTotal = useCartStore((state) => state.getCartTotal); const subtotal = getCartTotal(); const shipping = subtotal > 0 && subtotal < FREE_SHIPPING_THRESHOLD ? 15.0 : 0; const total = subtotal + shipping; const progressPercent = Math.min(100, (subtotal / FREE_SHIPPING_THRESHOLD) * 100); const amountNeeded = Math.max(0, FREE_SHIPPING_THRESHOLD - subtotal); return (
{cart.length > 0 && (
Continue shopping
)} {cart.length === 0 ? (

Your cart is empty

Looks like you haven't added any products yet.

Explore products
) : (
{/* Items */}
Product Subtotal
{cart.map((item) => { const img = resolveItemImage(item); const priceNum = typeof item.selectedVariant.price === 'number' ? item.selectedVariant.price : parseFloat(String(item.selectedVariant.price || 0)); const lineTotal = priceNum * item.quantity; const atMax = typeof item.selectedVariant.available_stock === 'number' && item.quantity >= item.selectedVariant.available_stock; return (
{img ? ( ) : (
)}
{item.product.name} SKU: {item.selectedVariant.sku} {formatCurrency(item.selectedVariant.price)}
{item.quantity}
{formatCurrency(lineTotal)}
); })}
{/* Summary */}

Order summary

{/* Free shipping progress */}

{shipping === 0 ? ( You've unlocked free shipping! ) : ( <> Add{' '} {formatCurrency(amountNeeded)} {' '} more for free shipping )}

Subtotal {formatCurrency(subtotal)}
Shipping {shipping === 0 ? ( Free ) : ( {formatCurrency(shipping)} )}
Total {formatCurrency(total)}

Taxes calculated at checkout

)}
); }