Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e78cf18409 | |||
| 5959806bf3 | |||
| 0e26d3a073 | |||
| 28f4f84e0c | |||
| 2041dd8b8a | |||
| 6fd75d201e | |||
| 484bffd95d | |||
| 348f55c5ba |
305
src/app/checkout/page.tsx
Normal file
305
src/app/checkout/page.tsx
Normal file
@@ -0,0 +1,305 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import ReactLenis from "lenis/react";
|
||||
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
|
||||
import FooterBaseReveal from '@/components/sections/footer/FooterBaseReveal';
|
||||
import Input from '@/components/form/Input';
|
||||
import ButtonHoverBubble from '@/components/button/ButtonHoverBubble';
|
||||
import { useState } from "react";
|
||||
|
||||
export default function CheckoutPage() {
|
||||
const [shippingInfo, setShippingInfo] = useState({
|
||||
fullName: "", address: "", city: "", state: "", zipCode: "", country: ""
|
||||
});
|
||||
|
||||
const [paymentInfo, setPaymentInfo] = useState({
|
||||
cardNumber: "", cardName: "", expiryDate: "", cvv: ""
|
||||
});
|
||||
|
||||
const [orderConfirmed, setOrderConfirmed] = useState(false);
|
||||
|
||||
const handleShippingChange = (field: string, value: string) => {
|
||||
setShippingInfo((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handlePaymentChange = (field: string, value: string) => {
|
||||
setPaymentInfo((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handlePlaceOrder = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
// Simulate API call for order placement
|
||||
console.log("Placing order with:", shippingInfo, paymentInfo);
|
||||
// In a real app, you'd send this to an API endpoint
|
||||
const response = await fetch('/api/checkout', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ shippingInfo, paymentInfo, items: [] }) // Add actual cart items
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
setOrderConfirmed(true);
|
||||
console.log("Order placed successfully!");
|
||||
// Optionally clear cart or redirect
|
||||
} else {
|
||||
console.error("Order failed:", await response.json());
|
||||
alert("Order placement failed. Please try again.");
|
||||
}
|
||||
};
|
||||
|
||||
// Dummy order summary data
|
||||
const orderItems = [
|
||||
{ id: "prod-1", name: "Luxury Velocity Runner", price: 12500, quantity: 1 },
|
||||
{ id: "prod-4", name: "Aura X Wireless Earbuds", price: 8999, quantity: 1 }
|
||||
];
|
||||
const subtotal = orderItems.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
||||
const shippingCost = 500; // Example
|
||||
const total = subtotal + shippingCost;
|
||||
|
||||
|
||||
if (orderConfirmed) {
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="shift-hover"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="rounded"
|
||||
contentWidth="small"
|
||||
sizing="mediumSizeLargeTitles"
|
||||
background="blurBottom"
|
||||
cardStyle="inset"
|
||||
primaryButtonStyle="double-inset"
|
||||
secondaryButtonStyle="layered"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<ReactLenis root>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleCentered
|
||||
navItems={[
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Shop", id: "/shop" },
|
||||
{ name: "Categories", id: "/categories" },
|
||||
{ name: "Wishlist", id: "/wishlist" },
|
||||
{ name: "About", id: "/about" },
|
||||
{ name: "Contact", id: "/contact" },
|
||||
{ name: "Checkout", id: "/checkout" }
|
||||
]}
|
||||
button={{ text: "Admin Login", href: "/admin" }}
|
||||
brandName="SHOEMOB"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<main className="min-h-screen py-20 flex flex-col items-center justify-center text-center">
|
||||
<h1 className="text-4xl font-bold mb-4">Order Confirmed!</h1>
|
||||
<p className="text-xl mb-8">Thank you for your purchase. Your order has been successfully placed.</p>
|
||||
<ButtonHoverBubble text="Continue Shopping" href="/shop" />
|
||||
</main>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterBaseReveal
|
||||
columns={[
|
||||
{
|
||||
title: "Shop", items: [
|
||||
{ label: "Luxury Sneakers", href: "/shop?category=luxury-sneakers" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="shift-hover"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="rounded"
|
||||
contentWidth="small"
|
||||
sizing="mediumSizeLargeTitles"
|
||||
background="blurBottom"
|
||||
cardStyle="inset"
|
||||
primaryButtonStyle="double-inset"
|
||||
secondaryButtonStyle="layered"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<ReactLenis root>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleCentered
|
||||
navItems={[
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Shop", id: "/shop" },
|
||||
{ name: "Categories", id: "/categories" },
|
||||
{ name: "Wishlist", id: "/wishlist" },
|
||||
{ name: "About", id: "/about" },
|
||||
{ name: "Contact", id: "/contact" },
|
||||
{ name: "Checkout", id: "/checkout" } // Added checkout link
|
||||
]}
|
||||
button={{ text: "Admin Login", href: "/admin" }}
|
||||
brandName="SHOEMOB"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<main className="min-h-screen py-20 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h1 className="text-4xl font-bold text-center mb-12">Checkout</h1>
|
||||
|
||||
<form onSubmit={handlePlaceOrder} className="grid md:grid-cols-2 gap-12">
|
||||
{/* Shipping Information */}
|
||||
<div className="bg-card p-8 rounded-lg shadow-lg">
|
||||
<h2 className="text-2xl font-semibold mb-6">Shipping Information</h2>
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<Input
|
||||
value={shippingInfo.fullName}
|
||||
onChange={(val) => handleShippingChange("fullName", val)}
|
||||
placeholder="Full Name"
|
||||
required
|
||||
ariaLabel="Full Name"
|
||||
/>
|
||||
<Input
|
||||
value={shippingInfo.address}
|
||||
onChange={(val) => handleShippingChange("address", val)}
|
||||
placeholder="Address"
|
||||
required
|
||||
ariaLabel="Address"
|
||||
/>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Input
|
||||
value={shippingInfo.city}
|
||||
onChange={(val) => handleShippingChange("city", val)}
|
||||
placeholder="City"
|
||||
required
|
||||
ariaLabel="City"
|
||||
/>
|
||||
<Input
|
||||
value={shippingInfo.state}
|
||||
onChange={(val) => handleShippingChange("state", val)}
|
||||
placeholder="State/Province"
|
||||
required
|
||||
ariaLabel="State/Province"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Input
|
||||
value={shippingInfo.zipCode}
|
||||
onChange={(val) => handleShippingChange("zipCode", val)}
|
||||
placeholder="Zip Code"
|
||||
required
|
||||
ariaLabel="Zip Code"
|
||||
/>
|
||||
<Input
|
||||
value={shippingInfo.country}
|
||||
onChange={(val) => handleShippingChange("country", val)}
|
||||
placeholder="Country"
|
||||
required
|
||||
ariaLabel="Country"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Order Summary & Payment */}
|
||||
<div className="flex flex-col gap-12">
|
||||
{/* Order Summary */}
|
||||
<div className="bg-card p-8 rounded-lg shadow-lg">
|
||||
<h2 className="text-2xl font-semibold mb-6">Order Summary</h2>
|
||||
<div className="space-y-4">
|
||||
{orderItems.map((item) => (
|
||||
<div key={item.id} className="flex justify-between items-center text-foreground">
|
||||
<span>{item.name} (x{item.quantity})</span>
|
||||
<span>₹{(item.price * item.quantity).toLocaleString()}</span>
|
||||
</div>
|
||||
))}
|
||||
<div className="pt-4 border-t border-accent/20">
|
||||
<div className="flex justify-between text-foreground">
|
||||
<span>Subtotal</span>
|
||||
<span>₹{subtotal.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-foreground">
|
||||
<span>Shipping</span>
|
||||
<span>₹{shippingCost.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex justify-between font-bold text-xl mt-4 text-primary-cta">
|
||||
<span>Total</span>
|
||||
<span>₹{total.toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Payment Information */}
|
||||
<div className="bg-card p-8 rounded-lg shadow-lg">
|
||||
<h2 className="text-2xl font-semibold mb-6">Payment Information</h2>
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<Input
|
||||
value={paymentInfo.cardName}
|
||||
onChange={(val) => handlePaymentChange("cardName", val)}
|
||||
placeholder="Name on Card"
|
||||
required
|
||||
ariaLabel="Name on Card"
|
||||
/>
|
||||
<Input
|
||||
value={paymentInfo.cardNumber}
|
||||
onChange={(val) => handlePaymentChange("cardNumber", val)}
|
||||
placeholder="Card Number"
|
||||
type="text"
|
||||
|
||||
|
||||
required
|
||||
ariaLabel="Card Number"
|
||||
/>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Input
|
||||
value={paymentInfo.expiryDate}
|
||||
onChange={(val) => handlePaymentChange("expiryDate", val)}
|
||||
placeholder="MM/YY"
|
||||
type="text"
|
||||
// MM/YY pattern
|
||||
required
|
||||
ariaLabel="Expiry Date"
|
||||
/>
|
||||
<Input
|
||||
value={paymentInfo.cvv}
|
||||
onChange={(val) => handlePaymentChange("cvv", val)}
|
||||
placeholder="CVV"
|
||||
type="text"
|
||||
// CVV pattern
|
||||
required
|
||||
ariaLabel="CVV"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Place Order Button */}
|
||||
<div className="text-center">
|
||||
<ButtonHoverBubble
|
||||
text="Place Order"
|
||||
type="submit"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterBaseReveal
|
||||
columns={[
|
||||
{
|
||||
title: "Shop", items: [
|
||||
{ label: "Luxury Sneakers", href: "/shop?category=luxury-sneakers" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -40,6 +40,8 @@ export default function LandingPage() {
|
||||
name: "About", id: "/about"},
|
||||
{
|
||||
name: "Contact", id: "/contact"},
|
||||
{
|
||||
name: "My Account", id: "/user/dashboard"}
|
||||
]}
|
||||
button={{
|
||||
text: "Admin Login", href: "/admin"}}
|
||||
@@ -163,4 +165,4 @@ export default function LandingPage() {
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
98
src/app/products/[productId]/page.tsx
Normal file
98
src/app/products/[productId]/page.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import ReactLenis from "lenis/react";
|
||||
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
|
||||
import FooterBaseReveal from '@/components/sections/footer/FooterBaseReveal';
|
||||
import ProductDetailCard from '@/components/ecommerce/productDetail/ProductDetailCard';
|
||||
import { Star } from 'lucide-react';
|
||||
|
||||
export default function ProductDetailPage({ params }: { params: { productId: string } }) {
|
||||
// In a real application, you would fetch product data based on params.productId
|
||||
const productData = {
|
||||
id: params.productId,
|
||||
name: "Luxury Velocity Runner", price: "₹12,500", description: "Experience unparalleled comfort and style with our Luxury Velocity Runner. Crafted from premium materials, these sneakers offer superior cushioning and dynamic support for your everyday adventures. Featuring a sleek design and advanced sole technology, they are perfect for both athletic performance and casual wear. Available in multiple sizes and limited-edition colors.", images: [
|
||||
{ src: "http://img.b2bpic.net/free-photo/view-white-ice-skates-with-cherries-tangerines_23-2150806813.jpg", alt: "Luxury Velocity Runner front view" },
|
||||
{ src: "http://img.b2bpic.net/free-photo/white-sneakers-with-red-sole-against-white-background_23-2147746592.jpg", alt: "Luxury Velocity Runner side view" },
|
||||
{ src: "http://img.b2bpic.net/free-photo/pair-white-sneakers_23-2147746591.jpg", alt: "Luxury Velocity Runner top view" }
|
||||
],
|
||||
variants: [
|
||||
{
|
||||
label: "Size", options: ["US 7", "US 8", "US 9", "US 10", "US 11", "US 12"],
|
||||
selected: "US 9", onChange: (value: string) => console.log("Selected size:", value)
|
||||
},
|
||||
{
|
||||
label: "Color", options: ["White", "Black", "Grey", "Blue"],
|
||||
selected: "White", onChange: (value: string) => console.log("Selected color:", value)
|
||||
}
|
||||
],
|
||||
quantity: {
|
||||
label: "Quantity", options: ["1", "2", "3", "4", "5"],
|
||||
selected: "1", onChange: (value: string) => console.log("Selected quantity:", value)
|
||||
},
|
||||
rating: 4.5,
|
||||
stock: 15
|
||||
};
|
||||
|
||||
const navItems = [
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Shop", id: "/products/prod-1" }
|
||||
];
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="shift-hover"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="rounded"
|
||||
contentWidth="small"
|
||||
sizing="mediumSizeLargeTitles"
|
||||
background="blurBottom"
|
||||
cardStyle="inset"
|
||||
primaryButtonStyle="double-inset"
|
||||
secondaryButtonStyle="layered"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<ReactLenis root>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleCentered
|
||||
navItems={navItems}
|
||||
button={{
|
||||
text: "Admin Login", href: "/admin"
|
||||
}}
|
||||
brandName="SHOEMOB"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="product-detail" data-section="product-detail">
|
||||
<ProductDetailCard
|
||||
layout="page"
|
||||
name={productData.name}
|
||||
price={productData.price}
|
||||
description={`${productData.description} In stock: ${productData.stock} units.`}
|
||||
images={productData.images}
|
||||
variants={productData.variants}
|
||||
quantity={productData.quantity}
|
||||
showRating={true}
|
||||
rating={productData.rating}
|
||||
ratingIcon={Star}
|
||||
buttons={[
|
||||
{ text: "Buy Now", onClick: () => alert(`Buying ${productData.name} (ID: ${productData.id})`) }
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterBaseReveal
|
||||
columns={[
|
||||
{
|
||||
title: "Shop", items: [
|
||||
{ label: "Luxury Sneakers", href: "/products/prod-1" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
74
src/app/user/dashboard/page.tsx
Normal file
74
src/app/user/dashboard/page.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import ReactLenis from "lenis/react";
|
||||
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
|
||||
import FooterBaseReveal from '@/components/sections/footer/FooterBaseReveal';
|
||||
|
||||
export default function UserDashboardPage() {
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="shift-hover"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="rounded"
|
||||
contentWidth="small"
|
||||
sizing="mediumSizeLargeTitles"
|
||||
background="blurBottom"
|
||||
cardStyle="inset"
|
||||
primaryButtonStyle="double-inset"
|
||||
secondaryButtonStyle="layered"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<ReactLenis root>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleCentered
|
||||
navItems={[
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Shop", id: "/shop" },
|
||||
{ name: "Categories", id: "/categories" },
|
||||
{ name: "Wishlist", id: "/wishlist" },
|
||||
{ name: "About", id: "/about" },
|
||||
{ name: "Contact", id: "/contact" },
|
||||
{ name: "My Account", id: "/user/dashboard" }
|
||||
]}
|
||||
button={{ text: "Admin Login", href: "/admin" }}
|
||||
brandName="SHOEMOB"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<main className="container mx-auto px-4 py-16">
|
||||
<h1 className="text-4xl font-semibold mb-8">My Account & Order History</h1>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<section className="bg-card p-6 rounded-lg shadow-md">
|
||||
<h2 className="text-2xl font-medium mb-4">Order History</h2>
|
||||
<p>No orders found. Start shopping today!</p>
|
||||
{/* Placeholder for order list */}
|
||||
</section>
|
||||
<section className="bg-card p-6 rounded-lg shadow-md">
|
||||
<h2 className="text-2xl font-medium mb-4">Account Details</h2>
|
||||
<p>Manage your profile, addresses, and payment methods here.</p>
|
||||
{/* Placeholder for account details */}
|
||||
</section>
|
||||
<section className="bg-card p-6 rounded-lg shadow-md md:col-span-2">
|
||||
<h2 className="text-2xl font-medium mb-4">Order Tracking</h2>
|
||||
<p>Enter your order number to track your shipment.</p>
|
||||
{/* Placeholder for tracking input and status */}
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterBaseReveal
|
||||
columns={[
|
||||
{
|
||||
title: "Shop", items: [
|
||||
{ label: "Luxury Sneakers", href: "/shop?category=luxury-sneakers" },
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
106
src/context/CartContext.tsx
Normal file
106
src/context/CartContext.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||
|
||||
interface CartItem {
|
||||
id: string;
|
||||
name: string;
|
||||
price: number; // Store price as number for calculations
|
||||
imageSrc: string;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
interface CartContextType {
|
||||
cartItems: CartItem[];
|
||||
addToCart: (item: Omit<CartItem, 'quantity'>) => void;
|
||||
removeFromCart: (id: string) => void;
|
||||
updateQuantity: (id: string, quantity: number) => void;
|
||||
clearCart: () => void;
|
||||
cartTotal: number;
|
||||
}
|
||||
|
||||
const CartContext = createContext<CartContextType | undefined>(undefined);
|
||||
|
||||
export const CartProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [cartItems, setCartItems] = useState<CartItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
// Load cart from localStorage on mount
|
||||
if (typeof window !== 'undefined') {
|
||||
const storedCart = localStorage.getItem('cart');
|
||||
if (storedCart) {
|
||||
setCartItems(JSON.parse(storedCart));
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Save cart to localStorage whenever it changes
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('cart', JSON.stringify(cartItems));
|
||||
}
|
||||
}, [cartItems]);
|
||||
|
||||
const addToCart = (item: Omit<CartItem, 'quantity'>) => {
|
||||
setCartItems((prevItems) => {
|
||||
const existingItem = prevItems.find((cartItem) => cartItem.id === item.id);
|
||||
if (existingItem) {
|
||||
return prevItems.map((cartItem) =>
|
||||
cartItem.id === item.id
|
||||
? { ...cartItem, quantity: cartItem.quantity + 1 }
|
||||
: cartItem
|
||||
);
|
||||
} else {
|
||||
return [...prevItems, { ...item, quantity: 1 }];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const removeFromCart = (id: string) => {
|
||||
setCartItems((prevItems) => prevItems.filter((item) => item.id !== id));
|
||||
};
|
||||
|
||||
const updateQuantity = (id: string, quantity: number) => {
|
||||
if (quantity <= 0) {
|
||||
removeFromCart(id);
|
||||
return;
|
||||
}
|
||||
setCartItems((prevItems) =>
|
||||
prevItems.map((item) =>
|
||||
item.id === id ? { ...item, quantity: quantity } : item
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const clearCart = () => {
|
||||
setCartItems([]);
|
||||
};
|
||||
|
||||
const cartTotal = cartItems.reduce(
|
||||
(total, item) => total + item.price * item.quantity,
|
||||
0
|
||||
);
|
||||
|
||||
return (
|
||||
<CartContext.Provider
|
||||
value={{
|
||||
cartItems,
|
||||
addToCart,
|
||||
removeFromCart,
|
||||
updateQuantity,
|
||||
clearCart,
|
||||
cartTotal,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useCart = () => {
|
||||
const context = useContext(CartContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useCart must be used within a CartProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
Reference in New Issue
Block a user