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.
106 lines
2.7 KiB
TypeScript
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;
|
|
}
|