diff --git a/api/src/index.ts b/api/src/index.ts
index b991a16..0fa96c2 100644
--- a/api/src/index.ts
+++ b/api/src/index.ts
@@ -10,6 +10,7 @@ import orderRoutes from './routes/order';
import branchRoutes from './routes/branch';
import headquartersRoutes from './routes/headquarters';
import supplierRoutes from './routes/supplier';
+import wishlistRoutes from './routes/wishlist';
const app = express();
const port = process.env.PORT || 3000;
@@ -74,6 +75,7 @@ app.use('/api/orders', orderRoutes);
app.use('/api/branches', branchRoutes);
app.use('/api/headquarters', headquartersRoutes);
app.use('/api/suppliers', supplierRoutes);
+app.use('/api/wishlist', wishlistRoutes);
app.get('/', (req, res) => {
res.send('Hello, world!');
diff --git a/api/src/models/wishlist.ts b/api/src/models/wishlist.ts
new file mode 100644
index 0000000..6d1c61f
--- /dev/null
+++ b/api/src/models/wishlist.ts
@@ -0,0 +1,27 @@
+/**
+ * @swagger
+ * components:
+ * schemas:
+ * WishlistItem:
+ * type: object
+ * required:
+ * - userId
+ * - productId
+ * - addedAt
+ * properties:
+ * userId:
+ * type: string
+ * description: The user's email address
+ * productId:
+ * type: integer
+ * description: The unique identifier of the product
+ * addedAt:
+ * type: string
+ * format: date-time
+ * description: ISO date string when the item was added
+ */
+export interface WishlistItem {
+ userId: string;
+ productId: number;
+ addedAt: string;
+}
diff --git a/api/src/routes/wishlist.test.ts b/api/src/routes/wishlist.test.ts
new file mode 100644
index 0000000..86b3af2
--- /dev/null
+++ b/api/src/routes/wishlist.test.ts
@@ -0,0 +1,92 @@
+import { describe, it, expect, beforeEach } from 'vitest';
+import request from 'supertest';
+import express from 'express';
+import wishlistRouter, { resetWishlist } from './wishlist';
+
+let app: express.Express;
+
+describe('Wishlist API', () => {
+ beforeEach(() => {
+ app = express();
+ app.use(express.json());
+ app.use('/api/wishlist', wishlistRouter);
+ resetWishlist();
+ });
+
+ it('should return 400 when userId is missing on GET', async () => {
+ const response = await request(app).get('/api/wishlist');
+ expect(response.status).toBe(400);
+ expect(response.body.error).toBeDefined();
+ });
+
+ it('should return empty array for a user with no wishlist items', async () => {
+ const response = await request(app).get('/api/wishlist?userId=test@example.com');
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual([]);
+ });
+
+ it('should add a product to the wishlist', async () => {
+ const response = await request(app)
+ .post('/api/wishlist')
+ .send({ userId: 'test@example.com', productId: 1 });
+ expect(response.status).toBe(201);
+ expect(response.body.userId).toBe('test@example.com');
+ expect(response.body.productId).toBe(1);
+ expect(response.body.addedAt).toBeDefined();
+ });
+
+ it('should return 409 when adding a duplicate wishlist item', async () => {
+ await request(app)
+ .post('/api/wishlist')
+ .send({ userId: 'test@example.com', productId: 1 });
+ const response = await request(app)
+ .post('/api/wishlist')
+ .send({ userId: 'test@example.com', productId: 1 });
+ expect(response.status).toBe(409);
+ expect(response.body.error).toBeDefined();
+ });
+
+ it('should retrieve wishlist items for a user', async () => {
+ await request(app)
+ .post('/api/wishlist')
+ .send({ userId: 'test@example.com', productId: 1 });
+ await request(app)
+ .post('/api/wishlist')
+ .send({ userId: 'test@example.com', productId: 2 });
+
+ const response = await request(app).get('/api/wishlist?userId=test@example.com');
+ expect(response.status).toBe(200);
+ expect(response.body.length).toBe(2);
+ });
+
+ it('should only return items for the specified user', async () => {
+ await request(app)
+ .post('/api/wishlist')
+ .send({ userId: 'user1@example.com', productId: 1 });
+ await request(app)
+ .post('/api/wishlist')
+ .send({ userId: 'user2@example.com', productId: 2 });
+
+ const response = await request(app).get('/api/wishlist?userId=user1@example.com');
+ expect(response.status).toBe(200);
+ expect(response.body.length).toBe(1);
+ expect(response.body[0].userId).toBe('user1@example.com');
+ });
+
+ it('should delete a wishlist item', async () => {
+ await request(app)
+ .post('/api/wishlist')
+ .send({ userId: 'test@example.com', productId: 1 });
+
+ const response = await request(app)
+ .delete('/api/wishlist/1?userId=test@example.com');
+ expect(response.status).toBe(204);
+ });
+
+ it('should return 404 when deleting a non-existing wishlist item', async () => {
+ const response = await request(app)
+ .delete('/api/wishlist/999?userId=test@example.com');
+ expect(response.status).toBe(404);
+ expect(response.body.error).toBeDefined();
+ });
+});
diff --git a/api/src/routes/wishlist.ts b/api/src/routes/wishlist.ts
new file mode 100644
index 0000000..3d4631f
--- /dev/null
+++ b/api/src/routes/wishlist.ts
@@ -0,0 +1,141 @@
+/**
+ * @swagger
+ * tags:
+ * name: Wishlist
+ * description: API endpoints for managing user wishlists
+ */
+
+/**
+ * @swagger
+ * /api/wishlist:
+ * get:
+ * summary: Get wishlist items for a user
+ * tags: [Wishlist]
+ * parameters:
+ * - in: query
+ * name: userId
+ * required: true
+ * schema:
+ * type: string
+ * description: The user's email address
+ * responses:
+ * 200:
+ * description: List of wishlist items for the user
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/WishlistItem'
+ * 400:
+ * description: Missing userId query parameter
+ * post:
+ * summary: Add a product to the wishlist
+ * tags: [Wishlist]
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - userId
+ * - productId
+ * properties:
+ * userId:
+ * type: string
+ * description: The user's email address
+ * productId:
+ * type: integer
+ * description: The product ID to add
+ * responses:
+ * 201:
+ * description: Wishlist item added successfully
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/WishlistItem'
+ * 409:
+ * description: Item already exists in wishlist
+ *
+ * /api/wishlist/{productId}:
+ * delete:
+ * summary: Remove a product from the wishlist
+ * tags: [Wishlist]
+ * parameters:
+ * - in: path
+ * name: productId
+ * required: true
+ * schema:
+ * type: integer
+ * description: The product ID to remove
+ * - in: query
+ * name: userId
+ * required: true
+ * schema:
+ * type: string
+ * description: The user's email address
+ * responses:
+ * 204:
+ * description: Wishlist item removed successfully
+ * 404:
+ * description: Wishlist item not found
+ */
+
+import express from 'express';
+import { WishlistItem } from '../models/wishlist';
+
+const router = express.Router();
+
+let wishlistItems: WishlistItem[] = [];
+
+export const resetWishlist = () => {
+ wishlistItems = [];
+};
+
+// Get wishlist items for a user
+router.get('/', (req, res) => {
+ const { userId } = req.query;
+ if (!userId) {
+ res.status(400).json({ error: 'Missing userId query parameter' });
+ } else {
+ const items = wishlistItems.filter(item => item.userId === userId);
+ res.json(items);
+ }
+});
+
+// Add a product to the wishlist
+router.post('/', (req, res) => {
+ const { userId, productId } = req.body;
+ const existing = wishlistItems.find(
+ item => item.userId === userId && item.productId === productId
+ );
+ if (existing) {
+ res.status(409).json({ error: 'Item already exists in wishlist' });
+ } else {
+ const newItem: WishlistItem = {
+ userId,
+ productId,
+ addedAt: new Date().toISOString(),
+ };
+ wishlistItems.push(newItem);
+ res.status(201).json(newItem);
+ }
+});
+
+// Remove a product from the wishlist
+router.delete('/:productId', (req, res) => {
+ const { userId } = req.query;
+ const productId = parseInt(req.params.productId);
+ const index = wishlistItems.findIndex(
+ item => item.userId === userId && item.productId === productId
+ );
+ if (index !== -1) {
+ wishlistItems.splice(index, 1);
+ res.status(204).send();
+ } else {
+ res.status(404).json({ error: 'Wishlist item not found' });
+ }
+});
+
+export default router;
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index d0b02da..99a2a14 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -9,6 +9,8 @@ import { AuthProvider } from './context/AuthContext';
import { ThemeProvider } from './context/ThemeContext';
import AdminProducts from './components/admin/AdminProducts';
import { useTheme } from './context/ThemeContext';
+import { WishlistProvider } from './context/WishlistContext';
+import Wishlist from './components/wishlist/Wishlist';
// Wrapper component to apply theme classes
function ThemedApp() {
@@ -24,6 +26,7 @@ function ThemedApp() {