Rocky_Mountain_Vending/lib/cart/context.tsx

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
}