diff options
Diffstat (limited to 'src-migrate/modules/cart/components/ItemSelect.tsx')
| -rw-r--r-- | src-migrate/modules/cart/components/ItemSelect.tsx | 172 |
1 files changed, 128 insertions, 44 deletions
diff --git a/src-migrate/modules/cart/components/ItemSelect.tsx b/src-migrate/modules/cart/components/ItemSelect.tsx index d4a1b537..72ab49aa 100644 --- a/src-migrate/modules/cart/components/ItemSelect.tsx +++ b/src-migrate/modules/cart/components/ItemSelect.tsx @@ -1,56 +1,140 @@ -import { Checkbox, Spinner } from '@chakra-ui/react' -import React, { useState } from 'react' - -import { getAuth } from '~/libs/auth' -import { CartItem } from '~/types/cart' -import { upsertUserCart } from '~/services/cart' - -import { useCartStore } from '../stores/useCartStore' +import { Checkbox } from '@chakra-ui/react'; +import React, { useState, useCallback, useEffect } from 'react'; +import { getAuth } from '~/libs/auth'; +import { CartItem } from '~/types/cart'; +import { upsertUserCart } from '~/services/cart'; +import { useCartStore } from '../stores/useCartStore'; +import { toast } from 'react-hot-toast'; +import { + getSelectedItemsFromCookie, + updateSelectedItemInCookie, + checkboxUpdateState, +} from '~/utils/cart'; type Props = { - item: CartItem -} + item: CartItem; +}; const CartItemSelect = ({ item }: Props) => { - const auth = getAuth() - const { updateCartItem, cart } = useCartStore() + const auth = getAuth(); + const { updateCartItem, cart, loadCart } = useCartStore(); + const [isUpdating, setIsUpdating] = useState<boolean>(false); + const [localSelected, setLocalSelected] = useState<boolean>(item.selected); + + // Subscribe to global checkbox update state + useEffect(() => { + const handleUpdateStateChange = (isUpdating) => { + // This component doesn't need to react to global state changes + // Individual checkboxes are managed independently + }; + + checkboxUpdateState.addListener(handleUpdateStateChange); + return () => checkboxUpdateState.removeListener(handleUpdateStateChange); + }, []); + + // Sync local state with cookie and server data + useEffect(() => { + if (isUpdating) return; + + const selectedItems = getSelectedItemsFromCookie(); + const storedState = selectedItems[item.id]; + + if (storedState !== undefined) { + // Update local state if cookie differs + if (localSelected !== storedState) { + setLocalSelected(storedState); + } + + // Sync cart state with cookie if needed + if (storedState !== item.selected && cart) { + const updatedCartItems = cart.products.map((cartItem) => + cartItem.id === item.id + ? { ...cartItem, selected: storedState } + : cartItem + ); + updateCartItem({ ...cart, products: updatedCartItems }); + } + } else { + // Initialize cookie with server state + setLocalSelected(item.selected); + updateSelectedItemInCookie(item.id, item.selected, false); + } + }, [item.id, item.selected, localSelected, cart, updateCartItem, isUpdating]); + + const handleChange = useCallback( + async (e: React.ChangeEvent<HTMLInputElement>) => { + if (typeof auth !== 'object' || !cart || isUpdating) return; + + const newSelectedState = e.target.checked; + + // Update local state immediately + setLocalSelected(newSelectedState); + setIsUpdating(true); + checkboxUpdateState.startUpdate(item.id); + + try { + // Update cookie immediately for responsive UI + updateSelectedItemInCookie(item.id, newSelectedState, false); + + // Update cart state optimistically + const updatedCartItems = cart.products.map((cartItem) => + cartItem.id === item.id + ? { ...cartItem, selected: newSelectedState } + : cartItem + ); + updateCartItem({ ...cart, products: updatedCartItems }); - const [isLoad, setIsLoad] = useState<boolean>(false) + // Send update to server + await upsertUserCart({ + userId: auth.id, + type: item.cart_type, + id: item.id, + qty: item.quantity, + selected: newSelectedState, + // purchase_tax_id: item.purchase_tax_id, + // vendor_id: item.vendor_id + }); - const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => { - if (typeof auth !== 'object' || !cart) return - - setIsLoad(true); - const updatedCartItems = cart.products.map(cartItem => - cartItem.id === item.id - ? { ...cartItem, selected: e.target.checked } - : cartItem - ); + // Reload cart for consistency + await loadCart(auth.id); + } catch (error) { + console.error('Failed to update item selection:', error); + toast.error('Gagal memperbarui pilihan barang'); - // Update the entire cart - const updatedCart = { ...cart, products: updatedCartItems }; - updateCartItem(updatedCart); + // Revert changes on error + setLocalSelected(!newSelectedState); + updateSelectedItemInCookie(item.id, !newSelectedState, false); + loadCart(auth.id); + } finally { + setIsUpdating(false); + checkboxUpdateState.endUpdate(item.id); + } + }, + [auth, cart, item, isUpdating, updateCartItem, loadCart] + ); - setIsLoad(false); - } + const isDisabled = + isUpdating || checkboxUpdateState.isCheckboxUpdating(item.id); return ( - <div className='w-6 my-auto'> - {isLoad && ( - <Spinner className='my-auto' size='sm' /> - )} - - {!isLoad && ( - <Checkbox - borderColor='gray.600' - colorScheme='red' - size='lg' - isChecked={item.selected} - onChange={handleChange} - /> - )} + <div className='w-6 my-auto relative'> + <Checkbox + borderColor='gray.600' + colorScheme='red' + size='lg' + isChecked={localSelected} + onChange={handleChange} + isDisabled={isDisabled} + opacity={isDisabled ? 0.5 : 1} + cursor={isDisabled ? 'not-allowed' : 'pointer'} + _disabled={{ + opacity: 0.5, + cursor: 'not-allowed', + backgroundColor: 'gray.100', + }} + /> </div> - ) -} + ); +}; -export default CartItemSelect
\ No newline at end of file +export default CartItemSelect; |
