diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index d0b02da..cad836a 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -7,7 +7,9 @@ import Products from './components/entity/product/Products';
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 Wishlist from './components/wishlist/Wishlist';
import { useTheme } from './context/ThemeContext';
// Wrapper component to apply theme classes
@@ -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..921a40e 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 { wishlistCount } = useWishlist();
const [adminMenuOpen, setAdminMenuOpen] = useState(false);
return (
@@ -30,6 +32,19 @@ export default function Navigation() {
Home
Products
About us
+ {isLoggedIn && (
+
+ Wishlist
+ {wishlistCount > 0 && (
+
+ {wishlistCount}
+
+ )}
+
+ )}
{isAdmin && (
)}
+
diff --git a/frontend/src/components/wishlist/Wishlist.tsx b/frontend/src/components/wishlist/Wishlist.tsx
new file mode 100644
index 0000000..d0cc42c
--- /dev/null
+++ b/frontend/src/components/wishlist/Wishlist.tsx
@@ -0,0 +1,117 @@
+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;
+ 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 { darkMode } = useTheme();
+ const { wishlistIds, toggleWishlist, isWishlisted } = useWishlist();
+ const { data: products, isLoading, error } = useQuery('products', fetchProducts);
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+
Failed to fetch products
+
+
+ );
+ }
+
+ const wishlistedProducts = products?.filter(p => wishlistIds.has(p.productId)) ?? [];
+
+ return (
+
+
+
+
Wishlist
+
+ {wishlistedProducts.length === 0 ? (
+
+ Your wishlist is empty — browse products to save items for later
+
+ ) : (
+
+ {wishlistedProducts.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/context/WishlistContext.tsx b/frontend/src/context/WishlistContext.tsx
new file mode 100644
index 0000000..f116efd
--- /dev/null
+++ b/frontend/src/context/WishlistContext.tsx
@@ -0,0 +1,54 @@
+import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
+
+interface WishlistContextType {
+ wishlistIds: Set;
+ toggleWishlist: (productId: number) => void;
+ isWishlisted: (productId: number) => boolean;
+ wishlistCount: number;
+}
+
+const WishlistContext = createContext(null);
+
+export function WishlistProvider({ children }: { children: ReactNode }) {
+ const [wishlistIds, setWishlistIds] = useState>(() => {
+ try {
+ const stored = localStorage.getItem('wishlist');
+ return stored ? new Set(JSON.parse(stored)) : new Set();
+ } catch {
+ return new Set();
+ }
+ });
+
+ useEffect(() => {
+ localStorage.setItem('wishlist', JSON.stringify(Array.from(wishlistIds)));
+ }, [wishlistIds]);
+
+ const toggleWishlist = (productId: number) => {
+ setWishlistIds(prev => {
+ const next = new Set(prev);
+ if (next.has(productId)) {
+ next.delete(productId);
+ } else {
+ next.add(productId);
+ }
+ return next;
+ });
+ };
+
+ const isWishlisted = (productId: number) => wishlistIds.has(productId);
+
+ return (
+
+ {children}
+
+ );
+}
+
+// eslint-disable-next-line react-refresh/only-export-components
+export function useWishlist() {
+ const context = useContext(WishlistContext);
+ if (!context) {
+ throw new Error('useWishlist must be used within a WishlistProvider');
+ }
+ return context;
+}