From 4904573845478e7e9648735d008153728870a123 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 23 May 2025 09:37:46 +0700 Subject: fix cookie not updating when delete an item --- src-migrate/modules/cart/components/ItemAction.tsx | 139 ++++++++++++++------- 1 file changed, 93 insertions(+), 46 deletions(-) (limited to 'src-migrate/modules/cart/components/ItemAction.tsx') diff --git a/src-migrate/modules/cart/components/ItemAction.tsx b/src-migrate/modules/cart/components/ItemAction.tsx index 7220e362..eea0cbe9 100644 --- a/src-migrate/modules/cart/components/ItemAction.tsx +++ b/src-migrate/modules/cart/components/ItemAction.tsx @@ -1,74 +1,121 @@ -import style from '../styles/item-action.module.css' +import style from '../styles/item-action.module.css'; -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState } from 'react'; -import { Spinner, Tooltip } from '@chakra-ui/react' -import { MinusIcon, PlusIcon, Trash2Icon } from 'lucide-react' +import { Spinner, Tooltip } from '@chakra-ui/react'; +import { MinusIcon, PlusIcon, Trash2Icon } from 'lucide-react'; -import { CartItem } from '~/types/cart' -import { getAuth } from '~/libs/auth' -import { deleteUserCart, upsertUserCart } from '~/services/cart' +import { CartItem } from '~/types/cart'; +import { getAuth } from '~/libs/auth'; +import { deleteUserCart, upsertUserCart } from '~/services/cart'; -import { useDebounce } from 'usehooks-ts' -import { useCartStore } from '../stores/useCartStore' -import { useProductCartContext } from '@/contexts/ProductCartContext' +import { useDebounce } from 'usehooks-ts'; +import { useCartStore } from '../stores/useCartStore'; +import { useProductCartContext } from '@/contexts/ProductCartContext'; +import { + removeSelectedItemsFromCookie, + removeCartItemsFromCookie, +} from '~/utils/cart'; +import { toast } from 'react-hot-toast'; type Props = { - item: CartItem -} + item: CartItem; +}; const CartItemAction = ({ item }: Props) => { - const auth = getAuth() - const { setRefreshCart } = useProductCartContext() - const [isLoadDelete, setIsLoadDelete] = useState(false) - const [isLoadQuantity, setIsLoadQuantity] = useState(false) + const auth = getAuth(); + const { setRefreshCart } = useProductCartContext(); + const [isLoadDelete, setIsLoadDelete] = useState(false); + const [isLoadQuantity, setIsLoadQuantity] = useState(false); - const [quantity, setQuantity] = useState(item.quantity) + const [quantity, setQuantity] = useState(item.quantity); - const { loadCart } = useCartStore() + const { loadCart, cart, updateCartItem } = useCartStore(); // TAMBAHKAN cart dan updateCartItem - const limitQty = item.limit_qty?.transaction || 0 + const limitQty = item.limit_qty?.transaction || 0; + // PERBAIKI FUNCTION INI const handleDelete = async () => { - if (typeof auth !== 'object') return - - setIsLoadDelete(true) - await deleteUserCart(auth.id, [item.cart_id]) - await loadCart(auth.id) - setIsLoadDelete(false) - setRefreshCart(true) - } - - const decreaseQty = () => { setQuantity((quantity) => quantity -= 1) } - const increaseQty = () => { setQuantity((quantity) => quantity += 1) } - const debounceQty = useDebounce(quantity, 1000) + if (typeof auth !== 'object') return; + + setIsLoadDelete(true); + + try { + // Step 1: Delete from server + await deleteUserCart(auth.id, [item.cart_id]); + + // Step 2: Clean up cookies IMMEDIATELY + removeSelectedItemsFromCookie([item.id]); + removeCartItemsFromCookie([item.cart_id]); + + // Step 3: Update local cart state optimistically + if (cart) { + const updatedProducts = cart.products.filter( + (product) => product.id !== item.id + ); + const updatedCart = { + ...cart, + products: updatedProducts, + product_total: updatedProducts.length, + }; + updateCartItem(updatedCart); + } + + // Step 4: Reload from server to ensure consistency + await loadCart(auth.id); + setRefreshCart(true); + + toast.success('Item berhasil dihapus'); + } catch (error) { + console.error('Failed to delete cart item:', error); + toast.error('Gagal menghapus item'); + + // Reload on error + await loadCart(auth.id); + } finally { + setIsLoadDelete(false); + } + }; + + const decreaseQty = () => { + setQuantity((quantity) => (quantity -= 1)); + }; + const increaseQty = () => { + setQuantity((quantity) => (quantity += 1)); + }; + const debounceQty = useDebounce(quantity, 1000); + useEffect(() => { - if (isNaN(debounceQty)) setQuantity(1) - if (limitQty > 0 && debounceQty > limitQty) setQuantity(limitQty) - }, [debounceQty, limitQty]) + if (isNaN(debounceQty)) setQuantity(1); + if (limitQty > 0 && debounceQty > limitQty) setQuantity(limitQty); + }, [debounceQty, limitQty]); useEffect(() => { const updateCart = async () => { - if (typeof auth !== 'object' || isNaN(debounceQty)) return + if (typeof auth !== 'object' || isNaN(debounceQty)) return; - setIsLoadQuantity(true) + setIsLoadQuantity(true); await upsertUserCart({ userId: auth.id, type: item.cart_type, id: item.id, qty: debounceQty, selected: item.selected, - }) - await loadCart(auth.id) - setIsLoadQuantity(false) - } - updateCart() + }); + await loadCart(auth.id); + setIsLoadQuantity(false); + }; + updateCart(); //eslint-disable-next-line react-hooks/exhaustive-deps - }, [debounceQty]) + }, [debounceQty]); return (
- @@ -106,7 +153,7 @@ const CartItemAction = ({ item }: Props) => {
- ) -} + ); +}; -export default CartItemAction \ No newline at end of file +export default CartItemAction; -- cgit v1.2.3 From 3feaad9127ff429b27f0eb69fa6ea539de2f2e8c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 26 May 2025 20:00:17 +0700 Subject: Cleaning code --- src-migrate/modules/cart/components/ItemAction.tsx | 125 ++++++++++++++++++--- 1 file changed, 109 insertions(+), 16 deletions(-) (limited to 'src-migrate/modules/cart/components/ItemAction.tsx') diff --git a/src-migrate/modules/cart/components/ItemAction.tsx b/src-migrate/modules/cart/components/ItemAction.tsx index eea0cbe9..4dcebd9e 100644 --- a/src-migrate/modules/cart/components/ItemAction.tsx +++ b/src-migrate/modules/cart/components/ItemAction.tsx @@ -15,7 +15,11 @@ import { useProductCartContext } from '@/contexts/ProductCartContext'; import { removeSelectedItemsFromCookie, removeCartItemsFromCookie, + quantityUpdateState, + getCartDataFromCookie, + setCartDataToCookie, } from '~/utils/cart'; + import { toast } from 'react-hot-toast'; type Props = { @@ -30,25 +34,24 @@ const CartItemAction = ({ item }: Props) => { const [quantity, setQuantity] = useState(item.quantity); - const { loadCart, cart, updateCartItem } = useCartStore(); // TAMBAHKAN cart dan updateCartItem + const { loadCart, cart, updateCartItem } = useCartStore(); const limitQty = item.limit_qty?.transaction || 0; - // PERBAIKI FUNCTION INI const handleDelete = async () => { if (typeof auth !== 'object') return; setIsLoadDelete(true); try { - // Step 1: Delete from server + // Delete from server await deleteUserCart(auth.id, [item.cart_id]); - // Step 2: Clean up cookies IMMEDIATELY + // Clean up cookies immediately removeSelectedItemsFromCookie([item.id]); removeCartItemsFromCookie([item.cart_id]); - // Step 3: Update local cart state optimistically + // Update local cart state optimistically if (cart) { const updatedProducts = cart.products.filter( (product) => product.id !== item.id @@ -61,7 +64,7 @@ const CartItemAction = ({ item }: Props) => { updateCartItem(updatedCart); } - // Step 4: Reload from server to ensure consistency + // Reload from server and refresh context await loadCart(auth.id); setRefreshCart(true); @@ -77,12 +80,47 @@ const CartItemAction = ({ item }: Props) => { } }; + const updateQuantityInCookie = (productId, cartId, newQuantity) => { + try { + const cartData = getCartDataFromCookie(); + let itemFound = false; + + // Find item by cart_id key or search within objects + if (cartData[cartId]) { + cartData[cartId].quantity = newQuantity; + itemFound = true; + } else { + // Search by product id or cart_id within objects + for (const key in cartData) { + const item = cartData[key]; + if (item.id === productId || item.cart_id === cartId) { + item.quantity = newQuantity; + itemFound = true; + break; + } + } + } + + if (itemFound) { + setCartDataToCookie(cartData); + return true; + } + + return false; + } catch (error) { + console.error('Error updating quantity in cookie:', error); + return false; + } + }; + const decreaseQty = () => { setQuantity((quantity) => (quantity -= 1)); }; + const increaseQty = () => { setQuantity((quantity) => (quantity += 1)); }; + const debounceQty = useDebounce(quantity, 1000); useEffect(() => { @@ -93,18 +131,73 @@ const CartItemAction = ({ item }: Props) => { useEffect(() => { const updateCart = async () => { if (typeof auth !== 'object' || isNaN(debounceQty)) return; + if (debounceQty === item.quantity) return; + quantityUpdateState.startUpdate(item.id); setIsLoadQuantity(true); - await upsertUserCart({ - userId: auth.id, - type: item.cart_type, - id: item.id, - qty: debounceQty, - selected: item.selected, - }); - await loadCart(auth.id); - setIsLoadQuantity(false); + + try { + // Update cookie immediately for responsive UI + updateQuantityInCookie(item.id, item.cart_id, debounceQty); + + // Update local cart state optimistically + if (cart) { + const updatedProducts = cart.products.map((product) => + product.id === item.id + ? { ...product, quantity: debounceQty } + : product + ); + const updatedCart = { + ...cart, + products: updatedProducts, + }; + updateCartItem(updatedCart); + } + + // Send update to server + await upsertUserCart({ + userId: auth.id, + type: item.cart_type, + id: item.id, + qty: debounceQty, + selected: item.selected, + }); + + // Reload from server to ensure consistency + await loadCart(auth.id); + + // Re-update cookie if server reload overwrote it + const currentCookieData = getCartDataFromCookie(); + let needsReUpdate = false; + + for (const key in currentCookieData) { + const cookieItem = currentCookieData[key]; + if ( + (cookieItem.id === item.id || + cookieItem.cart_id === item.cart_id) && + cookieItem.quantity !== debounceQty + ) { + needsReUpdate = true; + break; + } + } + + if (needsReUpdate) { + updateQuantityInCookie(item.id, item.cart_id, debounceQty); + } + } catch (error) { + console.error('Error updating quantity:', error); + toast.error('Gagal mengupdate quantity'); + + // Revert changes on error + updateQuantityInCookie(item.id, item.cart_id, item.quantity); + loadCart(auth.id); + } finally { + setIsLoadQuantity(false); + quantityUpdateState.endUpdate(item.id); + } }; + updateCart(); //eslint-disable-next-line react-hooks/exhaustive-deps }, [debounceQty]); @@ -156,4 +249,4 @@ const CartItemAction = ({ item }: Props) => { ); }; -export default CartItemAction; +export default CartItemAction; \ No newline at end of file -- cgit v1.2.3