Skip to content

Commit 00cd47b

Browse files
committed
feat: add useDebounce hook for debouncing values
feat: implement checkout API with validation, shipping options, and order management feat: enhance product API to support pagination and search parameters feat: create search API with advanced filtering, suggestions, and faceted results feat: establish a centralized API client with error handling and retry logic fix: update axios base URL for consistency across environments feat: implement error handling service for better error management feat: add validation utilities for form fields and common validation rules
1 parent 01ef669 commit 00cd47b

34 files changed

+5215
-103
lines changed

backend/app.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import dotenv from 'dotenv'
33
import { PORT } from './configs/variables.js';
44
import fileUpload from 'express-fileupload';
55
import productRouter from './routes/product.route.js'
6+
import publicProductRouter from './routes/public-product.route.js'
67
import userRouter from './routes/user.route.js';
78
import authRouter from './routes/auth.route.js'
89
import { adminMiddleware, userMiddleware } from './middlewares/auth.middleware.js';
910
import cors from 'cors';
1011
dotenv.config();
1112
import './configs/database.js';
1213
import orderRouter from './routes/order.route.js';
14+
import checkoutRouter from './routes/checkout.route.js';
1315

1416

1517
const app = express();
@@ -25,10 +27,15 @@ app.get('/', (req, res) => {
2527
res.send('Welcome to Wyze Academy!');
2628
});
2729

30+
// Public routes (no authentication required)
31+
app.use('/products', publicProductRouter)
32+
33+
// Protected routes (authentication required)
2834
app.use('/products', userMiddleware, productRouter)
2935
app.use('/users', adminMiddleware, userRouter)
3036
app.use('/auth', authRouter)
3137
app.use('/orders', userMiddleware, orderRouter)
38+
app.use('/checkout', checkoutRouter)
3239

