/**
* @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
)}
);
}