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