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
|
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;
};
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);
// 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,
});
// 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);
// Update cookie back
updateSelectedItemInCookie(item.id, !newSelectedState);
// 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 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;
|