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.
Files changed (64) hide show
  1. package/bin/index.js +30 -0
  2. package/package.json +29 -0
  3. package/templates/VehicleRentalReservationSystem/Documentation/API.md +57 -0
  4. package/templates/VehicleRentalReservationSystem/Documentation/DFD.md +62 -0
  5. package/templates/VehicleRentalReservationSystem/Documentation/ERD.md +109 -0
  6. package/templates/VehicleRentalReservationSystem/Documentation/Installation.md +40 -0
  7. package/templates/VehicleRentalReservationSystem/README.md +165 -0
  8. package/templates/VehicleRentalReservationSystem/backend-project/.env.example +8 -0
  9. package/templates/VehicleRentalReservationSystem/backend-project/config/db.js +10 -0
  10. package/templates/VehicleRentalReservationSystem/backend-project/controllers/auth.controller.js +39 -0
  11. package/templates/VehicleRentalReservationSystem/backend-project/controllers/customer.controller.js +19 -0
  12. package/templates/VehicleRentalReservationSystem/backend-project/controllers/dashboard.controller.js +48 -0
  13. package/templates/VehicleRentalReservationSystem/backend-project/controllers/report.controller.js +45 -0
  14. package/templates/VehicleRentalReservationSystem/backend-project/controllers/reservation.controller.js +94 -0
  15. package/templates/VehicleRentalReservationSystem/backend-project/controllers/user.controller.js +16 -0
  16. package/templates/VehicleRentalReservationSystem/backend-project/controllers/vehicle.controller.js +20 -0
  17. package/templates/VehicleRentalReservationSystem/backend-project/middleware/auth.js +22 -0
  18. package/templates/VehicleRentalReservationSystem/backend-project/middleware/errorHandler.js +10 -0
  19. package/templates/VehicleRentalReservationSystem/backend-project/middleware/validate.js +6 -0
  20. package/templates/VehicleRentalReservationSystem/backend-project/models/Customer.js +11 -0
  21. package/templates/VehicleRentalReservationSystem/backend-project/models/ReservationRental.js +17 -0
  22. package/templates/VehicleRentalReservationSystem/backend-project/models/User.js +20 -0
  23. package/templates/VehicleRentalReservationSystem/backend-project/models/Vehicle.js +14 -0
  24. package/templates/VehicleRentalReservationSystem/backend-project/package-lock.json +1695 -0
  25. package/templates/VehicleRentalReservationSystem/backend-project/package.json +26 -0
  26. package/templates/VehicleRentalReservationSystem/backend-project/routes/auth.routes.js +21 -0
  27. package/templates/VehicleRentalReservationSystem/backend-project/routes/customer.routes.js +20 -0
  28. package/templates/VehicleRentalReservationSystem/backend-project/routes/dashboard.routes.js +6 -0
  29. package/templates/VehicleRentalReservationSystem/backend-project/routes/report.routes.js +6 -0
  30. package/templates/VehicleRentalReservationSystem/backend-project/routes/reservation.routes.js +14 -0
  31. package/templates/VehicleRentalReservationSystem/backend-project/routes/user.routes.js +10 -0
  32. package/templates/VehicleRentalReservationSystem/backend-project/routes/vehicle.routes.js +21 -0
  33. package/templates/VehicleRentalReservationSystem/backend-project/seed/seed.js +80 -0
  34. package/templates/VehicleRentalReservationSystem/backend-project/server.js +40 -0
  35. package/templates/VehicleRentalReservationSystem/backend-project/utils/token.js +3 -0
  36. package/templates/VehicleRentalReservationSystem/frontend-project/index.html +14 -0
  37. package/templates/VehicleRentalReservationSystem/frontend-project/package-lock.json +3759 -0
  38. package/templates/VehicleRentalReservationSystem/frontend-project/package.json +32 -0
  39. package/templates/VehicleRentalReservationSystem/frontend-project/postcss.config.js +1 -0
  40. package/templates/VehicleRentalReservationSystem/frontend-project/src/App.jsx +33 -0
  41. package/templates/VehicleRentalReservationSystem/frontend-project/src/components/ConfirmDelete.jsx +12 -0
  42. package/templates/VehicleRentalReservationSystem/frontend-project/src/components/Modal.jsx +22 -0
  43. package/templates/VehicleRentalReservationSystem/frontend-project/src/components/ProtectedRoute.jsx +9 -0
  44. package/templates/VehicleRentalReservationSystem/frontend-project/src/components/Sidebar.jsx +42 -0
  45. package/templates/VehicleRentalReservationSystem/frontend-project/src/components/Topbar.jsx +24 -0
  46. package/templates/VehicleRentalReservationSystem/frontend-project/src/context/AuthContext.jsx +30 -0
  47. package/templates/VehicleRentalReservationSystem/frontend-project/src/context/ThemeContext.jsx +13 -0
  48. package/templates/VehicleRentalReservationSystem/frontend-project/src/index.css +37 -0
  49. package/templates/VehicleRentalReservationSystem/frontend-project/src/layouts/AppLayout.jsx +18 -0
  50. package/templates/VehicleRentalReservationSystem/frontend-project/src/main.jsx +20 -0
  51. package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Customers.jsx +95 -0
  52. package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Dashboard.jsx +84 -0
  53. package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Login.jsx +54 -0
  54. package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Register.jsx +36 -0
  55. package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Rentals.jsx +58 -0
  56. package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Reports.jsx +108 -0
  57. package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Reservations.jsx +125 -0
  58. package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Settings.jsx +32 -0
  59. package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Users.jsx +78 -0
  60. package/templates/VehicleRentalReservationSystem/frontend-project/src/pages/Vehicles.jsx +110 -0
  61. package/templates/VehicleRentalReservationSystem/frontend-project/src/services/api.js +16 -0
  62. package/templates/VehicleRentalReservationSystem/frontend-project/src/utils/format.js +20 -0
  63. package/templates/VehicleRentalReservationSystem/frontend-project/tailwind.config.js +18 -0
  64. package/templates/VehicleRentalReservationSystem/frontend-project/vite.config.js +9 -0
