From cc4b9ca8749e2f5b7bab61824b4fe576d69891ee Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 16 Feb 2026 04:51:34 +0000
Subject: [PATCH 1/3] Initial plan
From 092cf73b254820ea246a2c52eae59b680d352698 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 16 Feb 2026 04:54:46 +0000
Subject: [PATCH 2/3] Implement localStorage-based wishlist feature
Co-authored-by: thomasiverson <12767513+thomasiverson@users.noreply.github.com>
---
frontend/src/App.tsx | 7 +-
frontend/src/components/Navigation.tsx | 10 ++
frontend/src/components/Wishlist.tsx | 126 ++++++++++++++++++
.../components/entity/product/Products.tsx | 29 ++++
frontend/src/context/WishlistContext.tsx | 64 +++++++++
5 files changed, 235 insertions(+), 1 deletion(-)
create mode 100644 frontend/src/components/Wishlist.tsx
create mode 100644 frontend/src/context/WishlistContext.tsx
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index d0b02da..1df0c75 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 Wishlist from './components/Wishlist';
import Login from './components/Login';
import { AuthProvider } from './context/AuthContext';
import { ThemeProvider } from './context/ThemeContext';
+import { WishlistProvider } from './context/WishlistContext';
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..b4bcb8b 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 { useWishlist } from '../context/WishlistContext';
import { useState } from 'react';
export default function Navigation() {
const { isLoggedIn, isAdmin, logout } = useAuth();
const { darkMode, toggleTheme } = useTheme();
+ const { wishlistItems } = useWishlist();
const [adminMenuOpen, setAdminMenuOpen] = useState(false);
return (
@@ -29,6 +31,14 @@ export default function Navigation() {
Home
Products
+
+ Wishlist
+ {wishlistItems.length > 0 && (
+
+ {wishlistItems.length}
+
+ )}
+
About us
{isAdmin && (
diff --git a/frontend/src/components/Wishlist.tsx b/frontend/src/components/Wishlist.tsx
new file mode 100644
index 0000000..8970af5
--- /dev/null
+++ b/frontend/src/components/Wishlist.tsx
@@ -0,0 +1,126 @@
+import { useQuery } from 'react-query';
+import axios from 'axios';
+import { useWishlist } from '../context/WishlistContext';
+import { useTheme } from '../context/ThemeContext';
+import { api } from '../api/config';
+
+interface Product {
+ productId: number;
+ name: string;
+ description: string;
+ price: number;
+ imgName: string;
+ sku: string;
+ unit: string;
+ supplierId: number;
+ discount?: number;
+}
+
+const fetchProducts = async (): Promise
=> {
+ const { data } = await axios.get(`${api.baseURL}${api.endpoints.products}`);
+ return data;
+};
+
+export default function Wishlist() {
+ const { wishlistItems, removeFromWishlist } = useWishlist();
+ const { darkMode } = useTheme();
+ const { data: allProducts, isLoading } = useQuery('products', fetchProducts);
+
+ const wishlistProducts = allProducts?.filter(product =>
+ wishlistItems.includes(product.productId)
+ ) || [];
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+
+ My Wishlist
+
+
+ {wishlistProducts.length === 0 ? (
+
+
+
Your wishlist is empty
+
Add products to your wishlist to save them for later
+
+ ) : (
+
+ {wishlistProducts.map(product => (
+
+
+

+ {product.discount && (
+
+ {Math.round(product.discount * 100)}% OFF
+
+ )}
+
+
+
+
+
+ {product.name}
+
+
+ {product.description}
+
+
+ {product.discount ? (
+
+ ${product.price.toFixed(2)}
+ ${(product.price * (1 - product.discount)).toFixed(2)}
+
+ ) : (
+
${product.price.toFixed(2)}
+ )}
+
+
+
+ ))}
+
+ )}
+
+
+
+ );
+}
diff --git a/frontend/src/components/entity/product/Products.tsx b/frontend/src/components/entity/product/Products.tsx
index af2319e..463710a 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 { useWishlist } from '../../../context/WishlistContext';
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 { addToWishlist, removeFromWishlist, isInWishlist } = useWishlist();
const filteredProducts = products?.filter(product =>
product.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
@@ -53,6 +55,15 @@ export default function Products() {
}
};
+ const handleWishlistToggle = (productId: number, event: React.MouseEvent) => {
+ event.stopPropagation();
+ if (isInWishlist(productId)) {
+ removeFromWishlist(productId);
+ } else {
+ addToWishlist(productId);
+ }
+ };
+
const handleProductClick = (product: Product) => {
setSelectedProduct(product);
setShowModal(true);
@@ -125,6 +136,24 @@ export default function Products() {
{Math.round(product.discount * 100)}% OFF
)}
+
diff --git a/frontend/src/context/WishlistContext.tsx b/frontend/src/context/WishlistContext.tsx
new file mode 100644
index 0000000..56c009d
--- /dev/null
+++ b/frontend/src/context/WishlistContext.tsx
@@ -0,0 +1,64 @@
+/* eslint-disable react-refresh/only-export-components */
+import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
+
+interface WishlistContextType {
+ wishlistItems: number[];
+ addToWishlist: (productId: number) => void;
+ removeFromWishlist: (productId: number) => void;
+ isInWishlist: (productId: number) => boolean;
+}
+
+const WishlistContext = createContext(undefined);
+
+const WISHLIST_KEY = 'octocat-wishlist';
+
+export function WishlistProvider({ children }: { children: ReactNode }) {
+ const [wishlistItems, setWishlistItems] = useState(() => {
+ try {
+ const saved = localStorage.getItem(WISHLIST_KEY);
+ return saved ? JSON.parse(saved) : [];
+ } catch (error) {
+ console.error('Error loading wishlist from localStorage:', error);
+ return [];
+ }
+ });
+
+ useEffect(() => {
+ try {
+ localStorage.setItem(WISHLIST_KEY, JSON.stringify(wishlistItems));
+ } catch (error) {
+ console.error('Error saving wishlist to localStorage:', error);
+ }
+ }, [wishlistItems]);
+
+ const addToWishlist = (productId: number) => {
+ setWishlistItems(prev => {
+ if (!prev.includes(productId)) {
+ return [...prev, productId];
+ }
+ return prev;
+ });
+ };
+
+ const removeFromWishlist = (productId: number) => {
+ setWishlistItems(prev => prev.filter(id => id !== productId));
+ };
+
+ const isInWishlist = (productId: number) => {
+ return wishlistItems.includes(productId);
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useWishlist() {
+ const context = useContext(WishlistContext);
+ if (!context) {
+ throw new Error('useWishlist must be used within a WishlistProvider');
+ }
+ return context;
+}
From 3bd3d414814cdf5f7102fdab9da999a0fc200351 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 16 Feb 2026 04:58:00 +0000
Subject: [PATCH 3/3] Add focus styles for keyboard accessibility on wishlist
buttons
Co-authored-by: thomasiverson <12767513+thomasiverson@users.noreply.github.com>
---
frontend/src/components/Wishlist.tsx | 2 +-
frontend/src/components/entity/product/Products.tsx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/src/components/Wishlist.tsx b/frontend/src/components/Wishlist.tsx
index 8970af5..24bd8d5 100644
--- a/frontend/src/components/Wishlist.tsx
+++ b/frontend/src/components/Wishlist.tsx
@@ -85,7 +85,7 @@ export default function Wishlist() {
)}