144 lines
3.4 KiB
TypeScript
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;
|
|
}
|