diff --git a/FRONTEND/src/components/ui/Navbar.jsx b/FRONTEND/src/components/ui/Navbar.jsx index c0698d2..e62af64 100644 --- a/FRONTEND/src/components/ui/Navbar.jsx +++ b/FRONTEND/src/components/ui/Navbar.jsx @@ -18,6 +18,7 @@ import { useCurrencyStore } from "../../store/currency"; import { formatPrice } from "../../utils/currency"; import { useAuth } from "../../context/AuthContext"; + const API = (import.meta.env.VITE_API_URL || '').replace(/\/$/, ''); const EmptyCartIllustration = () => { diff --git a/FRONTEND/src/components/ui/ProductCard.jsx b/FRONTEND/src/components/ui/ProductCard.jsx index a7259d8..d0b37a0 100644 --- a/FRONTEND/src/components/ui/ProductCard.jsx +++ b/FRONTEND/src/components/ui/ProductCard.jsx @@ -93,7 +93,9 @@ const ProductCard = ({ product }) => { } = useProductStore(); const isInCompare = compareList.some((p) => p._id === product._id); - const { addToCart } = useCartStore(); + const addToCart = useCartStore( + (state) => state.addToCart + ); const { addToWishlist, removeFromWishlist, checkInWishlist } = useWishlist(); const toast = useToast(); const { currency, rates } = useCurrencyStore(); diff --git a/FRONTEND/src/components/ui/QuickViewModal.jsx b/FRONTEND/src/components/ui/QuickViewModal.jsx index 9a91a63..99b38df 100644 --- a/FRONTEND/src/components/ui/QuickViewModal.jsx +++ b/FRONTEND/src/components/ui/QuickViewModal.jsx @@ -19,7 +19,7 @@ import { useCurrencyStore } from "../../store/currency"; import { formatPrice } from "../../utils/currency"; const QuickViewModal = ({ isOpen, onClose, product }) => { - const { addToCart } = useCartStore(); + const addToCart = useCartStore((state) => state.addToCart); const { currency, rates } = useCurrencyStore(); if (!product) return null; diff --git a/FRONTEND/src/store/cart.js b/FRONTEND/src/store/cart.js index b2b75bc..382e978 100644 --- a/FRONTEND/src/store/cart.js +++ b/FRONTEND/src/store/cart.js @@ -1,40 +1,100 @@ import { create } from 'zustand'; -import axios from 'axios'; - -export const useCartStore = create((set) => ({ - cart: null, - loading: false, - - fetchCart: async () => { - set({ loading: true }); - try { - const response = await axios.get('/api/cart'); - set({ cart: response.data, loading: false }); - } catch (error) { - set({ loading: false }); - console.error('Error fetching cart:', error); - } - }, - - addToCart: async (productId, variantId = null, quantity = 1) => { - set({ loading: true }); - try { - const response = await axios.post('/api/cart', { productId, variantId, quantity }); - set({ cart: response.data, loading: false }); - } catch (error) { - set({ loading: false }); - console.error('Error adding to cart:', error); - } - }, - - removeFromCart: async (productId, variantId = null) => { - set({ loading: true }); - try { - const response = await axios.delete('/api/cart', { data: { productId, variantId } }); - set({ cart: response.data, loading: false }); - } catch (error) { - set({ loading: false }); - console.error('Error removing from cart:', error); +import { persist } from 'zustand/middleware'; + +export const useCartStore = create( + persist( + (set) => ({ + cartItems: [], + + addToCart: (product, quantity = 1) => { + const stockTracked = product.stock != null; + if (stockTracked && product.stock === 0) return { status: 'out_of_stock', added: 0 }; + + let status = 'added'; + let added = 0; + + set((state) => { + const existingItem = state.cartItems.find((item) => item._id === product._id); + const currentQty = existingItem ? existingItem.quantity : 0; + + let canAdd = quantity; + if (stockTracked) { + const available = product.stock - currentQty; + if (available <= 0) { + status = 'capped'; + added = 0; + return state; + } + canAdd = Math.min(quantity, available); + if (canAdd < quantity) status = 'capped'; + } + added = canAdd; + + if (existingItem) { + return { + cartItems: state.cartItems.map((item) => + item._id === product._id ? { ...item, quantity: item.quantity + canAdd } : item + ), + }; + } + return { cartItems: [...state.cartItems, { ...product, quantity: canAdd }] }; + }); + + return { status, added }; + }, + + removeFromCart: (id) => { + set((state) => ({ + cartItems: state.cartItems.filter((item) => item._id !== id), + })); + }, + + addBundleToCart: (items) => { + let addedCount = 0; + let skippedCount = 0; + set((state) => { + const updated = [...state.cartItems]; + for (const item of items) { + const stockTracked = item.stock != null; + const idx = updated.findIndex((i) => i._id === item._id); + const currentQty = idx >= 0 ? updated[idx].quantity : 0; + + if (stockTracked) { + const available = item.stock - currentQty; + if (available <= 0) { + skippedCount++; + continue; + } + } + + if (idx >= 0) { + updated[idx] = { ...updated[idx], quantity: updated[idx].quantity + 1 }; + } else { + updated.push({ ...item, quantity: 1 }); + } + addedCount++; + } + return { cartItems: updated }; + }); + return { addedCount, skippedCount }; + }, + + emptyCart: () => set({ cartItems: [] }), + }), + { + name: 'productStoreCart', } - } -})); \ No newline at end of file + ) +); + +export const useCart = () => { + const cartItems = useCartStore((state) => state.cartItems); + const addToCart = useCartStore((state) => state.addToCart); + const removeFromCart = useCartStore((state) => state.removeFromCart); + const addBundleToCart = useCartStore((state) => state.addBundleToCart); + const emptyCart = useCartStore((state) => state.emptyCart); + + const totalPrice = cartItems.reduce((total, item) => total + item.price * item.quantity, 0); + + return { cartItems, addToCart, removeFromCart, addBundleToCart, emptyCart, totalPrice }; +};