chore: add client/src/hooks/use-cart.tsx
This commit is contained in:
parent
12b68031e1
commit
d05c981dae
1 changed files with 91 additions and 0 deletions
91
client/src/hooks/use-cart.tsx
Normal file
91
client/src/hooks/use-cart.tsx
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue