Files
frontend/lib/cart-context.tsx
T
TakahashiNguyen ae8134fd64
Release package / release (push) Failing after 7m21s
fix: manager login and menu management (#38)
Co-authored-by: TakahashiNg <83152264+TakahashiNguyen@users.noreply.github.com>
Reviewed-on: #38
2026-05-13 06:29:14 +00:00

141 lines
3.4 KiB
TypeScript

"use client";
import { createContext, useContext, useEffect, useMemo, useState } from "react";
import { CartItemEntity, MenuItemEntity } from "./types";
interface CartContextValue {
items: CartItemEntity[];
totalItems: number;
totalPrice: number;
addToCart: (product: MenuItemEntity) => void;
increaseQty: (id: string) => void;
decreaseQty: (id: string) => void;
removeFromCart: (id: string) => void;
setQuantity: (id: string, 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<CartItemEntity[]>([]);
useEffect(() => {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return;
const parsed = JSON.parse(raw) as CartItemEntity[];
if (Array.isArray(parsed)) {
setItems(parsed.filter((i) => i && i.productId && i.quantity > 0));
}
} catch {
localStorage.removeItem(STORAGE_KEY);
}
}, []);
useEffect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
}, [items]);
const addToCart = (product: MenuItemEntity) => {
setItems((prev) => {
const index = prev.findIndex((i) => i.productId === product.id);
if (index === -1) {
return [
...prev,
{
productId: product.id!,
name: product.name,
description: product.description,
priceAtTimeOfAdding: product.price,
quantity: 1,
},
];
}
const next = [...prev];
next[index] = { ...next[index], quantity: next[index].quantity + 1 };
return next;
});
};
const increaseQty = (id: string) => {
setItems((prev) =>
prev.map((item) =>
item.productId === id ? { ...item, quantity: item.quantity + 1 } : item,
),
);
};
const decreaseQty = (id: string) => {
setItems((prev) =>
prev
.map((item) =>
item.productId === id
? { ...item, quantity: Math.max(0, item.quantity - 1) }
: item,
)
.filter((item) => item.quantity > 0),
);
};
const removeFromCart = (id: string) => {
setItems((prev) => prev.filter((item) => item.productId !== id));
};
const setQuantity = (id: string, 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.productId === 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.priceAtTimeOfAdding * 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;
}