Stripe Payment

This commit is contained in:
augmentedpotato
2026-04-09 22:27:03 -06:00
parent 559f3bc343
commit 4d91d8b331
26 changed files with 2022 additions and 33 deletions

171
web/context/CartContext.js Normal file
View File

@@ -0,0 +1,171 @@
"use client";
import { createContext, useContext, useState, useEffect, useCallback } from "react";
import { useAuth } from "@/context/AuthContext";
import {
fetchCart,
apiAddToCart,
apiUpdateCartItem,
apiRemoveCartItem,
apiClearCart,
apiApplyCoupon,
apiCheckout,
} from "@/lib/cartApi";
const CartContext = createContext(null);
const STORE_KEY = "selected_store_id";
export function CartProvider({ children }) {
const { user, token } = useAuth();
const [cart, setCart] = useState(null);
const [selectedStoreId, setSelectedStoreIdState] = useState(null);
const [cartLoading, setCartLoading] = useState(false);
const [cartError, setCartError] = useState(null);
const setStoreId = useCallback((id) => {
const parsed = id ? Number(id) : null;
setSelectedStoreIdState(parsed);
if (parsed) {
localStorage.setItem(STORE_KEY, String(parsed));
}
else {
localStorage.removeItem(STORE_KEY);
}
}, []);
useEffect(() => {
const stored = localStorage.getItem(STORE_KEY);
if (stored) {
setSelectedStoreIdState(Number(stored));
}
}, []);
const refreshCart = useCallback(async () => {
if (!token || !selectedStoreId) {
setCart(null);
return;
}
setCartLoading(true);
setCartError(null);
try {
const data = await fetchCart(token, selectedStoreId);
setCart(data);
}
catch (err) {
setCartError(err.message);
}
finally {
setCartLoading(false);
}
}, [token, selectedStoreId]);
useEffect(() => {
if (user && selectedStoreId) {
refreshCart();
}
else {
setCart(null);
}
}, [user, selectedStoreId, refreshCart]);
const addItem = useCallback(
async (prodId, quantity = 1) => {
if (!token || !selectedStoreId) throw new Error("Select a store first");
const updated = await apiAddToCart(token, { prodId, storeId: selectedStoreId, quantity });
setCart(updated);
return updated;
},
[token, selectedStoreId]
);
const updateItem = useCallback(
async (cartItemId, quantity) => {
if (!token) return;
const updated = await apiUpdateCartItem(token, { cartItemId, quantity });
setCart(updated);
return updated;
},
[token]
);
const removeItem = useCallback(
async (cartItemId) => {
if (!token) return;
const updated = await apiRemoveCartItem(token, cartItemId);
setCart(updated);
return updated;
},
[token]
);
const clearCart = useCallback(async () => {
if (!token || !selectedStoreId) return;
await apiClearCart(token, selectedStoreId);
setCart(null);
}, [token, selectedStoreId]);
const applyCoupon = useCallback(
async (couponCode) => {
if (!token || !selectedStoreId) throw new Error("Select a store first");
const updated = await apiApplyCoupon(token, selectedStoreId, couponCode);
setCart(updated);
return updated;
},
[token, selectedStoreId]
);
const checkout = useCallback(
async (paymentMethodId) => {
if (!token || !selectedStoreId) throw new Error("Select a store first");
const result = await apiCheckout(token, {
storeId: selectedStoreId,
paymentMethodId,
});
if (result?.status === "succeeded") {
setCart(null);
}
return result;
},
[token, selectedStoreId]
);
const itemCount = cart?.items?.reduce((sum, i) => sum + i.quantity, 0) ?? 0;
return (
<CartContext.Provider
value={{
cart,
cartLoading,
cartError,
itemCount,
selectedStoreId,
setStoreId,
addItem,
updateItem,
removeItem,
clearCart,
applyCoupon,
checkout,
refreshCart,
}}
>
{children}
</CartContext.Provider>
);
}
export function useCart() {
const ctx = useContext(CartContext);
if (!ctx) throw new Error("useCart must be used within a CartProvider");
return ctx;
}