create-vrrsystem-app 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +30 -0
- package/package.json +29 -0
- package/templates/VehicleRentalReservationSystem/Documentation/API.md +57 -0
- package/templates/VehicleRentalReservationSystem/Documentation/DFD.md +62 -0
- package/templates/VehicleRentalReservationSystem/Documentation/ERD.md +109 -0
- package/templates/VehicleRentalReservationSystem/Documentation/Installation.md +40 -0
- package/templates/VehicleRentalReservationSystem/README.md +165 -0
- package/templates/VehicleRentalReservationSystem/backend-project/.env.example +8 -0
- package/templates/VehicleRentalReservationSystem/backend-project/config/db.js +10 -0
- package/templates/VehicleRentalReservationSystem/backend-project/controllers/auth.controller.js +39 -0
- package/templates/VehicleRentalReservationSystem/backend-project/controllers/customer.controller.js +19 -0
- package/templates/VehicleRentalReservationSystem/backend-project/controllers/dashboard.controller.js +48 -0
- package/templates/VehicleRentalReservationSystem/backend-project/controllers/report.controller.js +45 -0
- package/templates/VehicleRentalReservationSystem/backend-project/controllers/reservation.controller.js +94 -0
- package/templates/VehicleRentalReservationSystem/backend-project/controllers/user.controller.js +16 -0
- package/templates/VehicleRentalReservationSystem/backend-project/controllers/vehicle.controller.js +20 -0
- package/templates/VehicleRentalReservationSystem/backend-project/middleware/auth.js +22 -0
- package/templates/VehicleRentalReservationSystem/backend-project/middleware/errorHandler.js +10 -0
- package/templates/VehicleRentalReservationSystem/backend-project/middleware/validate.js +6 -0
- package/templates/VehicleRentalReservationSystem/backend-project/models/Customer.js +11 -0
- package/templates/VehicleRentalReservationSystem/backend-project/models/ReservationRental.js +17 -0
- package/templates/VehicleRentalReservationSystem/backend-project/models/User.js +20 -0
- package/templates/VehicleRentalReservationSystem/backend-project/models/Vehicle.js +14 -0
- package/templates/VehicleRentalReservationSystem/backend-project/package-lock.json +1695 -0
- package/templates/VehicleRentalReservationSystem/backend-project/package.json +26 -0
- package/templates/VehicleRentalReservationSystem/backend-project/routes/auth.routes.js +21 -0
- package/templates/VehicleRentalReservationSystem/backend-project/routes/customer.routes.js +20 -0
- package/templates/VehicleRentalReservationSystem/backend-project/routes/dashboard.routes.js +6 -0
- package/templates/VehicleRentalReservationSystem/backend-project/routes/report.routes.js +6 -0
- package/templates/VehicleRentalReservationSystem/backend-project/routes/reservation.routes.js +14 -0
- package/templates/VehicleRentalReservationSystem/backend-project/routes/user.routes.js +10 -0
- package/templates/VehicleRentalReservationSystem/backend-project/routes/vehicle.routes.js +21 -0
- package/templates/VehicleRentalReservationSystem/backend-project/seed/seed.js +80 -0
- package/templates/VehicleRentalReservationSystem/backend-project/server.js +40 -0
- package/templates/VehicleRentalReservationSystem/backend-project/utils/token.js +3 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/index.html +14 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/package-lock.json +3759 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/package.json +32 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/postcss.config.js +1 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/App.jsx +33 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/components/ConfirmDelete.jsx +12 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/components/Modal.jsx +22 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/components/ProtectedRoute.jsx +9 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/components/Sidebar.jsx +42 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/components/Topbar.jsx +24 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/context/AuthContext.jsx +30 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/context/ThemeContext.jsx +13 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/index.css +37 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/layouts/AppLayout.jsx +18 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/main.jsx +20 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Customers.jsx +95 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Dashboard.jsx +84 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Login.jsx +54 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Register.jsx +36 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Rentals.jsx +58 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Reports.jsx +108 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Reservations.jsx +125 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Settings.jsx +32 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Users.jsx +78 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Vehicles.jsx +110 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/services/api.js +16 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/src/utils/format.js +20 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/tailwind.config.js +18 -0
- package/templates/VehicleRentalReservationSystem/frontend-project/vite.config.js +9 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vrs-backend",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Vehicle Rental & Reservation System - Backend",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node server.js",
|
|
8
|
+
"dev": "nodemon server.js",
|
|
9
|
+
"seed": "node seed/seed.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"bcryptjs": "^2.4.3",
|
|
13
|
+
"cors": "^2.8.5",
|
|
14
|
+
"dotenv": "^16.3.1",
|
|
15
|
+
"express": "^4.18.2",
|
|
16
|
+
"express-rate-limit": "^7.1.5",
|
|
17
|
+
"express-validator": "^7.0.1",
|
|
18
|
+
"helmet": "^7.1.0",
|
|
19
|
+
"jsonwebtoken": "^9.0.2",
|
|
20
|
+
"mongoose": "^8.0.3",
|
|
21
|
+
"morgan": "^1.10.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"nodemon": "^3.0.2"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const router = require('express').Router();
|
|
2
|
+
const { body } = require('express-validator');
|
|
3
|
+
const c = require('../controllers/auth.controller');
|
|
4
|
+
const validate = require('../middleware/validate');
|
|
5
|
+
const { protect } = require('../middleware/auth');
|
|
6
|
+
|
|
7
|
+
router.post('/register', [
|
|
8
|
+
body('username').isLength({ min: 3 }),
|
|
9
|
+
body('password').isLength({ min: 6 }),
|
|
10
|
+
body('role').optional().isIn(['ADMIN','STAFF','MANAGER'])
|
|
11
|
+
], validate, c.register);
|
|
12
|
+
|
|
13
|
+
router.post('/login', [
|
|
14
|
+
body('username').notEmpty(),
|
|
15
|
+
body('password').notEmpty()
|
|
16
|
+
], validate, c.login);
|
|
17
|
+
|
|
18
|
+
router.post('/refresh', c.refresh);
|
|
19
|
+
router.post('/logout', c.logout);
|
|
20
|
+
router.get('/me', protect, c.me);
|
|
21
|
+
module.exports = router;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const router = require('express').Router();
|
|
2
|
+
const { body } = require('express-validator');
|
|
3
|
+
const c = require('../controllers/customer.controller');
|
|
4
|
+
const validate = require('../middleware/validate');
|
|
5
|
+
const { protect, authorize } = require('../middleware/auth');
|
|
6
|
+
|
|
7
|
+
router.use(protect);
|
|
8
|
+
const rules = [
|
|
9
|
+
body('fullName').notEmpty().withMessage('Full name required'),
|
|
10
|
+
body('nationalId').isLength({ min: 6 }).withMessage('Invalid National ID'),
|
|
11
|
+
body('phone').matches(/^[+0-9\s-]{7,20}$/).withMessage('Invalid phone'),
|
|
12
|
+
body('email').isEmail().withMessage('Invalid email'),
|
|
13
|
+
body('address').notEmpty()
|
|
14
|
+
];
|
|
15
|
+
router.get('/', c.list);
|
|
16
|
+
router.get('/:id', c.get);
|
|
17
|
+
router.post('/', authorize('ADMIN','STAFF'), rules, validate, c.create);
|
|
18
|
+
router.put('/:id', authorize('ADMIN','STAFF'), c.update);
|
|
19
|
+
router.delete('/:id', authorize('ADMIN'), c.remove);
|
|
20
|
+
module.exports = router;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const router = require('express').Router();
|
|
2
|
+
const c = require('../controllers/report.controller');
|
|
3
|
+
const { protect, authorize } = require('../middleware/auth');
|
|
4
|
+
router.use(protect, authorize('ADMIN','MANAGER','STAFF'));
|
|
5
|
+
router.get('/', c.generate);
|
|
6
|
+
module.exports = router;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const router = require('express').Router();
|
|
2
|
+
const c = require('../controllers/reservation.controller');
|
|
3
|
+
const { protect, authorize } = require('../middleware/auth');
|
|
4
|
+
router.use(protect);
|
|
5
|
+
router.get('/', c.list);
|
|
6
|
+
router.get('/:id', c.get);
|
|
7
|
+
router.post('/', authorize('ADMIN','STAFF'), c.create);
|
|
8
|
+
router.put('/:id', authorize('ADMIN','STAFF'), c.update);
|
|
9
|
+
router.delete('/:id', authorize('ADMIN'), c.remove);
|
|
10
|
+
router.patch('/:id/approve', authorize('ADMIN','MANAGER'), c.approve);
|
|
11
|
+
router.patch('/:id/reject', authorize('ADMIN','MANAGER'), c.reject);
|
|
12
|
+
router.patch('/:id/pickup', authorize('ADMIN','STAFF'), c.pickup);
|
|
13
|
+
router.patch('/:id/return', authorize('ADMIN','STAFF'), c.returnVehicle);
|
|
14
|
+
module.exports = router;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const router = require('express').Router();
|
|
2
|
+
const c = require('../controllers/user.controller');
|
|
3
|
+
const { protect, authorize } = require('../middleware/auth');
|
|
4
|
+
router.use(protect, authorize('ADMIN'));
|
|
5
|
+
router.get('/', c.list);
|
|
6
|
+
router.get('/:id', c.get);
|
|
7
|
+
router.post('/', c.create);
|
|
8
|
+
router.put('/:id', c.update);
|
|
9
|
+
router.delete('/:id', c.remove);
|
|
10
|
+
module.exports = router;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const router = require('express').Router();
|
|
2
|
+
const { body } = require('express-validator');
|
|
3
|
+
const c = require('../controllers/vehicle.controller');
|
|
4
|
+
const validate = require('../middleware/validate');
|
|
5
|
+
const { protect, authorize } = require('../middleware/auth');
|
|
6
|
+
|
|
7
|
+
router.use(protect);
|
|
8
|
+
const rules = [
|
|
9
|
+
body('plateNumber').notEmpty(),
|
|
10
|
+
body('brand').notEmpty(),
|
|
11
|
+
body('model').notEmpty(),
|
|
12
|
+
body('year').isInt({ min: 1980 }),
|
|
13
|
+
body('vehicleType').notEmpty(),
|
|
14
|
+
body('purchasePrice').isFloat({ min: 0 })
|
|
15
|
+
];
|
|
16
|
+
router.get('/', c.list);
|
|
17
|
+
router.get('/:id', c.get);
|
|
18
|
+
router.post('/', authorize('ADMIN'), rules, validate, c.create);
|
|
19
|
+
router.put('/:id', authorize('ADMIN'), c.update);
|
|
20
|
+
router.delete('/:id', authorize('ADMIN'), c.remove);
|
|
21
|
+
module.exports = router;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require('dotenv').config();
|
|
2
|
+
const mongoose = require('mongoose');
|
|
3
|
+
const connectDB = require('../config/db');
|
|
4
|
+
const User = require('../models/User');
|
|
5
|
+
const Customer = require('../models/Customer');
|
|
6
|
+
const Vehicle = require('../models/Vehicle');
|
|
7
|
+
const RR = require('../models/ReservationRental');
|
|
8
|
+
|
|
9
|
+
const firstNames = ['Jean','Eric','Aline','Claudine','Patrick','Diane','Olivier','Sandrine','Emmanuel','Yvette','Pacifique','Ange','Fabrice','Solange','Innocent','Beatrice','Theodore','Josiane','Felix','Mireille'];
|
|
10
|
+
const lastNames = ['Uwase','Niyonsenga','Mukamana','Habimana','Iradukunda','Mugisha','Ndayisaba','Ingabire','Tuyishime','Rugamba','Mutuyimana','Nzeyimana','Kayitesi','Bizimana','Munyaneza','Hakizimana','Uwimana','Mukantwari','Nshimiyimana','Twagirayezu'];
|
|
11
|
+
const types = ['Sedan','SUV','Hatchback','Pickup','Van','Bus','Truck','Coupe'];
|
|
12
|
+
const brands = [['Toyota','Corolla'],['Toyota','Hilux'],['Honda','Civic'],['Nissan','Patrol'],['Hyundai','Tucson'],['Kia','Sportage'],['Ford','Ranger'],['Mazda','CX-5'],['Mitsubishi','Pajero'],['Suzuki','Vitara']];
|
|
13
|
+
|
|
14
|
+
const rand = (a) => a[Math.floor(Math.random()*a.length)];
|
|
15
|
+
const randInt = (a,b) => Math.floor(Math.random()*(b-a+1))+a;
|
|
16
|
+
|
|
17
|
+
(async () => {
|
|
18
|
+
await connectDB();
|
|
19
|
+
console.log('Clearing collections...');
|
|
20
|
+
await Promise.all([User.deleteMany(), Customer.deleteMany(), Vehicle.deleteMany(), RR.deleteMany()]);
|
|
21
|
+
|
|
22
|
+
console.log('Creating users...');
|
|
23
|
+
const users = await User.create([
|
|
24
|
+
{ username: 'admin', password: 'admin123', fullName: 'System Administrator', email: 'admin@swiftwheels.rw', role: 'ADMIN' },
|
|
25
|
+
{ username: 'manager', password: 'manager123', fullName: 'Operations Manager', email: 'manager@swiftwheels.rw', role: 'MANAGER' },
|
|
26
|
+
{ username: 'staff', password: 'staff123', fullName: 'Front Desk Staff', email: 'staff@swiftwheels.rw', role: 'STAFF' }
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
console.log('Creating 20 customers...');
|
|
30
|
+
const customers = [];
|
|
31
|
+
for (let i = 0; i < 20; i++) {
|
|
32
|
+
const fn = rand(firstNames), ln = rand(lastNames);
|
|
33
|
+
customers.push(await Customer.create({
|
|
34
|
+
fullName: `${fn} ${ln}`,
|
|
35
|
+
nationalId: '1' + (199500000000 + i * 137 + randInt(0,9)).toString(),
|
|
36
|
+
phone: '+25078' + randInt(1000000, 9999999),
|
|
37
|
+
email: `${fn.toLowerCase()}.${ln.toLowerCase()}${i}@example.rw`,
|
|
38
|
+
address: rand(['Huye','Kigali','Musanze','Rubavu','Nyagatare','Muhanga']) + ', Rwanda'
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log('Creating 20 vehicles...');
|
|
43
|
+
const vehicles = [];
|
|
44
|
+
for (let i = 0; i < 20; i++) {
|
|
45
|
+
const [brand, model] = rand(brands);
|
|
46
|
+
vehicles.push(await Vehicle.create({
|
|
47
|
+
plateNumber: 'RAB' + randInt(100,999) + String.fromCharCode(65+randInt(0,25)),
|
|
48
|
+
brand, model, year: randInt(2015, 2024),
|
|
49
|
+
vehicleType: rand(types),
|
|
50
|
+
purchasePrice: randInt(15000, 60000),
|
|
51
|
+
dailyRate: randInt(40, 200),
|
|
52
|
+
status: 'AVAILABLE'
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log('Creating 30 reservations...');
|
|
57
|
+
const statuses = ['PENDING','APPROVED','COMPLETED','REJECTED','CANCELLED'];
|
|
58
|
+
for (let i = 0; i < 30; i++) {
|
|
59
|
+
const start = new Date(); start.setDate(start.getDate() - randInt(-15, 60));
|
|
60
|
+
const end = new Date(start); end.setDate(end.getDate() + randInt(1, 10));
|
|
61
|
+
const days = Math.max(1, Math.ceil((end-start)/86400000));
|
|
62
|
+
const v = rand(vehicles);
|
|
63
|
+
const status = rand(statuses);
|
|
64
|
+
await RR.create({
|
|
65
|
+
customerId: rand(customers)._id,
|
|
66
|
+
vehicleId: v._id,
|
|
67
|
+
userId: rand(users)._id,
|
|
68
|
+
startDate: start, endDate: end,
|
|
69
|
+
reservationStatus: status,
|
|
70
|
+
rentalDate: status === 'COMPLETED' ? start : null,
|
|
71
|
+
returnDate: status === 'COMPLETED' ? end : null,
|
|
72
|
+
rentalStatus: status === 'COMPLETED' ? 'RETURNED' : 'NOT_STARTED',
|
|
73
|
+
rentalFee: days * v.dailyRate
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log('✅ Seed complete');
|
|
78
|
+
console.log('Login: admin/admin123, manager/manager123, staff/staff123');
|
|
79
|
+
await mongoose.disconnect();
|
|
80
|
+
})();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require('dotenv').config();
|
|
2
|
+
const express = require('express');
|
|
3
|
+
const cors = require('cors');
|
|
4
|
+
const helmet = require('helmet');
|
|
5
|
+
const morgan = require('morgan');
|
|
6
|
+
const rateLimit = require('express-rate-limit');
|
|
7
|
+
const connectDB = require('./config/db');
|
|
8
|
+
|
|
9
|
+
const authRoutes = require('./routes/auth.routes');
|
|
10
|
+
const customerRoutes = require('./routes/customer.routes');
|
|
11
|
+
const vehicleRoutes = require('./routes/vehicle.routes');
|
|
12
|
+
const reservationRoutes = require('./routes/reservation.routes');
|
|
13
|
+
const userRoutes = require('./routes/user.routes');
|
|
14
|
+
const reportRoutes = require('./routes/report.routes');
|
|
15
|
+
const dashboardRoutes = require('./routes/dashboard.routes');
|
|
16
|
+
const errorHandler = require('./middleware/errorHandler');
|
|
17
|
+
|
|
18
|
+
const app = express();
|
|
19
|
+
connectDB();
|
|
20
|
+
|
|
21
|
+
app.use(helmet());
|
|
22
|
+
app.use(cors({ origin: process.env.CLIENT_URL || '*', credentials: true }));
|
|
23
|
+
app.use(express.json({ limit: '5mb' }));
|
|
24
|
+
app.use(morgan('dev'));
|
|
25
|
+
app.use('/api/', rateLimit({ windowMs: 15 * 60 * 1000, max: 500 }));
|
|
26
|
+
|
|
27
|
+
app.get('/', (req, res) => res.json({ message: 'VRS API running', version: '1.0.0' }));
|
|
28
|
+
app.use('/api/auth', authRoutes);
|
|
29
|
+
app.use('/api/customers', customerRoutes);
|
|
30
|
+
app.use('/api/vehicles', vehicleRoutes);
|
|
31
|
+
app.use('/api/reservations', reservationRoutes);
|
|
32
|
+
app.use('/api/users', userRoutes);
|
|
33
|
+
app.use('/api/reports', reportRoutes);
|
|
34
|
+
app.use('/api/dashboard', dashboardRoutes);
|
|
35
|
+
|
|
36
|
+
app.use((req, res) => res.status(404).json({ message: 'Route not found' }));
|
|
37
|
+
app.use(errorHandler);
|
|
38
|
+
|
|
39
|
+
const PORT = process.env.PORT || 5000;
|
|
40
|
+
app.listen(PORT, () => console.log(`🚗 VRS Backend running on port ${PORT}`));
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const jwt = require('jsonwebtoken');
|
|
2
|
+
exports.signAccess = (user) => jwt.sign({ id: user._id, role: user.role }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES_IN || '1d' });
|
|
3
|
+
exports.signRefresh = (user) => jwt.sign({ id: user._id }, process.env.JWT_REFRESH_SECRET, { expiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d' });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>SwiftWheels VRS</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
<script type="module" src="/src/main.jsx"></script>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|