summaryrefslogtreecommitdiff
path: root/src-migrate/modules/cart/components/ItemSelect.tsx
diff options
context:
space:
mode:
authorIT Fixcomart <it@fixcomart.co.id>2025-05-31 02:33:14 +0000
committerIT Fixcomart <it@fixcomart.co.id>2025-05-31 02:33:14 +0000
commit2a1dea70b8f0062fe8eebeb7139a7b77a24e220b (patch)
treee7a5db13b2655cbdfb1c81859e240652fdc87bbb /src-migrate/modules/cart/components/ItemSelect.tsx
parent0b2e31247d4fe7eb1432079979478a0cfc38d049 (diff)
parent2732c04b36f98a25895826b28003b1e2c56ad952 (diff)
Merged in fix_responsive_cart (pull request #413)
<miqdad> Fix unresponsive cart
Diffstat (limited to 'src-migrate/modules/cart/components/ItemSelect.tsx')
-rw-r--r--src-migrate/modules/cart/components/ItemSelect.tsx172
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;