chore: add client/src/hooks/use-cart.tsx

This commit is contained in:
notshop 2026-04-26 16:35:02 +00:00
parent 12b68031e1
commit d05c981dae

View file

@ -0,0 +1,91 @@
import { createContext, useContext, useState, useCallback } from "react";
import { useQuery, useMutation } from "@tanstack/react-query";
import { queryClient, apiRequest } from "../lib/queryClient";
interface CartItem {
id: number;
cartId: number;
productId: number;
variantId?: number;
title: string;
variantTitle?: string;
price: string;
quantity: number;
imageUrl?: string;
productHandle?: string;
}
interface CartContextType {
items: CartItem[];
isOpen: boolean;
openCart: () => void;
closeCart: () => void;
toggleCart: () => void;
addItem: (productId: number, variantId?: number, quantity?: number) => Promise<void>;
updateQuantity: (itemId: number, quantity: number) => Promise<void>;
removeItem: (itemId: number) => Promise<void>;
itemCount: number;
subtotal: number;
isLoading: boolean;
}
const CartContext = createContext<CartContextType | null>(null);
export function CartProvider({ children }: { children: React.ReactNode }) {
const [isOpen, setIsOpen] = useState(false);
const { data, isLoading } = useQuery<{ cart: any; items: CartItem[] }>({
queryKey: ["/api/cart"],
queryFn: async () => {
const res = await fetch("/api/cart", { credentials: "include" });
return res.json();
},
});
const items = data?.items || [];
const invalidateCart = () => queryClient.invalidateQueries({ queryKey: ["/api/cart"] });
const addItem = useCallback(async (productId: number, variantId?: number, quantity = 1) => {
await apiRequest("POST", "/api/cart/items", { productId, variantId, quantity });
await invalidateCart();
setIsOpen(true);
}, []);
const updateQuantity = useCallback(async (itemId: number, quantity: number) => {
await apiRequest("PATCH", `/api/cart/items/${itemId}`, { quantity });
await invalidateCart();
}, []);
const removeItem = useCallback(async (itemId: number) => {
await apiRequest("DELETE", `/api/cart/items/${itemId}`);
await invalidateCart();
}, []);
const itemCount = items.reduce((sum, i) => sum + i.quantity, 0);
const subtotal = items.reduce((sum, i) => sum + parseFloat(i.price) * i.quantity, 0);
return (
<CartContext.Provider value={{
items,
isOpen,
openCart: () => setIsOpen(true),
closeCart: () => setIsOpen(false),
toggleCart: () => setIsOpen((v) => !v),
addItem,
updateQuantity,
removeItem,
itemCount,
subtotal,
isLoading,
}}>
{children}
</CartContext.Provider>
);
}
export function useCart() {
const ctx = useContext(CartContext);
if (!ctx) throw new Error("useCart must be used within CartProvider");
return ctx;
}