package/bin/index.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs-extra");
4
+ const path = require("path");
5
+
6
+ const projectName = process.argv[2];
7
+
8
+ if (!projectName) {
9
+ console.log("Please provide project name");
10
+ process.exit(1);
11
+ }
12
+
13
+ const templateDir = path.join(
14
+ __dirname,
15
+ "../templates/VehicleRentalReservationSystem"
16
+ );
17
+
18
+ const targetDir = path.join(process.cwd(), projectName);
19
+
20
+ fs.copySync(templateDir, targetDir);
21
+
22
+ console.log("");
23
+ console.log(" Project created successfully!");
24
+ console.log(`📁 ${projectName}`);
25
+ console.log("");
26
+ console.log("Next steps:");
27
+ console.log(`cd ${projectName}`);
28
+ console.log("cd backend-project && npm install");
29
+ console.log("cd frontend-project && npm install");
30
+ console.log("");
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "create-vrrsystem-app",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "create-vrrs-app": "bin/index.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "templates"
12
+ ],
13
+ "scripts": {
14
+ "test": "echo \"Error: no test specified\" && exit 1"
15
+ },
16
+ "keywords": [
17
+ "react",
18
+ "nodejs",
19
+ "mongodb",
20
+ "vrr",
21
+ "vehicle-rental"
22
+ ],
23
+ "author": "Teta",
24
+ "license": "MIT",
25
+ "type": "commonjs",
26
+ "dependencies": {
27
+ "fs-extra": "^11.3.5"
28
+ }
29
+ }
@@ -0,0 +1,57 @@
1
+ # API Documentation
2
+
3
+ Base URL: `http://localhost:5000/api`
4
+
5
+ All endpoints (except `/auth/login`, `/auth/register`, `/auth/refresh`) require:
6
+ ```
7
+ Authorization: Bearer <accessToken>
8
+ ```
9
+
10
+ ## Auth
11
+ ### POST /auth/login
12
+ Body:
13
+ ```json
14
+ { "username": "admin", "password": "admin123" }
15
+ ```
16
+ Response:
17
+ ```json
18
+ {
19
+ "user": { "_id": "...", "username": "admin", "role": "ADMIN" },
20
+ "accessToken": "...",
21
+ "refreshToken": "..."
22
+ }
23
+ ```
24
+
25
+ ### POST /auth/register
26
+ Body: `{ username, password, fullName?, email?, role? }`
27
+
28
+ ### POST /auth/refresh
29
+ Body: `{ refreshToken }`
30
+
31
+ ## Customers
32
+ - `GET /customers?search=&page=1&limit=10`
33
+ - `POST /customers` — `{ fullName, nationalId, phone, email, address }`
34
+ - `PUT /customers/:id`
35
+ - `DELETE /customers/:id` (ADMIN)
36
+
37
+ ## Vehicles
38
+ - `GET /vehicles?search=&status=AVAILABLE&page=1&limit=10`
39
+ - `POST /vehicles` (ADMIN) — `{ plateNumber, brand, model, year, vehicleType, purchasePrice, dailyRate }`
40
+
41
+ ## Reservations
42
+ - `GET /reservations?status=PENDING`
43
+ - `POST /reservations` — `{ customerId, vehicleId, startDate, endDate }`
44
+ - `PATCH /reservations/:id/approve` (ADMIN/MANAGER)
45
+ - `PATCH /reservations/:id/reject` (ADMIN/MANAGER)
46
+ - `PATCH /reservations/:id/pickup` (ADMIN/STAFF)
47
+ - `PATCH /reservations/:id/return` (ADMIN/STAFF)
48
+
49
+ ## Reports
50
+ - `GET /reports?period=monthly` — period ∈ `daily|weekly|monthly|yearly|all`
51
+ - Returns: `{ period, from, to, count, totalRevenue, rows[] }`
52
+
53
+ ## Dashboard
54
+ - `GET /dashboard` — full stats payload
55
+
56
+ ## Users (ADMIN)
57
+ - `GET/POST/PUT/DELETE /users[/:id]`
@@ -0,0 +1,62 @@
1
+ # Data Flow Diagram (DFD)
2
+
3
+ ## External Entities
4
+ - **Customer** — provides personal info, requests reservations
5
+ - **Staff** — creates reservations, performs pickup/return
6
+ - **Manager** — approves/rejects, reviews reports
7
+ - **Admin** — manages users and master data
8
+
9
+ ## Data Stores
10
+ - D1: Customers
11
+ - D2: Vehicles
12
+ - D3: Reservations / Rentals
13
+ - D4: Users
14
+
15
+ ## Context Diagram (Level 0 — single process)
16
+
17
+ ```mermaid
18
+ flowchart LR
19
+ C[Customer] -->|Reservation Request| VRS((Vehicle Rental & Reservation System))
20
+ S[Staff] -->|Customer & Reservation Data| VRS
21
+ M[Manager] -->|Approvals / Report Requests| VRS
22
+ A[Admin] -->|User & Master Data Mgmt| VRS
23
+ VRS -->|Receipts / Confirmations| C
24
+ VRS -->|Operational Lists| S
25
+ VRS -->|Reports / Analytics| M
26
+ VRS -->|System Configurations| A
27
+ ```
28
+
29
+ ## Level 0 DFD (decomposition)
30
+
31
+ ```mermaid
32
+ flowchart TB
33
+ C[Customer] --> P1[1.0 Manage Customers]
34
+ S[Staff] --> P1
35
+ P1 <--> D1[(D1 Customers)]
36
+
37
+ A[Admin] --> P2[2.0 Manage Vehicles]
38
+ P2 <--> D2[(D2 Vehicles)]
39
+
40
+ S --> P3[3.0 Manage Reservations]
41
+ P3 <--> D1
42
+ P3 <--> D2
43
+ P3 <--> D3[(D3 Reservations/Rentals)]
44
+
45
+ S --> P4[4.0 Manage Rentals]
46
+ P4 <--> D2
47
+ P4 <--> D3
48
+
49
+ M[Manager] --> P5[5.0 Approvals]
50
+ P5 <--> D3
51
+
52
+ M --> P6[6.0 Reports & Analytics]
53
+ P6 <--> D1
54
+ P6 <--> D2
55
+ P6 <--> D3
56
+
57
+ A --> P7[7.0 Manage Users]
58
+ P7 <--> D4[(D4 Users)]
59
+
60
+ P6 -->|PDF / Excel / CSV| M
61
+ P3 -->|Confirmation| C
62
+ ```
@@ -0,0 +1,109 @@
1
+ # Entity Relationship Diagram (ERD)
2
+
3
+ ## Entities
4
+
5
+ ### CUSTOMER
6
+ | Field | Type | Key |
7
+ |--------------|---------|-----|
8
+ | customerId | ObjectId| PK |
9
+ | fullName | String | |
10
+ | nationalId | String | UQ |
11
+ | phone | String | |
12
+ | email | String | |
13
+ | address | String | |
14
+
15
+ ### VEHICLE
16
+ | Field | Type | Key |
17
+ |---------------|----------|-----|
18
+ | vehicleId | ObjectId | PK |
19
+ | plateNumber | String | UQ |
20
+ | brand | String | |
21
+ | model | String | |
22
+ | year | Number | |
23
+ | vehicleType | String | |
24
+ | purchasePrice | Number | |
25
+ | dailyRate | Number | |
26
+ | status | Enum | |
27
+
28
+ ### RESERVATION_RENTAL
29
+ | Field | Type | Key |
30
+ |---------------------|----------|-----------|
31
+ | reservationRentalId | ObjectId | PK |
32
+ | customerId | ObjectId | FK→CUSTOMER|
33
+ | vehicleId | ObjectId | FK→VEHICLE |
34
+ | userId | ObjectId | FK→USERS |
35
+ | reservationDate | Date | |
36
+ | startDate | Date | |
37
+ | endDate | Date | |
38
+ | reservationStatus | Enum | |
39
+ | rentalDate | Date | |
40
+ | returnDate | Date | |
41
+ | rentalFee | Number | |
42
+ | rentalStatus | Enum | |
43
+
44
+ ### USERS
45
+ | Field | Type | Key |
46
+ |----------|----------|-----|
47
+ | userId | ObjectId | PK |
48
+ | username | String | UQ |
49
+ | password | String | |
50
+ | role | Enum | |
51
+
52
+ ## Cardinalities
53
+
54
+ - CUSTOMER (1) ──< (N) RESERVATION_RENTAL
55
+ - VEHICLE (1) ──< (N) RESERVATION_RENTAL
56
+ - USERS (1) ──< (N) RESERVATION_RENTAL
57
+
58
+ ## Mermaid ERD
59
+
60
+ ```mermaid
61
+ erDiagram
62
+ CUSTOMER ||--o{ RESERVATION_RENTAL : "places"
63
+ VEHICLE ||--o{ RESERVATION_RENTAL : "is reserved in"
64
+ USERS ||--o{ RESERVATION_RENTAL : "creates"
65
+
66
+ CUSTOMER {
67
+ ObjectId customerId PK
68
+ string fullName
69
+ string nationalId UK
70
+ string phone
71
+ string email
72
+ string address
73
+ }
74
+ VEHICLE {
75
+ ObjectId vehicleId PK
76
+ string plateNumber UK
77
+ string brand
78
+ string model
79
+ int year
80
+ string vehicleType
81
+ float purchasePrice
82
+ float dailyRate
83
+ string status
84
+ }
85
+ RESERVATION_RENTAL {
86
+ ObjectId reservationRentalId PK
87
+ ObjectId customerId FK
88
+ ObjectId vehicleId FK
89
+ ObjectId userId FK
90
+ date reservationDate
91
+ date startDate
92
+ date endDate
93
+ string reservationStatus
94
+ date rentalDate
95
+ date returnDate
96
+ float rentalFee
97
+ string rentalStatus
98
+ }
99
+ USERS {
100
+ ObjectId userId PK
101
+ string username UK
102
+ string password
103
+ string role
104
+ }
105
+ ```
106
+
107
+ ## Draw.io compatible (text)
108
+
109
+ Use Draw.io → Extras → Edit Diagram with the Mermaid snippet above (Draw.io supports Mermaid import directly).
@@ -0,0 +1,40 @@
1
+ # Installation Guide
2
+
3
+ ## Requirements
4
+ - Node.js ≥ 18
5
+ - npm ≥ 9
6
+ - MongoDB ≥ 6 (running on `mongodb://localhost:27017`)
7
+
8
+ ## Backend
9
+ ```bash
10
+ cd backend-project
11
+ npm install
12
+ # .env is already created from .env.example
13
+ npm run seed # seed DB (users + customers + vehicles + reservations)
14
+ npm run dev # http://localhost:5000
15
+ ```
16
+
17
+ ## Frontend
18
+ ```bash
19
+ cd frontend-project
20
+ npm install
21
+ npm run dev # http://localhost:5173
22
+ ```
23
+
24
+ ## Login
25
+ - admin / admin123
26
+ - manager / manager123
27
+ - staff / staff123
28
+
29
+ ## MongoDB Setup
30
+ - Install MongoDB Community Server (https://www.mongodb.com/try/download/community)
31
+ - Start the service:
32
+ - macOS: `brew services start mongodb-community`
33
+ - Linux: `sudo systemctl start mongod`
34
+ - Windows: MongoDB runs as a service after install
35
+ - The database `VRS` is created automatically on first connection.
36
+
37
+ ## Deployment
38
+ - **Backend** → deploy to Render / Railway / Heroku / VPS. Set env vars (`MONGO_URI`, `JWT_SECRET`, `JWT_REFRESH_SECRET`, `CLIENT_URL`).
39
+ - **Frontend** → `npm run build` then deploy `dist/` to Netlify / Vercel / static host. Set `VITE_API_URL` and adjust `vite.config.js` proxy or call full URL.
40
+ - **Database** → MongoDB Atlas free tier recommended for production.
@@ -0,0 +1,165 @@
1
+ # 🚗 Vehicle Rental & Reservation System (VRS)
2
+
3
+ **SwiftWheels Enterprises — Huye City, Southern Province, Rwanda**
4
+
5
+ A complete production-ready MERN stack web application that digitizes SwiftWheels' previously paper-based operations: vehicle reservations, rentals, customers, vehicles, staff activities, and management reporting.
6
+
7
+ ---
8
+
9
+ ## 🧱 Tech Stack
10
+
11
+ **Frontend:** React 18, Vite, Tailwind CSS, Axios, React Router DOM, Framer Motion, React Hook Form, Recharts, React Icons, React Toastify, jsPDF, xlsx
12
+ **Backend:** Node.js, Express, MongoDB, Mongoose, JWT, bcryptjs, Helmet, CORS, Express Validator, express-rate-limit
13
+ **Database:** MongoDB (database name: `VRS`)
14
+
15
+ ---
16
+
17
+ ## 📁 Project Structure
18
+
19
+ ```
20
+ VehicleRentalReservationSystem/
21
+ ├── backend-project/ Express + MongoDB API (MVC)
22
+ ├── frontend-project/ React + Vite + Tailwind UI
23
+ ├── Documentation/ ERD, DFD, API docs, deployment guide
24
+ └── README.md
25
+ ```
26
+
27
+ ---
28
+
29
+ ## ⚙️ Installation
30
+
31
+ ### 1. Prerequisites
32
+ - Node.js ≥ 18
33
+ - MongoDB running locally on `mongodb://localhost:27017`
34
+ - Git
35
+
36
+ ### 2. Backend
37
+
38
+ ```bash
39
+ cd backend-project
40
+ npm install
41
+ cp .env.example .env # already provided
42
+ npm run seed # creates default users + 20 customers + 20 vehicles + 30 reservations
43
+ npm run dev # starts API at http://localhost:5000
44
+ ```
45
+
46
+ ### 3. Frontend
47
+
48
+ ```bash
49
+ cd frontend-project
50
+ npm install
51
+ npm run dev # starts UI at http://localhost:5173
52
+ ```
53
+
54
+ The frontend proxies `/api/*` to the backend, so no extra config is needed.
55
+
56
+ ---
57
+
58
+ ## 🔐 Default Accounts
59
+
60
+ | Role | Username | Password |
61
+ |----------|----------|-------------|
62
+ | Admin | admin | admin123 |
63
+ | Manager | manager | manager123 |
64
+ | Staff | staff | staff123 |
65
+
66
+ ---
67
+
68
+ ## 👥 Roles & Permissions
69
+
70
+ | Module | ADMIN | MANAGER | STAFF |
71
+ |---------------|:-----:|:-------:|:-----:|
72
+ | Dashboard | ✅ | ✅ | ✅ |
73
+ | Customers | ✅ | 👁 | ✅ |
74
+ | Vehicles | ✅ | 👁 | 👁 |
75
+ | Reservations | ✅ | approve| ✅ |
76
+ | Rentals | ✅ | 👁 | ✅ |
77
+ | Reports | ✅ | ✅ | ✅ |
78
+ | Users | ✅ | — | — |
79
+
80
+ ---
81
+
82
+ ## 🌟 Features
83
+
84
+ - 🔐 **JWT Authentication** with refresh tokens, password hashing (bcrypt), protected routes
85
+ - 👤 **Role-Based Access Control** (ADMIN / MANAGER / STAFF)
86
+ - 📊 **Analytics Dashboard** — total/available/reserved/rented vehicles, customers, revenue, recent activity, charts (Recharts)
87
+ - 🚗 **Vehicles Module** — CRUD, status management (AVAILABLE / RESERVED / RENTED / MAINTENANCE), search, filter, pagination
88
+ - 👥 **Customers Module** — CRUD, validation (NID/phone/email), search, pagination
89
+ - 📅 **Reservations Module** — create, approve, reject, pickup, return; automatic fee calculation; late-return surcharge (1.5×)
90
+ - 🔑 **Rentals Module** — pickup/return workflow tracking
91
+ - 📑 **Reports Module** — Daily / Weekly / Monthly / Yearly / All-time
92
+ - 📥 **Export** — PDF (jsPDF), Excel (xlsx), CSV, Print
93
+ - 🌗 **Dark / Light Mode** with persistence
94
+ - 💎 **Premium Glassmorphism UI** with Tailwind + Framer Motion animations
95
+ - 🛡 **Security** — Helmet, CORS, rate limiting, input validation, sanitization
96
+
97
+ ---
98
+
99
+ ## 🌐 API Endpoints
100
+
101
+ ### Auth
102
+ - `POST /api/auth/register` — register user
103
+ - `POST /api/auth/login` — login (returns `accessToken`, `refreshToken`)
104
+ - `POST /api/auth/refresh` — refresh access token
105
+ - `POST /api/auth/logout`
106
+ - `GET /api/auth/me`
107
+
108
+ ### Customers
109
+ - `GET /api/customers` `?search=&page=&limit=`
110
+ - `GET /api/customers/:id`
111
+ - `POST /api/customers`
112
+ - `PUT /api/customers/:id`
113
+ - `DELETE /api/customers/:id`
114
+
115
+ ### Vehicles
116
+ - `GET /api/vehicles` `?search=&status=&page=&limit=`
117
+ - `GET /api/vehicles/:id`
118
+ - `POST /api/vehicles`
119
+ - `PUT /api/vehicles/:id`
120
+ - `DELETE /api/vehicles/:id`
121
+
122
+ ### Reservations / Rentals
123
+ - `GET /api/reservations`
124
+ - `POST /api/reservations`
125
+ - `PUT /api/reservations/:id`
126
+ - `DELETE /api/reservations/:id`
127
+ - `PATCH /api/reservations/:id/approve`
128
+ - `PATCH /api/reservations/:id/reject`
129
+ - `PATCH /api/reservations/:id/pickup`
130
+ - `PATCH /api/reservations/:id/return`
131
+
132
+ ### Reports
133
+ - `GET /api/reports?period=daily|weekly|monthly|yearly|all`
134
+
135
+ ### Dashboard
136
+ - `GET /api/dashboard`
137
+
138
+ ### Users (ADMIN only)
139
+ - `GET / POST / PUT / DELETE /api/users[/:id]`
140
+
141
+ ---
142
+
143
+ ## 🗄 Database (MongoDB — `VRS`)
144
+
145
+ Collections: `users`, `customers`, `vehicles`, `reservationrentals`
146
+
147
+ See **Documentation/ERD.md** and **Documentation/DFD.md** for full diagrams.
148
+
149
+ ---
150
+
151
+ ## 🎨 Design Tokens
152
+
153
+ | Token | Value |
154
+ |-------------|-----------|
155
+ | Primary | `#1E293B` |
156
+ | Secondary | `#334155` |
157
+ | Accent | `#2563EB` |
158
+ | Background | `#F8FAFC` |
159
+ | Dark BG | `#0F172A` |
160
+
161
+ ---
162
+
163
+ ## 📜 License
164
+
165
+ Proprietary — © SwiftWheels Enterprises, Huye, Rwanda.
@@ -0,0 +1,8 @@
1
+ PORT=5000
2
+ MONGO_URI=mongodb://localhost:27017/VRS
3
+ JWT_SECRET=swiftwheels_super_secret_key_change_me
4
+ JWT_REFRESH_SECRET=swiftwheels_refresh_secret_key_change_me
5
+ JWT_EXPIRES_IN=1d
6
+ JWT_REFRESH_EXPIRES_IN=7d
7
+ NODE_ENV=development
8
+ CLIENT_URL=http://localhost:5173
@@ -0,0 +1,10 @@
1
+ const mongoose = require('mongoose');
2
+ module.exports = async function connectDB() {
3
+ try {
4
+ await mongoose.connect(process.env.MONGO_URI);
5
+ console.log('✅ MongoDB connected:', mongoose.connection.name);
6
+ } catch (err) {
7
+ console.error('❌ MongoDB error:', err.message);
8
+ process.exit(1);
9
+ }
10
+ };
@@ -0,0 +1,39 @@
1
+ const User = require('../models/User');
2
+ const jwt = require('jsonwebtoken');
3
+ const { signAccess, signRefresh } = require('../utils/token');
4
+
5
+ exports.register = async (req, res, next) => {
6
+ try {
7
+ const { username, password, fullName, email, role } = req.body;
8
+ const exists = await User.findOne({ username });
9
+ if (exists) return res.status(409).json({ message: 'Username already exists' });
10
+ const user = await User.create({ username, password, fullName, email, role: role || 'STAFF' });
11
+ res.status(201).json({ user, accessToken: signAccess(user), refreshToken: signRefresh(user) });
12
+ } catch (e) { next(e); }
13
+ };
14
+
15
+ exports.login = async (req, res, next) => {
16
+ try {
17
+ const { username, password } = req.body;
18
+ const user = await User.findOne({ username });
19
+ if (!user) return res.status(401).json({ message: 'Invalid credentials' });
20
+ const ok = await user.compare(password);
21
+ if (!ok) return res.status(401).json({ message: 'Invalid credentials' });
22
+ if (!user.active) return res.status(403).json({ message: 'Account disabled' });
23
+ res.json({ user, accessToken: signAccess(user), refreshToken: signRefresh(user) });
24
+ } catch (e) { next(e); }
25
+ };
26
+
27
+ exports.refresh = async (req, res, next) => {
28
+ try {
29
+ const { refreshToken } = req.body;
30
+ if (!refreshToken) return res.status(400).json({ message: 'No refresh token' });
31
+ const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
32
+ const user = await User.findById(decoded.id);
33
+ if (!user) return res.status(401).json({ message: 'Invalid token' });
34
+ res.json({ accessToken: signAccess(user), refreshToken: signRefresh(user) });
35
+ } catch (e) { res.status(401).json({ message: 'Invalid refresh token' }); }
36
+ };
37
+
38
+ exports.logout = (req, res) => res.json({ message: 'Logged out' });
39
+ exports.me = (req, res) => res.json({ user: req.user });
@@ -0,0 +1,19 @@
1
+ const Customer = require('../models/Customer');
2
+ exports.list = async (req, res, next) => {
3
+ try {
4
+ const { search = '', page = 1, limit = 10 } = req.query;
5
+ const q = search ? { $or: [
6
+ { fullName: new RegExp(search, 'i') },
7
+ { nationalId: new RegExp(search, 'i') },
8
+ { phone: new RegExp(search, 'i') },
9
+ { email: new RegExp(search, 'i') }
10
+ ]} : {};
11
+ const total = await Customer.countDocuments(q);
12
+ const items = await Customer.find(q).sort('-createdAt').skip((page-1)*limit).limit(+limit);
13
+ res.json({ items, total, page: +page, pages: Math.ceil(total/limit) });
14
+ } catch (e) { next(e); }
15
+ };
16
+ exports.get = async (req, res, next) => { try { const c = await Customer.findById(req.params.id); if(!c) return res.status(404).json({message:'Not found'}); res.json(c);} catch(e){next(e);} };
17
+ exports.create = async (req, res, next) => { try { res.status(201).json(await Customer.create(req.body)); } catch(e){next(e);} };
18
+ exports.update = async (req, res, next) => { try { const c = await Customer.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true }); if(!c) return res.status(404).json({message:'Not found'}); res.json(c);} catch(e){next(e);} };
19
+ exports.remove = async (req, res, next) => { try { await Customer.findByIdAndDelete(req.params.id); res.json({ message: 'Deleted' }); } catch(e){next(e);} };
@@ -0,0 +1,48 @@
1
+ const Vehicle = require('../models/Vehicle');
2
+ const Customer = require('../models/Customer');
3
+ const RR = require('../models/ReservationRental');
4
+
5
+ exports.stats = async (req, res, next) => {
6
+ try {
7
+ const [totalVehicles, available, reserved, rented, customers, reservations] = await Promise.all([
8
+ Vehicle.countDocuments(),
9
+ Vehicle.countDocuments({ status: 'AVAILABLE' }),
10
+ Vehicle.countDocuments({ status: 'RESERVED' }),
11
+ Vehicle.countDocuments({ status: 'RENTED' }),
12
+ Customer.countDocuments(),
13
+ RR.countDocuments()
14
+ ]);
15
+ const revenueAgg = await RR.aggregate([{ $group: { _id: null, total: { $sum: '$rentalFee' } } }]);
16
+ const totalRevenue = revenueAgg[0]?.total || 0;
17
+ const startOfMonth = new Date(); startOfMonth.setDate(1); startOfMonth.setHours(0,0,0,0);
18
+ const monthlyAgg = await RR.aggregate([
19
+ { $match: { createdAt: { $gte: startOfMonth } } },
20
+ { $group: { _id: null, total: { $sum: '$rentalFee' } } }
21
+ ]);
22
+ const monthlyRevenue = monthlyAgg[0]?.total || 0;
23
+
24
+ const recentReservations = await RR.find().sort('-createdAt').limit(5).populate('customerId').populate('vehicleId');
25
+ const recentRentals = await RR.find({ rentalDate: { $ne: null } }).sort('-rentalDate').limit(5).populate('customerId').populate('vehicleId');
26
+
27
+ // monthly revenue chart - last 6 months
28
+ const months = [];
29
+ for (let i = 5; i >= 0; i--) {
30
+ const d = new Date(); d.setMonth(d.getMonth() - i); d.setDate(1); d.setHours(0,0,0,0);
31
+ const end = new Date(d); end.setMonth(end.getMonth() + 1);
32
+ const agg = await RR.aggregate([
33
+ { $match: { createdAt: { $gte: d, $lt: end } } },
34
+ { $group: { _id: null, total: { $sum: '$rentalFee' }, count: { $sum: 1 } } }
35
+ ]);
36
+ months.push({ month: d.toLocaleString('en', { month: 'short' }), revenue: agg[0]?.total || 0, count: agg[0]?.count || 0 });
37
+ }
38
+
39
+ const vehicleTypeAgg = await Vehicle.aggregate([{ $group: { _id: '$vehicleType', count: { $sum: 1 } } }]);
40
+
41
+ res.json({
42
+ totalVehicles, available, reserved, rented, customers, reservations,
43
+ totalRevenue, monthlyRevenue,
44
+ recentReservations, recentRentals, monthlyChart: months,
45
+ vehicleTypes: vehicleTypeAgg.map(x => ({ type: x._id, count: x.count }))
46
+ });
47
+ } catch (e) { next(e); }
48
+ };
@@ -0,0 +1,45 @@
1
+ const RR = require('../models/ReservationRental');
2
+
3
+ function rangeFor(period) {
4
+ const end = new Date();
5
+ const start = new Date();
6
+ if (period === 'daily') start.setHours(0,0,0,0);
7
+ else if (period === 'weekly') start.setDate(start.getDate() - 7);
8
+ else if (period === 'monthly') start.setMonth(start.getMonth() - 1);
9
+ else if (period === 'yearly') start.setFullYear(start.getFullYear() - 1);
10
+ else start.setFullYear(2000);
11
+ return { start, end };
12
+ }
13
+
14
+ exports.generate = async (req, res, next) => {
15
+ try {
16
+ const { period = 'monthly', from, to } = req.query;
17
+ let start, end;
18
+ if (from && to) { start = new Date(from); end = new Date(to); }
19
+ else ({ start, end } = rangeFor(period));
20
+
21
+ const items = await RR.find({ createdAt: { $gte: start, $lte: end } })
22
+ .populate('customerId').populate('vehicleId').populate('userId', 'username fullName')
23
+ .sort('-createdAt');
24
+
25
+ const rows = items.map(r => ({
26
+ customerName: r.customerId?.fullName || '-',
27
+ nationalId: r.customerId?.nationalId || '-',
28
+ phone: r.customerId?.phone || '-',
29
+ plateNumber: r.vehicleId?.plateNumber || '-',
30
+ brand: r.vehicleId?.brand || '-',
31
+ model: r.vehicleId?.model || '-',
32
+ year: r.vehicleId?.year || '-',
33
+ vehicleType: r.vehicleId?.vehicleType || '-',
34
+ reservationDate: r.reservationDate,
35
+ rentalDate: r.rentalDate,
36
+ returnDate: r.returnDate,
37
+ reservationStatus: r.reservationStatus,
38
+ rentalStatus: r.rentalStatus,
39
+ rentalFee: r.rentalFee
40
+ }));
41
+
42
+ const totalRevenue = rows.reduce((s, r) => s + (r.rentalFee || 0), 0);
43
+ res.json({ period, from: start, to: end, count: rows.length, totalRevenue, rows });
44
+ } catch (e) { next(e); }
45
+ };