summaryrefslogtreecommitdiff
path: root/src-migrate/modules/cart/components/ItemSelect.tsx
blob: 00c7be430b62ec5363b5e9ecdfb8a0473bf18544 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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;
};

const CartItemSelect = ({ item }: Props) => {
  const auth = getAuth();
  const { updateCartItem, cart, loadCart } = useCartStore();
  const [isUpdating, setIsUpdating] = useState<boolean>(false);
  const [localSelected, setLocalSelected] = useState<boolean>(item.selected);
  const [isGlobalUpdating, setIsGlobalUpdating] = useState<boolean>(false);

  // Subscribe to global checkbox update state
  useEffect(() => {
    const handleUpdateStateChange = (isUpdating) => {
      setIsGlobalUpdating(isUpdating);
    };

    checkboxUpdateState.addListener(handleUpdateStateChange);

    return () => {
      checkboxUpdateState.removeListener(handleUpdateStateChange);
    };
  }, []);

  // 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 state to cookie for future
      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 for responsiveness
      setLocalSelected(newSelectedState);
      setIsUpdating(true);

      // Start the update - notify global state with this checkbox's ID
      checkboxUpdateState.startUpdate(item.id);

      try {
        // The cookie update is now handled inside the function with notification
        updateSelectedItemInCookie(item.id, newSelectedState, false); // We already started above

        // 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,
          // purchase_tax_id: item.purchase_tax_id || null, // Ensure null for numeric fields
        });

        // 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');

        // Revert local state on error
        setLocalSelected(!newSelectedState);

        // Revert cookie change
        updateSelectedItemInCookie(item.id, !newSelectedState, false);

        // Reload cart to get server state
        loadCart(auth.id);
      } finally {
        setIsUpdating(false);

        // End the update - notify global state with this checkbox's ID
        checkboxUpdateState.endUpdate(item.id);
      }
    },
    [auth, cart, item, isUpdating, updateCartItem, loadCart]
  );

  // Determine if THIS specific checkbox should be disabled - only disable
  // if this specific checkbox is updating
  const isDisabled =
    isUpdating || checkboxUpdateState.isCheckboxUpdating(item.id);

  return (
    <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;