Files
frontend/lib/cart-context.tsx
T
2026-03-25 03:07:52 +00:00

144 lines
3.4 KiB
TypeScript

"use client";
import type { Product } from "@/lib/types";
import { createContext, useContext, useEffect, useMemo, useState } from "react";
export interface CartItem {
id: number;
name: string;
description: string;
price: number;
quantity: number;
}
interface CartContextValue {
items: CartItem[];
totalItems: number;
totalPrice: number;
addToCart: (product: Product) => void;
increaseQty: (id: number) => void;
decreaseQty: (id: number) => void;
removeFromCart: (id: number) => void;
setQuantity: (id: number, quantity: number) => void;
}
const STORAGE_KEY = "coffee-shop-cart";
const CartContext = createContext<CartContextValue | null>(null);
export function CartProvider({ children }: { children: React.ReactNode }) {
const [items, setItems] = useState<CartItem[]>([]);
useEffect(() => {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return;
const parsed = JSON.parse(raw) as CartItem[];
if (Array.isArray(parsed)) {
setItems(parsed.filter((i) => i && i.id && i.quantity > 0));
}
} catch {
localStorage.removeItem(STORAGE_KEY);
}
}, []);
useEffect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
}, [items]);
const addToCart = (product: Product) => {
setItems((prev) => {
const index = prev.findIndex((i) => i.id === product.id);
if (index === -1) {
return [
...prev,
{
id: product.id,
name: product.name,
description: product.description,
price: product.price,
quantity: 1,
},
];
}
const next = [...prev];
next[index] = { ...next[index], quantity: next[index].quantity + 1 };
return next;
});
};
const increaseQty = (id: number) => {
setItems((prev) =>
prev.map((item) =>
item.id === id ? { ...item, quantity: item.quantity + 1 } : item,
),
);
};
const decreaseQty = (id: number) => {
setItems((prev) =>
prev
.map((item) =>
item.id === id
? { ...item, quantity: Math.max(0, item.quantity - 1) }
: item,
)
.filter((item) => item.quantity > 0),
);
};
const removeFromCart = (id: number) => {
setItems((prev) => prev.filter((item) => item.id !== id));
};
const setQuantity = (id: number, quantity: number) => {
const safeQty = Number.isFinite(quantity)
? Math.max(0, Math.floor(quantity))
: 0;
if (safeQty === 0) {
removeFromCart(id);
return;
}
setItems((prev) =>
prev.map((item) =>
item.id === id ? { ...item, quantity: safeQty } : item,
),
);
};
const totalItems = useMemo(
() => items.reduce((sum, item) => sum + item.quantity, 0),
[items],
);
const totalPrice = useMemo(
() => items.reduce((sum, item) => sum + item.price * item.quantity, 0),
[items],
);
const value = useMemo(
() => ({
items,
totalItems,
totalPrice,
addToCart,
increaseQty,
decreaseQty,
removeFromCart,
setQuantity,
}),
[items, totalItems, totalPrice],
);
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
}
export function useCart() {
const context = useContext(CartContext);
if (!context) {
throw new Error("useCart must be used within CartProvider");
}
return context;
}