diff options
| author | Miqdad <ahmadmiqdad27@gmail.com> | 2025-05-19 11:02:19 +0700 |
|---|---|---|
| committer | Miqdad <ahmadmiqdad27@gmail.com> | 2025-05-19 11:02:19 +0700 |
| commit | 7d4445bb9bad3d6c945503086a07bd882536e5f6 (patch) | |
| tree | a24e5b110887fd96ee7803c7857a254c3aeb9590 /src-migrate/modules/cart/components/ItemSelect.tsx | |
| parent | 746a11b810ae9e8a974a76d0548297cd0faff9b5 (diff) | |
<miqdad> fix unresponsive cart select
Diffstat (limited to 'src-migrate/modules/cart/components/ItemSelect.tsx')
| -rw-r--r-- | src-migrate/modules/cart/components/ItemSelect.tsx | 170 |
1 files changed, 126 insertions, 44 deletions
diff --git a/src-migrate/modules/cart/components/ItemSelect.tsx b/src-migrate/modules/cart/components/ItemSelect.tsx index d4a1b537..733ee64d 100644 --- a/src-migrate/modules/cart/components/ItemSelect.tsx +++ b/src-migrate/modules/cart/components/ItemSelect.tsx @@ -1,56 +1,138 @@ -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, +} 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); + + // Initialize local state from cookie or server + useEffect(() => { + if (isUpdating) return; // Skip if we're currently updating + + // Check cookie first + const selectedItems = getSelectedItemsFromCookie(); + const storedState = selectedItems[item.id]; + + if (storedState !== undefined) { + // Only update local state if it differs from current state + if (localSelected !== storedState) { + setLocalSelected(storedState); + } + + // If cookie state differs from server state and we're not in the middle of an update, + // synchronize the item state with cookie + if (storedState !== item.selected) { + // Update cart item silently to match cookie + if (cart) { + const updatedCartItems = cart.products.map((cartItem) => + cartItem.id === item.id + ? { ...cartItem, selected: storedState } + : cartItem + ); + + const updatedCart = { ...cart, products: updatedCartItems }; + updateCartItem(updatedCart); + } + } + } else { + // Fall back to server state if no cookie exists + setLocalSelected(item.selected); + + // Save this state to cookie for future use + updateSelectedItemInCookie(item.id, item.selected); + } + }, [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 for responsiveness + setLocalSelected(newSelectedState); + setIsUpdating(true); + + try { + // Update cookie immediately + updateSelectedItemInCookie(item.id, newSelectedState); + + // Update cart state immediately for UI responsiveness + const updatedCartItems = cart.products.map((cartItem) => + cartItem.id === item.id + ? { ...cartItem, selected: newSelectedState } + : cartItem + ); + + const updatedCart = { ...cart, products: updatedCartItems }; + updateCartItem(updatedCart); + + // Save to server + await upsertUserCart({ + userId: auth.id, + type: item.cart_type, + id: item.id, + qty: item.quantity, + selected: newSelectedState, + }); - const [isLoad, setIsLoad] = useState<boolean>(false) + // Reload cart to ensure consistency + await loadCart(auth.id); + } catch (error) { + console.error('Failed to update item selection:', error); + toast.error('Gagal memperbarui pilihan barang'); - 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 - ); + // Revert local state on error + setLocalSelected(!newSelectedState); - // Update the entire cart - const updatedCart = { ...cart, products: updatedCartItems }; - updateCartItem(updatedCart); + // Update cookie back + updateSelectedItemInCookie(item.id, !newSelectedState); - setIsLoad(false); - } + // Reload cart to get server state + loadCart(auth.id); + } finally { + setIsUpdating(false); + } + }, + [auth, cart, item, isUpdating, updateCartItem, loadCart] + ); 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={isUpdating} + opacity={isUpdating ? 0.5 : 1} + cursor={isUpdating ? '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; |
