import style from './cart.module.css'; import React, { useEffect, useMemo, useState } from 'react'; import Link from 'next/link'; import { Button, Checkbox, Spinner, Tooltip } from '@chakra-ui/react'; import { toast } from 'react-hot-toast'; import { useRouter } from 'next/router'; import { getAuth } from '~/libs/auth'; import { useCartStore } from '~/modules/cart/stores/useCartStore'; import CartItemModule from '~/modules/cart/components/Item'; import CartSummary from '~/modules/cart/components/Summary'; import clsxm from '~/libs/clsxm'; import useDevice from '@/core/hooks/useDevice'; import CartSummaryMobile from '~/modules/cart/components/CartSummaryMobile'; import Image from '~/components/ui/image'; import { deleteUserCart, upsertUserCart } from '~/services/cart'; import { Trash2Icon } from 'lucide-react'; import { useProductCartContext } from '@/contexts/ProductCartContext'; import { getSelectedItemsFromCookie, syncSelectedItemsWithCookie, setAllSelectedInCookie, removeSelectedItemsFromCookie, removeCartItemsFromCookie, checkboxUpdateState, quantityUpdateState, } from '~/utils/cart'; const SELECT_ALL_ID = 'select_all_checkbox'; const CartPage: React.FC = () => { const router = useRouter(); const auth = getAuth(); const [isStepApproval, setIsStepApproval] = useState(false); const [isLoadDelete, setIsLoadDelete] = useState(false); const { loadCart, cart, summary, updateCartItem } = useCartStore(); const device = useDevice(); const { setRefreshCart } = useProductCartContext(); const [isTop, setIsTop] = useState(true); const [isUpdating, setIsUpdating] = useState(false); const [isAnyCheckboxUpdating, setIsAnyCheckboxUpdating] = useState(false); const [isAnyQuantityUpdating, setIsAnyQuantityUpdating] = useState(false); // Subscribe to update state changes useEffect(() => { const handleCheckboxUpdate = (isUpdating: boolean): void => setIsAnyCheckboxUpdating(isUpdating); const handleQuantityUpdate = (isUpdating: boolean): void => setIsAnyQuantityUpdating(isUpdating); checkboxUpdateState.addListener(handleCheckboxUpdate); quantityUpdateState.addListener(handleQuantityUpdate); return () => { checkboxUpdateState.removeListener(handleCheckboxUpdate); quantityUpdateState.removeListener(handleQuantityUpdate); }; }, []); // Handle scroll for sticky header styling useEffect(() => { const handleScroll = (): void => setIsTop(window.scrollY < 200); window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, []); // Initialize cart and sync with cookies useEffect(() => { const initializeCart = async (): Promise => { if (typeof auth === 'object' && !cart) { await loadCart(auth.id); setIsStepApproval(auth?.feature?.soApproval); } }; initializeCart(); }, [auth, cart, loadCart]); // Separate effect to sync with cookies after cart is loaded useEffect(() => { if (cart?.products) { const { items, needsUpdate } = syncSelectedItemsWithCookie(cart.products); const typedItems = items as Record; if (needsUpdate) { const updatedCart = { ...cart, products: cart.products.map((item) => ({ ...item, selected: typedItems[item.id] !== undefined ? typedItems[item.id] : item.selected, })), }; updateCartItem(updatedCart); } } }, [cart, updateCartItem]); // Computed values const hasSelectedPromo = useMemo((): boolean => { return ( cart?.products?.some( (item) => item.cart_type === 'promotion' && item.selected ) || false ); }, [cart]); const hasSelected = useMemo((): boolean => { return cart?.products?.some((item) => item.selected) || false; }, [cart]); const hasSelectNoPrice = useMemo((): boolean => { return ( cart?.products?.some( (item) => item.selected && item.price.price_discount === 0 ) || false ); }, [cart]); const hasSelectedAll = useMemo((): boolean => { if (!cart?.products?.length) return false; return cart.products.every((item) => item.selected); }, [cart]); // Button states const areButtonsDisabled: boolean = isUpdating || isLoadDelete || isAnyCheckboxUpdating || isAnyQuantityUpdating; const isSelectAllDisabled: boolean = isUpdating || checkboxUpdateState.isCheckboxUpdating(); // Handlers const handleCheckout = (): void => { if (areButtonsDisabled) { toast.error('Harap tunggu pembaruan selesai'); return; } router.push('/shop/checkout'); }; const handleQuotation = (): void => { if (areButtonsDisabled) { toast.error('Harap tunggu pembaruan selesai'); return; } if (hasSelectedPromo || !hasSelected) { toast.error('Maaf, Barang promo tidak dapat dibuat quotation'); } else { router.push('/shop/quotation'); } }; const handleSelectAll = async ( e: React.ChangeEvent ): Promise => { if (!cart || isUpdating || typeof auth !== 'object') return; const newSelectedState = !hasSelectedAll; setIsUpdating(true); checkboxUpdateState.startUpdate(); try { // Update UI immediately const updatedCart = { ...cart, products: cart.products.map((item) => ({ ...item, selected: newSelectedState, })), }; updateCartItem(updatedCart); // Update cookies const productIds = cart.products.map((item) => item.id); setAllSelectedInCookie(productIds, newSelectedState, false); // Update server const updatePromises = cart.products.map((item) => upsertUserCart({ userId: auth.id, type: item.cart_type, id: item.id, qty: item.quantity, selected: newSelectedState, }) ); await Promise.all(updatePromises); await loadCart(auth.id); } catch (error) { console.error('Error updating select all:', error); toast.error('Gagal memperbarui pilihan'); // Revert on error const revertedCart = { ...cart, products: cart.products.map((item) => ({ ...item, selected: !newSelectedState, })), }; updateCartItem(revertedCart); setAllSelectedInCookie( cart.products.map((item) => item.id), !newSelectedState, false ); } finally { setIsUpdating(false); checkboxUpdateState.endUpdate(); } }; const handleDelete = async (): Promise => { if (typeof auth !== 'object' || !cart) return; setIsLoadDelete(true); checkboxUpdateState.startUpdate(); try { const itemsToDelete = cart.products.filter((item) => item.selected); const itemIdsToDelete = itemsToDelete.map((item) => item.id); const cartIdsToDelete = itemsToDelete.map((item) => item.cart_id); // Delete from server for (const item of itemsToDelete) { await deleteUserCart(auth.id, [item.cart_id]); } // Update local state optimistically const updatedProducts = cart.products.filter((item) => !item.selected); const updatedCart = { ...cart, products: updatedProducts, product_total: updatedProducts.length, }; updateCartItem(updatedCart); // Clean up cookies removeSelectedItemsFromCookie(itemIdsToDelete); removeCartItemsFromCookie(cartIdsToDelete.map(String)); // Reload from server loadCart(auth.id).catch((error) => console.error('Error reloading cart:', error) ); setRefreshCart(true); toast.success('Item berhasil dihapus'); } catch (error) { console.error('Failed to delete cart items:', error); toast.error('Gagal menghapus item'); loadCart(auth.id); } finally { setIsLoadDelete(false); checkboxUpdateState.endUpdate(); } }; // Tooltip messages const getTooltipMessage = (): string => { if (isAnyQuantityUpdating) return 'Harap tunggu update quantity selesai'; if (isAnyCheckboxUpdating) return 'Harap tunggu pembaruan checkbox selesai'; if (isLoadDelete) return 'Harap tunggu penghapusan selesai'; if (isUpdating) return 'Harap tunggu pembaruan selesai'; return ''; }; const getQuotationTooltip = (): string => { const baseMessage = getTooltipMessage(); if (baseMessage) return baseMessage; if (hasSelectedPromo) return 'Barang promo tidak dapat dibuat quotation'; if (!hasSelected) return 'Tidak ada item yang dipilih'; return ''; }; const getCheckoutTooltip = (): string => { const baseMessage = getTooltipMessage(); if (baseMessage) return baseMessage; if (!hasSelected) return 'Tidak ada item yang dipilih'; if (hasSelectNoPrice) return 'Terdapat item yang tidak ada harga'; return ''; }; const getDeleteTooltip = (): string => { const baseMessage = getTooltipMessage(); if (baseMessage) return baseMessage; if (!hasSelected) return 'Tidak ada item yang dipilih'; return ''; }; return ( <> {/* Sticky Header */}

Keranjang Belanja

{hasSelectedAll ? 'Uncheck all' : 'Select all'}

{/* Main Content */}
{!cart && }
{cart?.products?.map((item) => ( ))} {cart?.products?.length === 0 && (
Empty Cart
Keranjangnya masih kosong nih
Yuk, tambahin barang-barang yang kamu mau ke keranjang sekarang!
Ada banyak potongan belanjanya pakai kode voucher
Mulai Belanja
)}
{/* Cart Summary */}
{device.isMobile ? ( ) : ( )}
{!isStepApproval && ( )}
); }; export default CartPage;