3340
app.listen(PORT, () => {
3441
console.log(`Server is running on port ${PORT}`);

backend/controllers/auth.controller.js

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,23 @@ import { getUserByEmail, createUser, getUserById, updateUser } from "../models/u
55
export async function registerController(req, res) {
66
try {
77
const { firstName, lastName, email, password, address } = req.body
8+
9+
// Validation
810
if (!(email && password && firstName && lastName && address)) {
9-
res.status(400).send('All field are required')
11+
return res.status(400).send({ message: 'All fields are required' })
12+
}
13+
14+
// Password validation
15+
if (password.length < 6) {
16+
return res.status(400).send({ message: 'Password must be at least 6 characters long' })
17+
}
18+
19+
// Check if user already exists
20+
const existingUser = await getUserByEmail(email.toLowerCase())
21+
if (existingUser) {
22+
return res.status(409).send({ message: 'User with this email already exists' })
1023
}
24+
1125
const encryptedPassword = await encryptPassword(password)
1226

1327
const user = await createUser({
@@ -17,33 +31,50 @@ export async function registerController(req, res) {
1731
password: encryptedPassword,
1832
address,
1933
})
20-
const token = generateToken({ userId: user._id, email, role: user.role })
21-
user.token = token
22-
user.password = undefined
23-
user.__v = undefined
2434

25-
res.status(201).json({ user })
35+
const token = generateToken({ userId: user._id, email: user.email, role: user.role })
36+
37+
// Clean user object
38+
const userResponse = user.toObject()
39+
delete userResponse.password
40+
delete userResponse.__v
41+
42+
res.status(201).json({ user: userResponse, token })
2643
} catch (error) {
27-
console.log(error)
44+
console.error('Registration error:', error)
2845
res.status(500).send({ error: 'Internal Server Error' });
2946
}
3047
}
3148

3249
export async function loginController(req, res) {
3350
try {
3451
const { email, password } = req.body;
35-
const user = await getUserByEmail(email)
52+
53+
// Validation
54+
if (!email || !password) {
55+
return res.status(400).send({ message: 'Email and password are required' })
56+
}
57+
58+
const user = await getUserByEmail(email.toLowerCase())
3659
if (!user) {
37-
res.status(404).send({ message: 'User not found' })
60+
return res.status(401).send({ message: 'Invalid email or password' })
3861
}
62+
3963
const passwordValidity = await comparePasswords(password, user.password)
4064
if (!passwordValidity) {
41-
res.status(403).send({ message: "The password is incorrect" })
65+
return res.status(401).send({ message: 'Invalid email or password' })
4266
}
67+
4368
const token = generateToken({ userId: user._id, email: user.email, role: user.role })
44-
res.status(200).send({ user, token });
69+
70+
// Clean user object
71+
const userResponse = user.toObject()
72+
delete userResponse.password
73+
delete userResponse.__v
74+
75+
res.status(200).send({ user: userResponse, token });
4576
} catch (error) {
46-
console.log(error)
77+
console.error('Login error:', error)
4778
res.status(500).send({ message: 'Internal server error' })
4879
}
4980
}
Lines changed: 146 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,170 @@
1-
import { createOrder, getMyOrders, getOrderById, modifyStatus } from "../models/order.model.js"
1+
import {
2+
createOrder,
3+
getMyOrders,
4+
getOrderById,
5+
modifyStatus,
6+
getOrderStats,
7+
getOrdersByStatus,
8+
cancelOrder,
9+
getAllOrders
10+
} from "../models/order.model.js"
211

312
export async function createOrderController(req, res) {
413
try {
5-
const order = await createOrder(req.user.userId, req.body)
6-
res.status(201).send({ order, message: "Order created successfully" })
14+
// Validate required fields
15+
const { products, shippingAddress, totalPrice } = req.body;
16+
17+
if (!products || !Array.isArray(products) || products.length === 0) {
18+
return res.status(400).send({ error: "Products are required" });
19+
}
20+
21+
if (!shippingAddress || !shippingAddress.fullName || !shippingAddress.addressLine1) {
22+
return res.status(400).send({ error: "Complete shipping address is required" });
23+
}
24+
25+
if (!totalPrice || totalPrice <= 0) {
26+
return res.status(400).send({ error: "Valid total price is required" });
27+
}
28+
29+
const order = await createOrder(req.user.userId, req.body);
30+
res.status(201).send({
31+
success: true,
32+
order,
33+
message: "Order created successfully"
34+
});
735
} catch (error) {
8-
res.status(500).send({ error: error.message })
36+
console.log(`Error: ${error.message}`);
37+
if (error.message.includes('stock') || error.message.includes('mismatch') || error.message.includes('not found')) {
38+
return res.status(400).send({ error: error.message });
39+
}
40+
res.status(500).send({ error: "Internal Server Error" });
941
}
1042
}
1143

1244
export async function getMyOrdersController(req, res) {
1345
try {
14-
const myOrders = await getMyOrders(req.user.userId)
15-
return res.status(200).send(myOrders)
46+
const { page = 1, limit = 10, status } = req.query;
47+
48+
let myOrders;
49+
if (status) {
50+
myOrders = await getOrdersByStatus(status, parseInt(page), parseInt(limit));
51+
// Filter by user
52+
myOrders.orders = myOrders.orders.filter(order => order.userId === req.user.userId);
53+
} else {
54+
myOrders = await getMyOrders(req.user.userId);
55+
}
56+
57+
return res.status(200).send({
58+
success: true,
59+
orders: myOrders
60+
});
1661
} catch (error) {
17-
res.status(500).send({ error: error.message })
62+
console.log(`Error: ${error.message}`);
63+
res.status(500).send({ error: "Internal Server Error" });
1864
}
1965
}
2066

2167
export async function getOrderByIdController(req, res) {
2268
try {
23-
const order = await getOrderById(req.user.userId, req.params.id)
24-
res.status(200).send(order)
69+
const order = await getOrderById(req.user.userId, req.params.id);
70+
res.status(200).send({
71+
success: true,
72+
order
73+
});
2574
} catch (error) {
26-
res.status(500).send({ error: error.message })
75+
console.log(`Error: ${error.message}`);
76+
if (error.message.includes('not allowed') || error.message.includes('not found')) {
77+
return res.status(404).send({ error: error.message });
78+
}
79+
res.status(500).send({ error: "Internal Server Error" });
2780
}
2881
}
2982

3083
export async function modifyStatusController(req, res) {
3184
try {
32-
const order = await modifyStatus(req.params.id, req.body.status)
33-
res.status(200).send({ order, message: "Order status modified successfully" })
85+
const { status, trackingNumber, estimatedDelivery } = req.body;
86+
87+
if (!status) {
88+
return res.status(400).send({ error: "Status is required" });
89+
}
90+
91+
const order = await modifyStatus(req.params.id, status, trackingNumber, estimatedDelivery);
92+
res.status(200).send({
93+
success: true,
94+
order,
95+
message: "Order status updated successfully"
96+
});
97+
} catch (error) {
98+
console.log(`Error: ${error.message}`);
99+
if (error.message.includes('Cannot change status')) {
100+
return res.status(400).send({ error: error.message });
101+
}
102+
res.status(500).send({ error: "Internal Server Error" });
103+
}
104+
}
105+
106+
export async function cancelOrderController(req, res) {
107+
try {
108+
const { reason } = req.body;
109+
const order = await cancelOrder(req.params.id, req.user.userId, reason);
110+
res.status(200).send({
111+
success: true,
112+
order,
113+
message: "Order cancelled successfully"
114+
});
115+
} catch (error) {
116+
console.log(`Error: ${error.message}`);
117+
if (error.message.includes('Cannot cancel') || error.message.includes('not found')) {
118+
return res.status(400).send({ error: error.message });
119+
}
120+
res.status(500).send({ error: "Internal Server Error" });
121+
}
122+
}
123+
124+
export async function getOrderStatsController(req, res) {
125+
try {
126+
const stats = await getOrderStats(req.user.userId);
127+
res.status(200).send({
128+
success: true,
129+
stats
130+
});
131+
} catch (error) {
132+
console.log(`Error: ${error.message}`);
133+
res.status(500).send({ error: "Internal Server Error" });
134+
}
135+
}
136+
137+
// Admin only controllers
138+
export async function getAllOrdersController(req, res) {
139+
try {
140+
const { page = 1, limit = 10, status } = req.query;
141+
142+
let orders;
143+
if (status) {
144+
orders = await getOrdersByStatus(status, parseInt(page), parseInt(limit));
145+
} else {
146+
orders = await getAllOrders();
147+
}
148+
149+
res.status(200).send({
150+
success: true,
151+
orders
152+
});
153+
} catch (error) {
154+
console.log(`Error: ${error.message}`);
155+
res.status(500).send({ error: "Internal Server Error" });
156+
}
157+
}
158+
159+
export async function getAdminOrderStatsController(req, res) {
160+
try {
161+
const stats = await getOrderStats();
162+
res.status(200).send({
163+
success: true,
164+
stats
165+
});
34166
} catch (error) {
35-
res.status(500).send({ error: error.message })
167+
console.log(`Error: ${error.message}`);
168+
res.status(500).send({ error: "Internal Server Error" });
36169
}
37170
}

0 commit comments

Comments
 (0)