From 4904573845478e7e9648735d008153728870a123 Mon Sep 17 00:00:00 2001
From: Miqdad <ahmadmiqdad27@gmail.com>
Date: Fri, 23 May 2025 09:37:46 +0700
Subject: <miqdad> 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<boolean>(false)
-  const [isLoadQuantity, setIsLoadQuantity] = useState<boolean>(false)
+  const auth = getAuth();
+  const { setRefreshCart } = useProductCartContext();
+  const [isLoadDelete, setIsLoadDelete] = useState<boolean>(false);
+  const [isLoadQuantity, setIsLoadQuantity] = useState<boolean>(false);
 
-  const [quantity, setQuantity] = useState<number>(item.quantity)
+  const [quantity, setQuantity] = useState<number>(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 (
     <div className={style.actionSection}>
-      <button className={style.deleteButton} onClick={handleDelete} disabled={isLoadDelete}>
+      <button
+        className={style.deleteButton}
+        onClick={handleDelete}
+        disabled={isLoadDelete}
+      >
         {isLoadDelete && <Spinner size='xs' />}
         {!isLoadDelete && <Trash2Icon size={16} />}
       </button>
@@ -106,7 +153,7 @@ const CartItemAction = ({ item }: Props) => {
         </Tooltip>
       </div>
     </div>
-  )
-}
+  );
+};
 
-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 <ahmadmiqdad27@gmail.com>
Date: Mon, 26 May 2025 20:00:17 +0700
Subject: <miqdad> 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<number>(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

