import { create } from 'zustand'; import { CartItem, CartProps } from '~/types/cart'; import { getUserCart } from '~/services/cart'; import { syncCartWithCookie, getCartDataFromCookie, getSelectedItemsFromCookie, updateSelectedItemInCookie, setAllSelectedInCookie, removeCartItemsFromCookie, forceResetAllSelectedItems, } from '~/utils/cart'; type State = { cart: CartProps | null; isLoadCart: boolean; summary: { subtotal: number; discount: number; total: number; tax: number; grandTotal: number; }; }; type Action = { loadCart: (userId: number) => Promise; updateCartItem: (updateCart: CartProps) => void; syncCartWithCookieAndUpdate: (cart: CartProps) => { needsUpdate: boolean }; forceResetSelection: () => void; }; export const useCartStore = create((set, get) => ({ cart: null, isLoadCart: false, summary: { subtotal: 0, discount: 0, total: 0, tax: 0, grandTotal: 0, }, loadCart: async (userId) => { if (get().isLoadCart === true) return; set({ isLoadCart: true }); try { // Fetch cart from API const cart: CartProps = (await getUserCart(userId)) as CartProps; // Sync with cookie and get updated data if needed const { needsUpdate } = get().syncCartWithCookieAndUpdate(cart); // If no update needed from cookie, just set the cart directly if (!needsUpdate) { set({ cart }); } // Update summary with current cart const summary = computeSummary(get().cart!); set({ summary }); } catch (error) { console.error('Failed to load cart from API:', error); // Fallback to cookie if API fails try { const cartData = getCartDataFromCookie(); if (Object.keys(cartData).length > 0) { // Transform cart data from cookie to expected format const products = Object.values(cartData).map((item) => ({ cart_id: item.cart_id, id: item.id, cart_type: item.cart_type, product_id: item.product?.id, product_name: item.product?.name, program_line_id: item.program_line?.id, program_line_name: item.program_line?.name, quantity: item.quantity, selected: item.selected, price: item.price, package_price: item.package_price, source: item.source, })); const fallbackCart: CartProps = { product_total: products.length, products, }; set({ cart: fallbackCart }); const summary = computeSummary(fallbackCart); set({ summary }); } } catch (cookieError) { console.error('Failed to fallback to cookie:', cookieError); } } finally { set({ isLoadCart: false }); } }, updateCartItem: (updatedCart) => { const cart = get().cart; if (!cart) return; set({ cart: updatedCart }); // Sync updated cart with cookie syncCartWithCookie(updatedCart); const summary = computeSummary(updatedCart); set({ summary }); }, syncCartWithCookieAndUpdate: (cart) => { if (!cart) return { needsUpdate: false }; // Sync cart with cookie const result = syncCartWithCookie(cart); // If we need to update the cart based on cookie data if (result.needsUpdate && cart.products) { // Create updated cart with selections from cookie const selectedItems = getSelectedItemsFromCookie(); const updatedCart = { ...cart, products: cart.products.map((item) => ({ ...item, selected: selectedItems[item.id] !== undefined ? selectedItems[item.id] : item.selected, })), }; // Update the store set({ cart: updatedCart }); } return result; }, forceResetSelection: () => { const { cart } = get(); if (!cart) return; // Reset all selections in cookie forceResetAllSelectedItems(); // Update the cart in state const updatedCart = { ...cart, products: cart.products.map((item) => ({ ...item, selected: false, })), }; set({ cart: updatedCart }); // Update summary const summary = computeSummary(updatedCart); set({ summary }); }, })); const computeSummary = (cart: CartProps) => { let subtotal = 0; let discount = 0; const PPN: number = process.env.NEXT_PUBLIC_PPN ? parseFloat(process.env.NEXT_PUBLIC_PPN) : 0; for (const item of cart?.products) { if (!item.selected) continue; let price = 0; if (item.cart_type === 'promotion') price = (item?.package_price || 0) * item.quantity; else if (item.cart_type === 'product') price = item.price.price * item.quantity; subtotal += price; discount += price - item.price.price_discount * item.quantity; } let total = subtotal - discount; let grandTotal = total * PPN; let tax = grandTotal - total; // let grandTotal = total + tax; return { subtotal, discount, total, grandTotal, tax }; };