127 lines
3.1 KiB
TypeScript
127 lines
3.1 KiB
TypeScript
"use client"
|
|
|
|
import {
|
|
createContext,
|
|
useContext,
|
|
useState,
|
|
useEffect,
|
|
ReactNode,
|
|
useMemo,
|
|
useCallback,
|
|
} from "react"
|
|
|
|
export interface CartItem {
|
|
id: string
|
|
name: string
|
|
price: number
|
|
priceId: string
|
|
image: string
|
|
quantity: number
|
|
}
|
|
|
|
interface CartContextType {
|
|
items: CartItem[]
|
|
addItem: (item: Omit<CartItem, "quantity"> & { quantity?: number }) => void
|
|
removeItem: (id: string) => void
|
|
updateQuantity: (id: string, quantity: number) => void
|
|
clearCart: () => void
|
|
getTotal: () => number
|
|
getItemCount: () => number
|
|
}
|
|
|
|
const CartContext = createContext<CartContextType | undefined>(undefined)
|
|
|
|
const CART_STORAGE_KEY = "rmv-cart"
|
|
|
|
export function CartProvider({ children }: { children: ReactNode }) {
|
|
const [items, setItems] = useState<CartItem[]>([])
|
|
const [isLoaded, setIsLoaded] = useState(false)
|
|
|
|
// Load cart from localStorage on mount
|
|
useEffect(() => {
|
|
try {
|
|
const stored = localStorage.getItem(CART_STORAGE_KEY)
|
|
if (stored) {
|
|
setItems(JSON.parse(stored))
|
|
}
|
|
} catch (error) {
|
|
console.error("Error loading cart from localStorage:", error)
|
|
} finally {
|
|
setIsLoaded(true)
|
|
}
|
|
}, [])
|
|
|
|
// Save cart to localStorage whenever it changes
|
|
useEffect(() => {
|
|
if (isLoaded) {
|
|
try {
|
|
localStorage.setItem(CART_STORAGE_KEY, JSON.stringify(items))
|
|
} catch (error) {
|
|
console.error("Error saving cart to localStorage:", error)
|
|
}
|
|
}
|
|
}, [items, isLoaded])
|
|
|
|
const addItem = (
|
|
item: Omit<CartItem, "quantity"> & { quantity?: number }
|
|
) => {
|
|
setItems((prevItems) => {
|
|
const existingItem = prevItems.find((i) => i.id === item.id)
|
|
if (existingItem) {
|
|
return prevItems.map((i) =>
|
|
i.id === item.id
|
|
? { ...i, quantity: i.quantity + (item.quantity || 1) }
|
|
: i
|
|
)
|
|
}
|
|
return [...prevItems, { ...item, quantity: item.quantity || 1 }]
|
|
})
|
|
}
|
|
|
|
const removeItem = (id: string) => {
|
|
setItems((prevItems) => prevItems.filter((i) => i.id !== id))
|
|
}
|
|
|
|
const updateQuantity = (id: string, quantity: number) => {
|
|
if (quantity <= 0) {
|
|
removeItem(id)
|
|
return
|
|
}
|
|
setItems((prevItems) =>
|
|
prevItems.map((i) => (i.id === id ? { ...i, quantity } : i))
|
|
)
|
|
}
|
|
|
|
const clearCart = () => {
|
|
setItems([])
|
|
}
|
|
|
|
// Memoize cart totals to prevent unnecessary re-renders in consuming components
|
|
const cartTotal = useMemo(() => {
|
|
return items.reduce((total, item) => total + item.price * item.quantity, 0)
|
|
}, [items])
|
|
|
|
const cartItemCount = useMemo(() => {
|
|
return items.reduce((count, item) => count + item.quantity, 0)
|
|
}, [items])
|
|
|
|
const value: CartContextType = {
|
|
items,
|
|
addItem,
|
|
removeItem,
|
|
updateQuantity,
|
|
clearCart,
|
|
getTotal: () => cartTotal,
|
|
getItemCount: () => cartItemCount,
|
|
}
|
|
|
|
return <CartContext.Provider value={value}>{children}</CartContext.Provider>
|
|
}
|
|
|
|
export function useCart() {
|
|
const context = useContext(CartContext)
|
|
if (context === undefined) {
|
|
throw new Error("useCart must be used within a CartProvider")
|
|
}
|
|
return context
|
|
}
|