From 72b28a49325fd9061df5a32eadec24b3759f887c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 30 Mar 2026 19:24:53 +0000
Subject: [PATCH 1/2] Initial plan
From 7a8a57c407a8eea80899fa5a1d6fbab684466c91 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 30 Mar 2026 19:33:18 +0000
Subject: [PATCH 2/2] Implement shopping cart with CartContext, Cart page, and
nav icon
Agent-Logs-Url: https://github.com/yortch/GitHubCopilot_Customized/sessions/569b5636-fced-4f62-8768-36b967281dad
Co-authored-by: yortch <4576246+yortch@users.noreply.github.com>
---
frontend/src/App.tsx | 7 +-
frontend/src/components/Navigation.tsx | 16 ++
frontend/src/components/entity/cart/Cart.tsx | 141 ++++++++++++++++++
.../components/entity/product/Products.tsx | 14 +-
frontend/src/context/CartContext.tsx | 82 ++++++++++
5 files changed, 257 insertions(+), 3 deletions(-)
create mode 100644 frontend/src/components/entity/cart/Cart.tsx
create mode 100644 frontend/src/context/CartContext.tsx
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index d0b02da..db4e244 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -4,9 +4,11 @@ import Welcome from './components/Welcome';
import About from './components/About';
import Footer from './components/Footer';
import Products from './components/entity/product/Products';
+import Cart from './components/entity/cart/Cart';
import Login from './components/Login';
import { AuthProvider } from './context/AuthContext';
import { ThemeProvider } from './context/ThemeContext';
+import { CartProvider } from './context/CartContext';
import AdminProducts from './components/admin/AdminProducts';
import { useTheme } from './context/ThemeContext';
@@ -23,6 +25,7 @@ function ThemedApp() {
} />
} />
} />
+ } />
} />
} />
@@ -37,7 +40,9 @@ function App() {
return (
-
+
+
+
);
diff --git a/frontend/src/components/Navigation.tsx b/frontend/src/components/Navigation.tsx
index d7b393b..0943f17 100644
--- a/frontend/src/components/Navigation.tsx
+++ b/frontend/src/components/Navigation.tsx
@@ -1,11 +1,13 @@
import { Link } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
import { useTheme } from '../context/ThemeContext';
+import { useCart } from '../context/CartContext';
import { useState } from 'react';
export default function Navigation() {
const { isLoggedIn, isAdmin, logout } = useAuth();
const { darkMode, toggleTheme } = useTheme();
+ const { totalItems } = useCart();
const [adminMenuOpen, setAdminMenuOpen] = useState(false);
return (
@@ -68,6 +70,20 @@ export default function Navigation() {
+
+
+ {totalItems > 0 && (
+
+ {totalItems > 99 ? '99+' : totalItems}
+
+ )}
+
+ );
+ }
+
+ return (
+
+
+
+
+ Your Cart
+
+
+
+
+
+ {items.map((item: CartItem) => {
+ const effectivePrice = item.discount ? item.price * (1 - item.discount) : item.price;
+ return (
+
+

+
+
+ {item.name}
+
+
+ ${effectivePrice.toFixed(2)}
+ {item.discount && (
+
+ ${item.price.toFixed(2)}
+
+ )}
+
+
+
+
+
+
+ {item.quantity}
+
+
+
+
+ ${(effectivePrice * item.quantity).toFixed(2)}
+
+
+
+
+ );
+ })}
+
+
+ {/* Order Summary */}
+
+
+ Order Summary
+
+
+ Total
+ ${totalPrice.toFixed(2)}
+
+
+
+ Continue Shopping
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/entity/product/Products.tsx b/frontend/src/components/entity/product/Products.tsx
index af2319e..2dba5d9 100644
--- a/frontend/src/components/entity/product/Products.tsx
+++ b/frontend/src/components/entity/product/Products.tsx
@@ -3,6 +3,7 @@ import axios from 'axios';
import { useQuery } from 'react-query';
import { api } from '../../../api/config';
import { useTheme } from '../../../context/ThemeContext';
+import { useCart } from '../../../context/CartContext';
interface Product {
productId: number;
@@ -28,6 +29,7 @@ export default function Products() {
const [showModal, setShowModal] = useState(false);
const { data: products, isLoading, error } = useQuery('products', fetchProducts);
const { darkMode } = useTheme();
+ const { addToCart } = useCart();
const filteredProducts = products?.filter(product =>
product.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
@@ -44,8 +46,16 @@ export default function Products() {
const handleAddToCart = (productId: number) => {
const quantity = quantities[productId] || 0;
if (quantity > 0) {
- // TODO: Implement cart functionality
- alert(`Added ${quantity} items to cart`);
+ const product = products?.find(p => p.productId === productId);
+ if (product) {
+ addToCart({
+ productId: product.productId,
+ name: product.name,
+ price: product.price,
+ imgName: product.imgName,
+ discount: product.discount,
+ }, quantity);
+ }
setQuantities(prev => ({
...prev,
[productId]: 0
diff --git a/frontend/src/context/CartContext.tsx b/frontend/src/context/CartContext.tsx
new file mode 100644
index 0000000..bdcda1b
--- /dev/null
+++ b/frontend/src/context/CartContext.tsx
@@ -0,0 +1,82 @@
+/* eslint-disable react-refresh/only-export-components */
+import { createContext, useContext, useState, useCallback, useMemo, ReactNode } from 'react';
+
+export interface CartItem {
+ productId: number;
+ name: string;
+ price: number;
+ imgName: string;
+ quantity: number;
+ discount?: number;
+}
+
+interface CartContextType {
+ items: CartItem[];
+ addToCart: (item: Omit, quantity: number) => void;
+ removeFromCart: (productId: number) => void;
+ updateQuantity: (productId: number, quantity: number) => void;
+ clearCart: () => void;
+ totalItems: number;
+ totalPrice: number;
+}
+
+const CartContext = createContext(null);
+
+export function CartProvider({ children }: { children: ReactNode }) {
+ const [items, setItems] = useState([]);
+
+ const addToCart = useCallback((product: Omit, quantity: number) => {
+ setItems(prev => {
+ const existing = prev.find(i => i.productId === product.productId);
+ if (existing) {
+ return prev.map(i =>
+ i.productId === product.productId
+ ? { ...i, quantity: i.quantity + quantity }
+ : i
+ );
+ }
+ return [...prev, { ...product, quantity }];
+ });
+ }, []);
+
+ const removeFromCart = useCallback((productId: number) => {
+ setItems(prev => prev.filter(i => i.productId !== productId));
+ }, []);
+
+ const updateQuantity = useCallback((productId: number, quantity: number) => {
+ if (quantity <= 0) {
+ setItems(prev => prev.filter(i => i.productId !== productId));
+ return;
+ }
+ setItems(prev =>
+ prev.map(i => (i.productId === productId ? { ...i, quantity } : i))
+ );
+ }, []);
+
+ const clearCart = useCallback(() => setItems([]), []);
+
+ const totalItems = useMemo(() => items.reduce((sum, i) => sum + i.quantity, 0), [items]);
+ const totalPrice = useMemo(() => items.reduce((sum, i) => {
+ const effectivePrice = i.discount ? i.price * (1 - i.discount) : i.price;
+ return sum + effectivePrice * i.quantity;
+ }, 0), [items]);
+
+ const value = useMemo(
+ () => ({ items, addToCart, removeFromCart, updateQuantity, clearCart, totalItems, totalPrice }),
+ [items, addToCart, removeFromCart, updateQuantity, clearCart, totalItems, totalPrice]
+ );
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useCart() {
+ const context = useContext(CartContext);
+ if (!context) {
+ throw new Error('useCart must be used within a CartProvider');
+ }
+ return context;
+}