180 lines
6.7 KiB
TypeScript
180 lines
6.7 KiB
TypeScript
'use client';
|
|
|
|
import { container } from '@/lib/utils';
|
|
import { useCart } from 'components/cart/cart-context';
|
|
import { useTransition } from 'react';
|
|
import { removeItem, updateItemQuantity } from './actions';
|
|
import { CartDiscountForm } from './CartDiscountForm';
|
|
import { CartSummary } from './CartSummary';
|
|
import { EmptyCartMessage } from './EmptyCartMessage';
|
|
import { useCartProcessing } from './hooks/useCartProcessing';
|
|
import { BoxesSection } from './sections/BoxesSection';
|
|
import { CartHeader } from './sections/CartHeader';
|
|
import { CartLoading } from './sections/CartLoading';
|
|
import { OrderNotes } from './sections/OrderNotes';
|
|
import { ProductsSection } from './sections/ProductsSection';
|
|
|
|
export default function CartPage() {
|
|
const { cart, updateCartItem } = useCart();
|
|
const [isPending, startTransition] = useTransition();
|
|
const { boxes, standaloneProducts, isGroupingComplete, didInitialProcess } = useCartProcessing(cart);
|
|
|
|
if (!cart?.lines.length) {
|
|
return <EmptyCartMessage />;
|
|
}
|
|
|
|
// Show loading state while processing cart items
|
|
if (!didInitialProcess || !isGroupingComplete) {
|
|
return <CartLoading />;
|
|
}
|
|
|
|
const handleUpdateCartItem = (merchandiseId: string, updateType: 'plus' | 'minus' | 'delete', itemId?: string) => {
|
|
// First update the client-side cart state for immediate feedback
|
|
updateCartItem(merchandiseId, updateType, itemId);
|
|
|
|
// Update lastBoxState in localStorage if this is a box item
|
|
try {
|
|
const boxStateString = localStorage.getItem('lastBoxState');
|
|
if (boxStateString) {
|
|
const boxState = JSON.parse(boxStateString);
|
|
|
|
// Check if this item belongs to a box
|
|
const cartItem = cart.lines.find(item => {
|
|
if (itemId && item.id) {
|
|
return item.id === itemId;
|
|
}
|
|
return item.merchandise.id === merchandiseId;
|
|
});
|
|
|
|
if (cartItem) {
|
|
// Check if it's a box item by looking at attributes
|
|
const attrs = cartItem.attributes || [];
|
|
const boxType = attrs.find(attr => attr.key === '_box_type')?.value;
|
|
const boxGroupId = attrs.find(attr => attr.key === '_box_group_id')?.value;
|
|
|
|
if (boxType && boxGroupId) {
|
|
// Find the corresponding item in the stored state
|
|
if (boxType === 'item' && boxState.productItems) {
|
|
if (updateType === 'delete') {
|
|
// Remove the item from productItems
|
|
boxState.productItems = boxState.productItems.filter((item: { id: string, variantId: string }) =>
|
|
item.id !== cartItem.merchandise.product.id ||
|
|
item.variantId !== cartItem.merchandise.id
|
|
);
|
|
} else {
|
|
// Update quantity
|
|
const productItem = boxState.productItems.find((item: { id: string, variantId: string }) =>
|
|
item.id === cartItem.merchandise.product.id &&
|
|
item.variantId === cartItem.merchandise.id
|
|
);
|
|
|
|
if (productItem) {
|
|
const newQuantity = updateType === 'plus'
|
|
? cartItem.quantity + 1
|
|
: Math.max(1, cartItem.quantity - 1);
|
|
productItem.quantity = newQuantity;
|
|
}
|
|
}
|
|
|
|
// Save updated state back to localStorage
|
|
localStorage.setItem('lastBoxState', JSON.stringify(boxState));
|
|
}
|
|
else if (boxType === 'container' && boxState.boxItems) {
|
|
if (updateType === 'delete') {
|
|
// Remove the box
|
|
boxState.boxItems = boxState.boxItems.filter((item: { id: string }) =>
|
|
item.id !== cartItem.merchandise.product.id
|
|
);
|
|
} else {
|
|
// Update quantity
|
|
const boxItem = boxState.boxItems.find((item: { id: string }) =>
|
|
item.id === cartItem.merchandise.product.id
|
|
);
|
|
|
|
if (boxItem) {
|
|
const newQuantity = updateType === 'plus'
|
|
? cartItem.quantity + 1
|
|
: Math.max(1, cartItem.quantity - 1);
|
|
boxItem.quantity = newQuantity;
|
|
}
|
|
}
|
|
|
|
// Save updated state back to localStorage
|
|
localStorage.setItem('lastBoxState', JSON.stringify(boxState));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error updating box state in localStorage', error);
|
|
}
|
|
|
|
// Then update the server-side cart
|
|
startTransition(() => {
|
|
if (updateType === 'delete') {
|
|
// Call server action to remove item
|
|
removeItem({}, merchandiseId, itemId);
|
|
} else {
|
|
// Find the specific item to update, using both merchandise ID and item ID if provided
|
|
let item = cart.lines.find((item: { id?: string, merchandise: { id: string } }) => {
|
|
if (itemId && item.id) {
|
|
// If we have item ID, use it for more specific matching
|
|
return item.id === itemId;
|
|
}
|
|
// Fall back to merchandise ID only
|
|
return item.merchandise.id === merchandiseId;
|
|
});
|
|
|
|
if (item) {
|
|
// Calculate new quantity based on the updateType
|
|
const newQuantity = updateType === 'plus'
|
|
? item.quantity + 1
|
|
: Math.max(1, item.quantity - 1);
|
|
|
|
// Call server action to update quantity
|
|
updateItemQuantity({}, { merchandiseId, quantity: newQuantity, itemId });
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className={container}>
|
|
<div className="pb-20">
|
|
<div className="flex flex-col lg:flex-row lg:justify-between gap-8">
|
|
{/* Left Side: Cart Items */}
|
|
<div className="lg:w-[62%]">
|
|
<CartHeader totalQuantity={cart.totalQuantity} />
|
|
|
|
{/* Boxes Section */}
|
|
<BoxesSection
|
|
boxes={boxes}
|
|
onUpdate={handleUpdateCartItem}
|
|
isPending={isPending}
|
|
/>
|
|
|
|
{/* Standalone Products Section */}
|
|
<ProductsSection
|
|
products={standaloneProducts}
|
|
onUpdate={handleUpdateCartItem}
|
|
isPending={isPending}
|
|
/>
|
|
|
|
{/* Order Notes */}
|
|
<OrderNotes />
|
|
|
|
{/* Discount Form */}
|
|
<CartDiscountForm />
|
|
</div>
|
|
|
|
{/* Right Side: Order Summary */}
|
|
<div className="lg:w-[30%]">
|
|
<div className="mt-6 lg:mt-[72px]">
|
|
<CartSummary cart={cart} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|