From 9cbb7676f2de3be041c007bf28e081464bcff9e4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 16 Feb 2026 04:47:34 +0000
Subject: [PATCH 1/4] Initial plan
From 2f25cdaebd216ba94bd2e8208c9add20d6280095 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:50 +0000
Subject: [PATCH 2/4] Complete backend implementation: user model, routes,
tests, and seed data
Co-authored-by: thomasiverson <12767513+thomasiverson@users.noreply.github.com>
---
api/src/index.ts | 2 +
api/src/models/user.ts | 44 +++++
api/src/routes/user.test.ts | 277 +++++++++++++++++++++++++++
api/src/routes/user.ts | 361 ++++++++++++++++++++++++++++++++++++
api/src/seedData.ts | 29 +++
5 files changed, 713 insertions(+)
create mode 100644 api/src/models/user.ts
create mode 100644 api/src/routes/user.test.ts
create mode 100644 api/src/routes/user.ts
diff --git a/api/src/index.ts b/api/src/index.ts
index b991a16..a372f97 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 userRoutes from './routes/user';
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/users', userRoutes);
app.get('/', (req, res) => {
res.send('Hello, world!');
diff --git a/api/src/models/user.ts b/api/src/models/user.ts
new file mode 100644
index 0000000..d745fa1
--- /dev/null
+++ b/api/src/models/user.ts
@@ -0,0 +1,44 @@
+/**
+ * @swagger
+ * components:
+ * schemas:
+ * User:
+ * type: object
+ * required:
+ * - userId
+ * - email
+ * - name
+ * properties:
+ * userId:
+ * type: integer
+ * description: The unique identifier for the user
+ * email:
+ * type: string
+ * format: email
+ * description: The user's email address (must be unique)
+ * name:
+ * type: string
+ * description: The user's full name
+ * isAdmin:
+ * type: boolean
+ * description: Whether the user has admin privileges
+ * default: false
+ * createdAt:
+ * type: string
+ * format: date-time
+ * description: The date and time the user was created
+ * wishlistProductIds:
+ * type: array
+ * items:
+ * type: integer
+ * description: Array of product IDs in the user's wishlist
+ * default: []
+ */
+export interface User {
+ userId: number;
+ email: string;
+ name: string;
+ isAdmin: boolean;
+ createdAt: Date;
+ wishlistProductIds: number[];
+}
diff --git a/api/src/routes/user.test.ts b/api/src/routes/user.test.ts
new file mode 100644
index 0000000..e820a48
--- /dev/null
+++ b/api/src/routes/user.test.ts
@@ -0,0 +1,277 @@
+import { describe, it, expect, beforeEach } from 'vitest';
+import request from 'supertest';
+import express from 'express';
+import userRouter, { resetUsers } from './user';
+import { users as seedUsers } from '../seedData';
+
+let app: express.Express;
+
+describe('User API', () => {
+ beforeEach(() => {
+ app = express();
+ app.use(express.json());
+ app.use('/users', userRouter);
+ resetUsers();
+ });
+
+ describe('POST /users - Register new user', () => {
+ it('should create a new user', async () => {
+ const newUser = {
+ email: 'test@example.com',
+ name: 'Test User',
+ password: 'password123'
+ };
+ const response = await request(app).post('/users').send(newUser);
+ expect(response.status).toBe(201);
+ expect(response.body.email).toBe(newUser.email);
+ expect(response.body.name).toBe(newUser.name);
+ expect(response.body.isAdmin).toBe(false);
+ expect(response.body.wishlistProductIds).toEqual([]);
+ expect(response.body).toHaveProperty('userId');
+ expect(response.body).toHaveProperty('createdAt');
+ });
+
+ it('should create an admin user with @github.com email', async () => {
+ const newUser = {
+ email: 'admin@github.com',
+ name: 'Admin User',
+ password: 'password123'
+ };
+ const response = await request(app).post('/users').send(newUser);
+ expect(response.status).toBe(201);
+ expect(response.body.isAdmin).toBe(true);
+ });
+
+ it('should reject invalid email format', async () => {
+ const newUser = {
+ email: 'invalid-email',
+ name: 'Test User',
+ password: 'password123'
+ };
+ const response = await request(app).post('/users').send(newUser);
+ expect(response.status).toBe(400);
+ expect(response.body.error).toContain('Invalid email format');
+ });
+
+ it('should reject password shorter than 8 characters', async () => {
+ const newUser = {
+ email: 'test@example.com',
+ name: 'Test User',
+ password: 'short'
+ };
+ const response = await request(app).post('/users').send(newUser);
+ expect(response.status).toBe(400);
+ expect(response.body.error).toContain('Password must be at least 8 characters');
+ });
+
+ it('should reject duplicate email', async () => {
+ const existingUser = seedUsers[0];
+ const newUser = {
+ email: existingUser.email,
+ name: 'Test User',
+ password: 'password123'
+ };
+ const response = await request(app).post('/users').send(newUser);
+ expect(response.status).toBe(400);
+ expect(response.body.error).toContain('Email already exists');
+ });
+
+ it('should reject missing required fields', async () => {
+ const response = await request(app).post('/users').send({
+ email: 'test@example.com'
+ });
+ expect(response.status).toBe(400);
+ expect(response.body.error).toContain('required');
+ });
+ });
+
+ describe('GET /users/:email - Get user by email', () => {
+ it('should get user by email', async () => {
+ const user = seedUsers[0];
+ const response = await request(app).get(`/users/${user.email}`);
+ expect(response.status).toBe(200);
+ expect(response.body.email).toBe(user.email);
+ expect(response.body.userId).toBe(user.userId);
+ });
+
+ it('should be case-insensitive', async () => {
+ const user = seedUsers[0];
+ const response = await request(app).get(`/users/${user.email.toUpperCase()}`);
+ expect(response.status).toBe(200);
+ expect(response.body.email).toBe(user.email);
+ });
+
+ it('should return 404 for non-existing user', async () => {
+ const response = await request(app).get('/users/nonexistent@example.com');
+ expect(response.status).toBe(404);
+ expect(response.body.error).toContain('User not found');
+ });
+ });
+
+ describe('PUT /users/id/:userId - Update user details', () => {
+ it('should update user name', async () => {
+ const user = seedUsers[0];
+ const updatedData = { name: 'Updated Name' };
+ const response = await request(app)
+ .put(`/users/id/${user.userId}`)
+ .send(updatedData);
+ expect(response.status).toBe(200);
+ expect(response.body.name).toBe(updatedData.name);
+ expect(response.body.email).toBe(user.email);
+ });
+
+ it('should update user email', async () => {
+ const user = seedUsers[0];
+ const updatedData = { email: 'newemail@example.com' };
+ const response = await request(app)
+ .put(`/users/id/${user.userId}`)
+ .send(updatedData);
+ expect(response.status).toBe(200);
+ expect(response.body.email).toBe(updatedData.email);
+ });
+
+ it('should update isAdmin when email changes to @github.com', async () => {
+ const user = seedUsers[0];
+ const updatedData = { email: 'admin@github.com' };
+ const response = await request(app)
+ .put(`/users/id/${user.userId}`)
+ .send(updatedData);
+ expect(response.status).toBe(200);
+ expect(response.body.isAdmin).toBe(true);
+ });
+
+ it('should reject duplicate email on update', async () => {
+ const user1 = seedUsers[0];
+ const user2 = seedUsers[1];
+ const updatedData = { email: user2.email };
+ const response = await request(app)
+ .put(`/users/id/${user1.userId}`)
+ .send(updatedData);
+ expect(response.status).toBe(400);
+ expect(response.body.error).toContain('Email already exists');
+ });
+
+ it('should return 404 for non-existing user', async () => {
+ const response = await request(app)
+ .put('/users/id/999')
+ .send({ name: 'Updated Name' });
+ expect(response.status).toBe(404);
+ expect(response.body.error).toContain('User not found');
+ });
+ });
+
+ describe('Wishlist Operations', () => {
+ describe('GET /users/:userId/wishlist - Get wishlist', () => {
+ it('should get empty wishlist for new user', async () => {
+ const user = seedUsers[0];
+ const response = await request(app).get(`/users/${user.userId}/wishlist`);
+ expect(response.status).toBe(200);
+ expect(Array.isArray(response.body)).toBe(true);
+ });
+
+ it('should get wishlist with product details', async () => {
+ const user = seedUsers[1]; // User with pre-populated wishlist
+ const response = await request(app).get(`/users/${user.userId}/wishlist`);
+ expect(response.status).toBe(200);
+ expect(Array.isArray(response.body)).toBe(true);
+ if (response.body.length > 0) {
+ expect(response.body[0]).toHaveProperty('productId');
+ expect(response.body[0]).toHaveProperty('name');
+ expect(response.body[0]).toHaveProperty('price');
+ }
+ });
+
+ it('should return 404 for non-existing user', async () => {
+ const response = await request(app).get('/users/999/wishlist');
+ expect(response.status).toBe(404);
+ expect(response.body.error).toContain('User not found');
+ });
+ });
+
+ describe('POST /users/:userId/wishlist - Add to wishlist', () => {
+ it('should add product to wishlist', async () => {
+ const user = seedUsers[0];
+ const productId = 1;
+ const response = await request(app)
+ .post(`/users/${user.userId}/wishlist`)
+ .send({ productId });
+ expect(response.status).toBe(200);
+ expect(response.body.wishlistProductIds).toContain(productId);
+ });
+
+ it('should reject duplicate product in wishlist', async () => {
+ const user = seedUsers[0];
+ const productId = 1;
+ // Add once
+ await request(app)
+ .post(`/users/${user.userId}/wishlist`)
+ .send({ productId });
+ // Try to add again
+ const response = await request(app)
+ .post(`/users/${user.userId}/wishlist`)
+ .send({ productId });
+ expect(response.status).toBe(400);
+ expect(response.body.error).toContain('already in wishlist');
+ });
+
+ it('should reject invalid product ID', async () => {
+ const user = seedUsers[0];
+ const productId = 99999;
+ const response = await request(app)
+ .post(`/users/${user.userId}/wishlist`)
+ .send({ productId });
+ expect(response.status).toBe(400);
+ expect(response.body.error).toContain('Invalid product ID');
+ });
+
+ it('should reject missing product ID', async () => {
+ const user = seedUsers[0];
+ const response = await request(app)
+ .post(`/users/${user.userId}/wishlist`)
+ .send({});
+ expect(response.status).toBe(400);
+ expect(response.body.error).toContain('Product ID is required');
+ });
+
+ it('should return 404 for non-existing user', async () => {
+ const response = await request(app)
+ .post('/users/999/wishlist')
+ .send({ productId: 1 });
+ expect(response.status).toBe(404);
+ expect(response.body.error).toContain('User not found');
+ });
+ });
+
+ describe('DELETE /users/:userId/wishlist/:productId - Remove from wishlist', () => {
+ it('should remove product from wishlist', async () => {
+ const user = seedUsers[0];
+ const productId = 1;
+ // Add product first
+ await request(app)
+ .post(`/users/${user.userId}/wishlist`)
+ .send({ productId });
+ // Remove product
+ const response = await request(app)
+ .delete(`/users/${user.userId}/wishlist/${productId}`);
+ expect(response.status).toBe(200);
+ expect(response.body.wishlistProductIds).not.toContain(productId);
+ });
+
+ it('should return 404 for product not in wishlist', async () => {
+ const user = seedUsers[0];
+ const productId = 1;
+ const response = await request(app)
+ .delete(`/users/${user.userId}/wishlist/${productId}`);
+ expect(response.status).toBe(404);
+ expect(response.body.error).toContain('not found in wishlist');
+ });
+
+ it('should return 404 for non-existing user', async () => {
+ const response = await request(app)
+ .delete('/users/999/wishlist/1');
+ expect(response.status).toBe(404);
+ expect(response.body.error).toContain('User not found');
+ });
+ });
+ });
+});
diff --git a/api/src/routes/user.ts b/api/src/routes/user.ts
new file mode 100644
index 0000000..b14bb47
--- /dev/null
+++ b/api/src/routes/user.ts
@@ -0,0 +1,361 @@
+/**
+ * @swagger
+ * tags:
+ * name: Users
+ * description: API endpoints for managing users and wishlists
+ */
+
+/**
+ * @swagger
+ * /api/users:
+ * post:
+ * summary: Register a new user
+ * tags: [Users]
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - email
+ * - name
+ * - password
+ * properties:
+ * email:
+ * type: string
+ * format: email
+ * name:
+ * type: string
+ * password:
+ * type: string
+ * minLength: 8
+ * responses:
+ * 201:
+ * description: User created successfully
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/User'
+ * 400:
+ * description: Invalid input or email already exists
+ *
+ * /api/users/{email}:
+ * get:
+ * summary: Get user by email (for login)
+ * tags: [Users]
+ * parameters:
+ * - in: path
+ * name: email
+ * required: true
+ * schema:
+ * type: string
+ * format: email
+ * description: User email address
+ * responses:
+ * 200:
+ * description: User found
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/User'
+ * 404:
+ * description: User not found
+ *
+ * /api/users/id/{userId}:
+ * put:
+ * summary: Update user details
+ * tags: [Users]
+ * parameters:
+ * - in: path
+ * name: userId
+ * required: true
+ * schema:
+ * type: integer
+ * description: User ID
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * name:
+ * type: string
+ * email:
+ * type: string
+ * format: email
+ * responses:
+ * 200:
+ * description: User updated successfully
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/User'
+ * 404:
+ * description: User not found
+ *
+ * /api/users/{userId}/wishlist:
+ * get:
+ * summary: Get user's wishlist with full product details
+ * tags: [Users]
+ * parameters:
+ * - in: path
+ * name: userId
+ * required: true
+ * schema:
+ * type: integer
+ * description: User ID
+ * responses:
+ * 200:
+ * description: List of products in user's wishlist
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/Product'
+ * 404:
+ * description: User not found
+ * post:
+ * summary: Add product to user's wishlist
+ * tags: [Users]
+ * parameters:
+ * - in: path
+ * name: userId
+ * required: true
+ * schema:
+ * type: integer
+ * description: User ID
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - productId
+ * properties:
+ * productId:
+ * type: integer
+ * responses:
+ * 200:
+ * description: Product added to wishlist
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/User'
+ * 400:
+ * description: Product already in wishlist or invalid product ID
+ * 404:
+ * description: User not found
+ *
+ * /api/users/{userId}/wishlist/{productId}:
+ * delete:
+ * summary: Remove product from user's wishlist
+ * tags: [Users]
+ * parameters:
+ * - in: path
+ * name: userId
+ * required: true
+ * schema:
+ * type: integer
+ * description: User ID
+ * - in: path
+ * name: productId
+ * required: true
+ * schema:
+ * type: integer
+ * description: Product ID
+ * responses:
+ * 200:
+ * description: Product removed from wishlist
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/User'
+ * 404:
+ * description: User or product not found in wishlist
+ */
+
+import { Router, Request, Response } from 'express';
+import { User } from '../models/user';
+import { users as seedUsers } from '../seedData';
+import { products } from '../seedData';
+
+const router = Router();
+
+let users: User[] = [...seedUsers];
+
+// Validation helpers
+const isValidEmail = (email: string): boolean => {
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ return emailRegex.test(email);
+};
+
+const isValidPassword = (password: string): boolean => {
+ // Password must be at least 8 characters
+ return password.length >= 8;
+};
+
+// POST /api/users - Register new user
+router.post('/', (req: Request, res: Response) => {
+ const { email, name, password } = req.body;
+
+ // Validate required fields
+ if (!email || !name || !password) {
+ return res.status(400).json({ error: 'Email, name, and password are required' });
+ }
+
+ // Validate email format
+ if (!isValidEmail(email)) {
+ return res.status(400).json({ error: 'Invalid email format' });
+ }
+
+ // Validate password requirements
+ if (!isValidPassword(password)) {
+ return res.status(400).json({ error: 'Password must be at least 8 characters' });
+ }
+
+ // Check for duplicate email
+ const existingUser = users.find(u => u.email.toLowerCase() === email.toLowerCase());
+ if (existingUser) {
+ return res.status(400).json({ error: 'Email already exists' });
+ }
+
+ // Create new user
+ const newUser: User = {
+ userId: Math.max(0, ...users.map(u => u.userId)) + 1,
+ email,
+ name,
+ isAdmin: email.endsWith('@github.com'), // Admin if GitHub email
+ createdAt: new Date(),
+ wishlistProductIds: []
+ };
+
+ users.push(newUser);
+ res.status(201).json(newUser);
+});
+
+// GET /api/users/:email - Get user by email (for login)
+router.get('/:email', (req: Request, res: Response) => {
+ const { email } = req.params;
+ const user = users.find(u => u.email.toLowerCase() === email.toLowerCase());
+
+ if (!user) {
+ return res.status(404).json({ error: 'User not found' });
+ }
+
+ res.json(user);
+});
+
+// PUT /api/users/id/:userId - Update user details
+router.put('/id/:userId', (req: Request, res: Response) => {
+ const userId = parseInt(req.params.userId);
+ const { name, email } = req.body;
+
+ const userIndex = users.findIndex(u => u.userId === userId);
+
+ if (userIndex === -1) {
+ return res.status(404).json({ error: 'User not found' });
+ }
+
+ // If email is being updated, check for duplicates
+ if (email && email !== users[userIndex].email) {
+ if (!isValidEmail(email)) {
+ return res.status(400).json({ error: 'Invalid email format' });
+ }
+ const existingUser = users.find(u => u.email.toLowerCase() === email.toLowerCase());
+ if (existingUser) {
+ return res.status(400).json({ error: 'Email already exists' });
+ }
+ }
+
+ // Update user
+ if (name) users[userIndex].name = name;
+ if (email) {
+ users[userIndex].email = email;
+ users[userIndex].isAdmin = email.endsWith('@github.com');
+ }
+
+ res.json(users[userIndex]);
+});
+
+// GET /api/users/:userId/wishlist - Get user's wishlist with full product details
+router.get('/:userId/wishlist', (req: Request, res: Response) => {
+ const userId = parseInt(req.params.userId);
+ const user = users.find(u => u.userId === userId);
+
+ if (!user) {
+ return res.status(404).json({ error: 'User not found' });
+ }
+
+ // Get full product details for each product in wishlist
+ const wishlistProducts = products.filter(p =>
+ user.wishlistProductIds.includes(p.productId)
+ );
+
+ res.json(wishlistProducts);
+});
+
+// POST /api/users/:userId/wishlist - Add product to wishlist
+router.post('/:userId/wishlist', (req: Request, res: Response) => {
+ const userId = parseInt(req.params.userId);
+ const { productId } = req.body;
+
+ if (!productId) {
+ return res.status(400).json({ error: 'Product ID is required' });
+ }
+
+ const userIndex = users.findIndex(u => u.userId === userId);
+
+ if (userIndex === -1) {
+ return res.status(404).json({ error: 'User not found' });
+ }
+
+ // Verify product exists
+ const product = products.find(p => p.productId === productId);
+ if (!product) {
+ return res.status(400).json({ error: 'Invalid product ID' });
+ }
+
+ // Check if product is already in wishlist
+ if (users[userIndex].wishlistProductIds.includes(productId)) {
+ return res.status(400).json({ error: 'Product already in wishlist' });
+ }
+
+ // Add to wishlist
+ users[userIndex].wishlistProductIds.push(productId);
+
+ res.json(users[userIndex]);
+});
+
+// DELETE /api/users/:userId/wishlist/:productId - Remove from wishlist
+router.delete('/:userId/wishlist/:productId', (req: Request, res: Response) => {
+ const userId = parseInt(req.params.userId);
+ const productId = parseInt(req.params.productId);
+
+ const userIndex = users.findIndex(u => u.userId === userId);
+
+ if (userIndex === -1) {
+ return res.status(404).json({ error: 'User not found' });
+ }
+
+ const productIndex = users[userIndex].wishlistProductIds.indexOf(productId);
+
+ if (productIndex === -1) {
+ return res.status(404).json({ error: 'Product not found in wishlist' });
+ }
+
+ // Remove from wishlist
+ users[userIndex].wishlistProductIds.splice(productIndex, 1);
+
+ res.json(users[userIndex]);
+});
+
+// Helper function to reset users for testing
+export function resetUsers() {
+ users = [...seedUsers];
+}
+
+export default router;
diff --git a/api/src/seedData.ts b/api/src/seedData.ts
index ba0bebe..07df6e4 100644
--- a/api/src/seedData.ts
+++ b/api/src/seedData.ts
@@ -6,6 +6,7 @@ import { Order } from './models/order';
import { OrderDetail } from './models/orderDetail';
import { Delivery } from './models/delivery';
import { OrderDetailDelivery } from './models/orderDetailDelivery';
+import { User } from './models/user';
// Suppliers
export const suppliers: Supplier[] = [
@@ -291,4 +292,32 @@ export const orderDetailDeliveries: OrderDetailDelivery[] = [
quantity: 20,
notes: "Delivery"
}
+];
+
+// Users
+export const users: User[] = [
+ {
+ userId: 1,
+ email: "alice@example.com",
+ name: "Alice Johnson",
+ isAdmin: false,
+ createdAt: new Date("2024-01-15T10:00:00Z"),
+ wishlistProductIds: []
+ },
+ {
+ userId: 2,
+ email: "bob@github.com",
+ name: "Bob Smith",
+ isAdmin: true,
+ createdAt: new Date("2024-02-01T14:30:00Z"),
+ wishlistProductIds: [1, 3, 5]
+ },
+ {
+ userId: 3,
+ email: "charlie@example.com",
+ name: "Charlie Brown",
+ isAdmin: false,
+ createdAt: new Date("2024-03-10T09:15:00Z"),
+ wishlistProductIds: [2, 4, 7, 10]
+ }
];
\ No newline at end of file
From 935b55daaf7c66caf7e8e6cf21161bc4964d2110 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 16 Feb 2026 04:55:06 +0000
Subject: [PATCH 3/4] Complete frontend implementation: auth, wishlist,
register, and UI components
Co-authored-by: thomasiverson <12767513+thomasiverson@users.noreply.github.com>
---
frontend/src/App.tsx | 23 ++-
frontend/src/api/config.ts | 3 +-
frontend/src/components/Login.tsx | 25 ++-
frontend/src/components/Navigation.tsx | 36 ++++-
frontend/src/components/Register.tsx | 146 +++++++++++++++++
frontend/src/components/Wishlist.tsx | 148 ++++++++++++++++++
.../components/entity/product/Products.tsx | 57 +++++++
frontend/src/context/AuthContext.tsx | 83 ++++++++--
frontend/src/context/WishlistContext.tsx | 121 ++++++++++++++
9 files changed, 611 insertions(+), 31 deletions(-)
create mode 100644 frontend/src/components/Register.tsx
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..96f1c70 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -5,10 +5,17 @@ import About from './components/About';
import Footer from './components/Footer';
import Products from './components/entity/product/Products';
import Login from './components/Login';
+import Register from './components/Register';
+import Wishlist from './components/Wishlist';
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';
+import { QueryClient, QueryClientProvider } from 'react-query';
+
+// Create a client
+const queryClient = new QueryClient();
// Wrapper component to apply theme classes
function ThemedApp() {
@@ -24,6 +31,8 @@ function ThemedApp() {
+ You need to be logged in to view your wishlist. +
++ Start adding products you love to your wishlist! +
++ {product.description} +
+