Files
dcorya/src/lib/cart-context.tsx
T
Dcorya Store c126a6914a Initial commit: Dcorya storefront on Meshkee Website API
Next.js/TypeScript/Tailwind rebuild of dcorya.com in Persian/RTL, wired to
the live Meshkee Website API for products, blog, comments, cart, and contact.
2026-09-05 15:40:20 +03:30

106 lines
2.7 KiB
TypeScript

"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import { meshkee, type Cart } from "./meshkee";
import { useAuth } from "./auth-context";
type CartContextValue = {
cart: Cart | null;
itemCount: number;
isLoading: boolean;
refresh: () => Promise<void>;
addItem: (storeItemVariantId: string, quantity?: number) => Promise<void>;
updateItem: (itemId: string, quantity: number) => Promise<void>;
removeItem: (itemId: string) => Promise<void>;
};
const CartContext = createContext<CartContextValue | null>(null);
export function CartProvider({
businessId,
children,
}: {
businessId: string;
children: React.ReactNode;
}) {
const { accessToken } = useAuth();
const [cart, setCart] = useState<Cart | null>(null);
const [isLoading, setIsLoading] = useState(false);
const refresh = useCallback(async () => {
if (!accessToken) {
setCart(null);
return;
}
setIsLoading(true);
try {
const { cart } = await meshkee.getCart(businessId, accessToken);
setCart(cart);
} catch {
setCart(null);
} finally {
setIsLoading(false);
}
}, [accessToken, businessId]);
useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect -- refetch cart when auth state changes
refresh();
}, [refresh]);
const addItem = useCallback(
async (storeItemVariantId: string, quantity = 1) => {
if (!accessToken) return;
const { cart } = await meshkee.addCartItem(businessId, accessToken, {
storeItemVariantId,
quantity,
});
setCart(cart);
},
[accessToken, businessId]
);
const updateItem = useCallback(
async (itemId: string, quantity: number) => {
if (!accessToken) return;
const { cart } = await meshkee.updateCartItem(businessId, accessToken, itemId, quantity);
setCart(cart);
},
[accessToken, businessId]
);
const removeItem = useCallback(
async (itemId: string) => {
if (!accessToken) return;
const { cart } = await meshkee.removeCartItem(businessId, accessToken, itemId);
setCart(cart);
},
[accessToken, businessId]
);
const itemCount = useMemo(
() => cart?.items?.reduce((sum, item) => sum + item.quantity, 0) ?? 0,
[cart]
);
const value = useMemo(
() => ({ cart, itemCount, isLoading, refresh, addItem, updateItem, removeItem }),
[cart, itemCount, isLoading, refresh, addItem, updateItem, removeItem]
);
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
}
export function useCart() {
const ctx = useContext(CartContext);
if (!ctx) throw new Error("useCart must be used within CartProvider");
return ctx;
}