import style from '../styles/item-action.module.css'; import React, { useEffect, useState } from 'react'; import { Spinner, Tooltip } from '@chakra-ui/react'; import { MinusIcon, PlusIcon, Trash2Icon } from 'lucide-react'; import { CartItem } from '~/types/cart'; import { getAuth } from '~/libs/auth'; import { deleteUserCart, upsertUserCart } from '~/services/cart'; import { useDebounce } from 'usehooks-ts'; import { useCartStore } from '../stores/useCartStore'; import { useProductCartContext } from '@/contexts/ProductCartContext'; import { removeSelectedItemsFromCookie, removeCartItemsFromCookie, quantityUpdateState, getCartDataFromCookie, setCartDataToCookie, } from '~/utils/cart'; import { toast } from 'react-hot-toast'; interface Props { item: CartItem; } const CartItemAction: React.FC = ({ item }) => { const auth = getAuth(); const { setRefreshCart } = useProductCartContext(); const [isLoadDelete, setIsLoadDelete] = useState(false); const [isLoadQuantity, setIsLoadQuantity] = useState(false); const [quantity, setQuantity] = useState(item.quantity); const { loadCart, cart, updateCartItem } = useCartStore(); const limitQty: number = item.limit_qty?.transaction || 0; const handleDelete = async (): Promise => { if (typeof auth !== 'object') return; setIsLoadDelete(true); try { // Delete from server await deleteUserCart(auth.id, [item.cart_id]); // Clean up cookies immediately removeSelectedItemsFromCookie([item.id]); removeCartItemsFromCookie([item.cart_id]); // Update local cart state optimistically if (cart) { const updatedProducts = cart.products.filter( (product) => product.id !== item.id ); const updatedCart = { ...cart, products: updatedProducts, product_total: updatedProducts.length, }; updateCartItem(updatedCart); } // Reload from server and refresh context await loadCart(auth.id); setRefreshCart(true); toast.success('Item berhasil dihapus'); } catch (error) { console.error('Failed to delete cart item:', error); toast.error('Gagal menghapus item'); // Reload on error await loadCart(auth.id); } finally { setIsLoadDelete(false); } }; const updateQuantityInCookie = ( productId: number, cartId: string | number, newQuantity: number ): boolean => { try { const cartData = getCartDataFromCookie() as Record; let itemFound = false; const cartIdString = String(cartId); // Find item by cart_id key or search within objects if (cartData[cartIdString]) { cartData[cartIdString].quantity = newQuantity; itemFound = true; } else { // Search by product id or cart_id within objects Object.keys(cartData).forEach((key) => { const item = cartData[key]; if (item.id === productId || String(item.cart_id) === cartIdString) { item.quantity = newQuantity; itemFound = true; } }); } if (itemFound) { setCartDataToCookie(cartData); return true; } return false; } catch (error) { console.error('Error updating quantity in cookie:', error); return false; } }; const decreaseQty = (): void => { setQuantity((prevQuantity) => prevQuantity - 1); }; const increaseQty = (): void => { setQuantity((prevQuantity) => prevQuantity + 1); }; const debounceQty = useDebounce(quantity, 1000); useEffect(() => { if (isNaN(debounceQty)) setQuantity(1); if (limitQty > 0 && debounceQty > limitQty) setQuantity(limitQty); }, [debounceQty, limitQty]); useEffect(() => { const updateCart = async (): Promise => { if (typeof auth !== 'object' || isNaN(debounceQty)) return; if (debounceQty === item.quantity) return; quantityUpdateState.startUpdate(item.id); setIsLoadQuantity(true); try { // Update cookie immediately for responsive UI updateQuantityInCookie(item.id, item.cart_id, debounceQty); // Update local cart state optimistically if (cart) { const updatedProducts = cart.products.map((product) => product.id === item.id ? { ...product, quantity: debounceQty } : product ); const updatedCart = { ...cart, products: updatedProducts, }; updateCartItem(updatedCart); } // Send update to server await upsertUserCart({ userId: auth.id, type: item.cart_type, id: item.id, qty: debounceQty, selected: item.selected, }); // Reload from server to ensure consistency await loadCart(auth.id); // Re-update cookie if server reload overwrote it const currentCookieData = getCartDataFromCookie() as Record< string, any >; let needsReUpdate = false; Object.keys(currentCookieData).forEach((key) => { const cookieItem = currentCookieData[key]; if ( (cookieItem.id === item.id || String(cookieItem.cart_id) === String(item.cart_id)) && cookieItem.quantity !== debounceQty ) { needsReUpdate = true; } }); if (needsReUpdate) { updateQuantityInCookie(item.id, item.cart_id, debounceQty); } } catch (error) { console.error('Error updating quantity:', error); toast.error('Gagal mengupdate quantity'); // Revert changes on error updateQuantityInCookie(item.id, item.cart_id, item.quantity); loadCart(auth.id); } finally { setIsLoadQuantity(false); quantityUpdateState.endUpdate(item.id); } }; updateCart(); //eslint-disable-next-line react-hooks/exhaustive-deps }, [debounceQty]); const handleQuantityInputChange = ( e: React.ChangeEvent ): void => { const value = parseInt(e.target.value); setQuantity(isNaN(value) ? 1 : value); }; return (
{isLoadQuantity && (
)} 0 ? `Max. ${limitQty}` : ''}>
); }; export default CartItemAction;