create-myexam-app 1.0.24 → 1.0.26
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/index.js +28 -26
- package/package.json +1 -1
- package/projects/Elysee File/# Full-Stack [SUBJECT] Management S.txt +200 -0
- package/projects/SRMS/backend/config/db.js +12 -0
- package/projects/SRMS/backend/controllers/authController.js +90 -0
- package/projects/SRMS/backend/controllers/customerController.js +45 -0
- package/projects/SRMS/backend/controllers/productController.js +44 -0
- package/projects/SRMS/backend/controllers/reportsController.js +44 -0
- package/projects/SRMS/backend/controllers/salesController.js +88 -0
- package/projects/SRMS/backend/middleware/auth.js +28 -13
- package/projects/SRMS/backend/models/User.js +26 -6
- package/projects/SRMS/backend/models/customer.js +36 -0
- package/projects/SRMS/backend/models/product.js +30 -0
- package/projects/SRMS/backend/models/sales.js +44 -0
- package/projects/SRMS/backend/package-lock.json +2190 -1568
- package/projects/SRMS/backend/package.json +23 -23
- package/projects/SRMS/backend/routes/authRoutes.js +11 -0
- package/projects/SRMS/backend/routes/customerRoutes.js +12 -0
- package/projects/SRMS/backend/routes/productRoutes.js +12 -0
- package/projects/SRMS/backend/routes/protectedRoutes.js +13 -0
- package/projects/SRMS/backend/routes/reportsRoutes.js +10 -0
- package/projects/SRMS/backend/routes/salesRoutes.js +15 -0
- package/projects/SRMS/backend/server.js +22 -17
- package/projects/SRMS/frontend/index.html +1 -1
- package/projects/SRMS/frontend/package-lock.json +24 -24
- package/projects/SRMS/frontend/package.json +1 -1
- package/projects/SRMS/frontend/src/App.jsx +28 -32
- package/projects/SRMS/frontend/src/components/Dashboard.jsx +32 -0
- package/projects/SRMS/frontend/src/components/Layout.jsx +104 -0
- package/projects/SRMS/frontend/src/components/ProtectedRoute.jsx +22 -0
- package/projects/SRMS/frontend/src/index.css +57 -1
- package/projects/SRMS/frontend/src/pages/Customer.jsx +127 -104
- package/projects/SRMS/frontend/src/pages/Login.jsx +76 -52
- package/projects/SRMS/frontend/src/pages/Product.jsx +116 -95
- package/projects/SRMS/frontend/src/pages/Profile.jsx +67 -0
- package/projects/SRMS/frontend/src/pages/Register.jsx +111 -56
- package/projects/SRMS/frontend/src/pages/Reports.jsx +72 -229
- package/projects/SRMS/frontend/src/pages/Sales.jsx +269 -0
- package/projects/SRMS/frontend/vite.config.js +1 -4
- package/projects/SRMS/backend/.env +0 -4
- package/projects/SRMS/backend/controllers/CustomerControllers.js +0 -29
- package/projects/SRMS/backend/controllers/ProductControllers.js +0 -31
- package/projects/SRMS/backend/controllers/SaleControllers.js +0 -49
- package/projects/SRMS/backend/controllers/UserControllers.js +0 -40
- package/projects/SRMS/backend/db/connectDB.js +0 -11
- package/projects/SRMS/backend/models/Customer.js +0 -13
- package/projects/SRMS/backend/models/Product.js +0 -9
- package/projects/SRMS/backend/models/Sale.js +0 -12
- package/projects/SRMS/backend/routes/CustomerRoutes.js +0 -11
- package/projects/SRMS/backend/routes/ProductRoutes.js +0 -11
- package/projects/SRMS/backend/routes/SaleRoutes.js +0 -13
- package/projects/SRMS/backend/routes/UserRoutes.js +0 -11
- package/projects/SRMS/frontend/App.jsx +0 -6
- package/projects/SRMS/frontend/README.md +0 -16
- package/projects/SRMS/frontend/public/icons.svg +0 -24
- package/projects/SRMS/frontend/src/App.css +0 -184
- package/projects/SRMS/frontend/src/assets/hero.png +0 -0
- package/projects/SRMS/frontend/src/assets/react.svg +0 -1
- package/projects/SRMS/frontend/src/assets/vite.svg +0 -1
- package/projects/SRMS/frontend/src/axiosInstance.js +0 -11
- package/projects/SRMS/frontend/src/components/Navbar.jsx +0 -30
- package/projects/SRMS/frontend/src/pages/Dashboard.jsx +0 -31
- package/projects/SRMS/frontend/src/pages/Sale.jsx +0 -228
package/index.js
CHANGED
|
@@ -10,19 +10,20 @@ const rl = readline.createInterface({
|
|
|
10
10
|
output: process.stdout
|
|
11
11
|
})
|
|
12
12
|
console.log('\n[1] Car Repair Parking Managemnt System')
|
|
13
|
-
console.log('[2]
|
|
14
|
-
console.log('[3]Employee Payroll Management System
|
|
15
|
-
console.log('[4]
|
|
16
|
-
console.log('[5]
|
|
17
|
-
console.log('[6]
|
|
18
|
-
console.log('[7]Supply Chain
|
|
19
|
-
|
|
20
|
-
console.log('[9]
|
|
21
|
-
console.log('[10]Stock
|
|
22
|
-
console.log('[11]Stock Management System')
|
|
23
|
-
console.log('[12]
|
|
24
|
-
console.log('[13]Sales Record Management System
|
|
25
|
-
console.log('[14]
|
|
13
|
+
console.log('[2]Elysee File Prompt')
|
|
14
|
+
console.log('[3]Employee Payroll Management System')
|
|
15
|
+
console.log('[4]Employee Payroll Management System Elysee')
|
|
16
|
+
console.log('[5]middleware')
|
|
17
|
+
console.log('[6] Parking Management System')
|
|
18
|
+
console.log('[7]Supply Chain Managemnt System')
|
|
19
|
+
console.log('[8]Supply Chain Management System Elysee')
|
|
20
|
+
console.log('[9] School fee Management System')
|
|
21
|
+
console.log('[10]Stock Hub Management System')
|
|
22
|
+
console.log('[11]Stock Invetory Management System')
|
|
23
|
+
console.log('[12]Stock Management System')
|
|
24
|
+
console.log('[13]Sales Record Management System')
|
|
25
|
+
console.log('[14]Sales Record Management System Elysee')
|
|
26
|
+
console.log('[15]userController.js')
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
|
|
@@ -31,19 +32,20 @@ rl.question('Pick (1-14): ', (answer) => {
|
|
|
31
32
|
|
|
32
33
|
const projects = {
|
|
33
34
|
'1':'crpms',
|
|
34
|
-
'2':'
|
|
35
|
-
'3':'EPMS
|
|
36
|
-
'4':'
|
|
37
|
-
'5':
|
|
38
|
-
|
|
39
|
-
'7':'SCMS
|
|
40
|
-
'8':
|
|
41
|
-
'9':'
|
|
42
|
-
'10':'
|
|
43
|
-
'11':
|
|
44
|
-
'12':'
|
|
45
|
-
'13':'SRMS
|
|
46
|
-
'14':'
|
|
35
|
+
'2':'Elysee File',
|
|
36
|
+
'3':'EPMS',
|
|
37
|
+
'4':'EPMS(Ely)',
|
|
38
|
+
'5':'middleware',
|
|
39
|
+
'6': 'parking managementt system',
|
|
40
|
+
'7':'SCMS',
|
|
41
|
+
'8':'SCMS(Ely)',
|
|
42
|
+
'9': 'sfms-app',
|
|
43
|
+
'10':'SHMS(Ely)',
|
|
44
|
+
'11':'sims',
|
|
45
|
+
'12': 'SMS-',
|
|
46
|
+
'13':'SRMS',
|
|
47
|
+
'14':'SRMS(Ely)',
|
|
48
|
+
'15':'user',
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Full-Stack [SUBJECT] Management System
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
A full-stack web application for managing **[SUBJECT]** records. The system provides CRUD operations, authentication, and reporting functionality through a RESTful API backend and a React-based single-page frontend.
|
|
5
|
+
|
|
6
|
+
## Tech Stack
|
|
7
|
+
- **Frontend:** React 18 + Vite + React Router v6 + Axios
|
|
8
|
+
- **Backend:** Node.js + Express 4 + JWT authentication + bcrypt
|
|
9
|
+
- **Database:** MongoDB + Mongoose ODM
|
|
10
|
+
- **Styling:** Custom CSS (no framework)
|
|
11
|
+
- **Build:** Vite (frontend), Nodemon (backend dev)
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
Full-stack MERN pattern:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Frontend (React/Vite) --HTTP--> Backend (Express API) --Mongoose--> MongoDB
|
|
18
|
+
localhost:5173 localhost:5000 localhost:27017
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Backend Structure
|
|
22
|
+
|
|
23
|
+
### Entry Point: `server.js`
|
|
24
|
+
- Express app setup with CORS (origins: localhost:5173, 5174)
|
|
25
|
+
- Connects to MongoDB, starts listening on PORT (default 5000)
|
|
26
|
+
- Mounts all route groups under `/api/`
|
|
27
|
+
|
|
28
|
+
### Authentication (`middleware/authMiddleware.js`)
|
|
29
|
+
- JWT-based bearer token verification
|
|
30
|
+
- Extracts token from `Authorization: Bearer <token>` header
|
|
31
|
+
- Attaches decoded user (`{ id, username }`) to `req.user`
|
|
32
|
+
- Returns 401 on missing/invalid/expired token
|
|
33
|
+
|
|
34
|
+
### Data Models (`models/`)
|
|
35
|
+
| Model | Key Fields |
|
|
36
|
+
|---------|---------------------------------------------------------------------------|
|
|
37
|
+
| User | username (unique), password (hashed with bcrypt) |
|
|
38
|
+
| [A] | code (unique), name, additional fields per subject |
|
|
39
|
+
| [B] | code (unique), name, quantity, unit price |
|
|
40
|
+
| [C] | invoice number (unique), date, payment method, total amount, customer ref, line items |
|
|
41
|
+
|
|
42
|
+
### API Routes
|
|
43
|
+
| Method | Endpoint | Auth | Description |
|
|
44
|
+
|--------|---------------------|------|---------------------------------|
|
|
45
|
+
| POST | /api/auth/register | No | Register new user |
|
|
46
|
+
| POST | /api/auth/login | No | Login, returns JWT token |
|
|
47
|
+
| GET | /api/[A] | JWT | List all [A] records |
|
|
48
|
+
| POST | /api/[A] | JWT | Create new [A] record |
|
|
49
|
+
| GET | /api/[B] | JWT | List all [B] records |
|
|
50
|
+
| POST | /api/[B] | JWT | Create new [B] record |
|
|
51
|
+
| GET | /api/[C] | JWT | List all [C] records |
|
|
52
|
+
| GET | /api/[C]/:id | JWT | Get single [C] record |
|
|
53
|
+
| POST | /api/[C] | JWT | Create [C] record |
|
|
54
|
+
| PUT | /api/[C]/:id | JWT | Update [C] record |
|
|
55
|
+
| DELETE | /api/[C]/:id | JWT | Delete [C] record |
|
|
56
|
+
| GET | /api/reports/daily | JWT | Daily report (date filter) |
|
|
57
|
+
| GET | /api/reports/weekly | JWT | Weekly report (week filter) |
|
|
58
|
+
| GET | /api/reports/monthly| JWT | Monthly report (year/month) |
|
|
59
|
+
|
|
60
|
+
### Controllers (`controllers/`)
|
|
61
|
+
Each controller handles request validation, database interaction, and response formatting:
|
|
62
|
+
- **authController** — register (hash password, create user, return JWT), login (verify credentials, return JWT)
|
|
63
|
+
- **[A]Controller** — list (all sorted by newest), create (validate fields, check duplicate code)
|
|
64
|
+
- **[B]Controller** — list (all sorted by newest), create (validate fields, check duplicate code)
|
|
65
|
+
- **[C]Controller** — full CRUD: list, getById, create, update, delete; populates related [A] reference
|
|
66
|
+
- **reportController** — daily/weekly/monthly aggregation with date range filtering, totals calculation
|
|
67
|
+
|
|
68
|
+
## Frontend Structure
|
|
69
|
+
|
|
70
|
+
### Entry: `index.html` → `main.jsx` → `App.jsx`
|
|
71
|
+
- React StrictMode wrapping root App component
|
|
72
|
+
- Global CSS import
|
|
73
|
+
|
|
74
|
+
### App Component (`App.jsx`)
|
|
75
|
+
- **State:** session (localStorage-persisted token + user), notice (notification messages)
|
|
76
|
+
- **Session management:** saveSession (localStorage + state), logout (clear both)
|
|
77
|
+
- **Routing (React Router v6):**
|
|
78
|
+
- `/login` — Login page
|
|
79
|
+
- `/register` — Register page
|
|
80
|
+
- `/dashboard` — Protected dashboard with stats overview
|
|
81
|
+
- `/[A]` — Protected [A] list/create page
|
|
82
|
+
- `/[B]` — Protected [B] list/create page
|
|
83
|
+
- `/[C]` — Protected [C] full CRUD page
|
|
84
|
+
- `/reports` — Protected reports page (daily/weekly/monthly)
|
|
85
|
+
- `/` — Redirects to dashboard or login
|
|
86
|
+
- `*` — Catch-all redirect
|
|
87
|
+
- **Protected wrapper:** Redirects to `/login` if no session
|
|
88
|
+
- **TopNav:** Navigation bar with links, username, logout button (hidden on print via `.no-print`)
|
|
89
|
+
- **Notice bar:** Dismissible notification messages
|
|
90
|
+
- API base URL: `http://localhost:5000/api` (hardcoded)
|
|
91
|
+
|
|
92
|
+
### Pages (`src/pages/`)
|
|
93
|
+
|
|
94
|
+
#### Login (`Login.jsx`)
|
|
95
|
+
- Username/password form
|
|
96
|
+
- Validates non-empty fields
|
|
97
|
+
- POSTs to `/api/auth/login`, stores session, redirects to dashboard
|
|
98
|
+
- Link to register page
|
|
99
|
+
|
|
100
|
+
#### Register (`Register.jsx`)
|
|
101
|
+
- Username/password/confirm password form
|
|
102
|
+
- Validates non-empty fields, password match, password >= 4 chars
|
|
103
|
+
- POSTs to `/api/auth/register`, stores session, redirects to dashboard
|
|
104
|
+
- Link back to login
|
|
105
|
+
|
|
106
|
+
#### Dashboard (`Dashboard.jsx`)
|
|
107
|
+
- Fetches counts from all 3 record types on mount (parallel GET requests)
|
|
108
|
+
- Displays 4 stat cards: Total [A], Total [B], Total [C], Total Revenue in [CURRENCY]
|
|
109
|
+
- Quick action links to add new records
|
|
110
|
+
|
|
111
|
+
#### [A] Records (`[A].jsx`)
|
|
112
|
+
- Form with fields per [A] schema + submit button
|
|
113
|
+
- Validates all fields, POSTs to create endpoint
|
|
114
|
+
- Table listing all records with their fields
|
|
115
|
+
- Currency formatting where applicable
|
|
116
|
+
|
|
117
|
+
#### [B] Records (`[B].jsx`)
|
|
118
|
+
- Form with fields per [B] schema + submit button
|
|
119
|
+
- Validates fields (including numeric), POSTs to create endpoint
|
|
120
|
+
- Table listing all records with computed totals
|
|
121
|
+
- Currency formatting in [CURRENCY]
|
|
122
|
+
|
|
123
|
+
#### [C] Records (`[C].jsx`)
|
|
124
|
+
- Form with fields per [C] schema + [A] dropdown selector + submit/update/cancel buttons
|
|
125
|
+
- Full CRUD: create (POST), edit (PUT with populated form), delete (DELETE with confirmation)
|
|
126
|
+
- Fetches both [C] records and [A] list on mount
|
|
127
|
+
- Table with all [C] fields and action buttons (Edit/Delete)
|
|
128
|
+
|
|
129
|
+
#### Reports (`Reports.jsx`)
|
|
130
|
+
- Daily / Weekly / Monthly toggle buttons
|
|
131
|
+
- Date picker (daily/weekly) or month picker (monthly)
|
|
132
|
+
- "Generate Report" button triggers filtered API call
|
|
133
|
+
- "Print" button uses `window.print()`, nav hidden via CSS `@media print`
|
|
134
|
+
- Report summary: Total Transactions, Total Revenue, Average per [C]
|
|
135
|
+
- Report table with [C] details
|
|
136
|
+
|
|
137
|
+
### Styling (`index.css`)
|
|
138
|
+
- Global reset, blue primary color `#1E3A8A`
|
|
139
|
+
- Responsive layout with max-width 1100px
|
|
140
|
+
- 4-column stat card grid
|
|
141
|
+
- Form grids (5-column, variants for 3/4 fields)
|
|
142
|
+
- Scrollable tables with striped headers and hover rows
|
|
143
|
+
- Centered auth card layout
|
|
144
|
+
- Report filters and summary sections
|
|
145
|
+
- Print media query hiding navigation
|
|
146
|
+
- Responsive breakpoints at 950px and 620px
|
|
147
|
+
|
|
148
|
+
## Configuration
|
|
149
|
+
|
|
150
|
+
### Backend (`.env`)
|
|
151
|
+
```
|
|
152
|
+
PORT=5000
|
|
153
|
+
MONGO_URI=mongodb://127.0.0.1:27017/[DB_NAME]
|
|
154
|
+
JWT_SECRET=[JWT_SECRET_KEY]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Frontend (`package.json`)
|
|
158
|
+
- Scripts: `dev` (vite), `build` (vite build), `preview` (vite preview)
|
|
159
|
+
- Port: 5173 (Vite dev server)
|
|
160
|
+
|
|
161
|
+
## Authentication Flow
|
|
162
|
+
1. User registers or logs in → backend returns JWT (expires 1 day, payload: `{ id, username }`)
|
|
163
|
+
2. Frontend stores token + user in `localStorage` under key `[SESSION_KEY]`
|
|
164
|
+
3. All API calls include `Authorization: Bearer <token>` header
|
|
165
|
+
4. Backend `authMiddleware` verifies JWT on every protected route
|
|
166
|
+
5. 401 response → frontend redirects to login
|
|
167
|
+
|
|
168
|
+
## Key Design Decisions
|
|
169
|
+
- No JSX in App.jsx (uses `React.createElement`); page components use JSX
|
|
170
|
+
- No state management library (plain React state + localStorage)
|
|
171
|
+
- No form validation library (manual validation)
|
|
172
|
+
- No pagination (all records fetched at once)
|
|
173
|
+
- No testing framework
|
|
174
|
+
- Currency displayed in [CURRENCY]
|
|
175
|
+
- Print support via CSS `@media print` and `window.print()`
|
|
176
|
+
- Passwords hashed with bcrypt (10 salt rounds)
|
|
177
|
+
- CORS restricted to localhost:5173/5174
|
|
178
|
+
- No input sanitization library
|
|
179
|
+
|
|
180
|
+
## Setup Instructions
|
|
181
|
+
|
|
182
|
+
### Backend
|
|
183
|
+
```bash
|
|
184
|
+
cd backend-project
|
|
185
|
+
npm install
|
|
186
|
+
# Configure .env with your MongoDB URI and JWT secret
|
|
187
|
+
npm run dev # or npm start
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Frontend
|
|
191
|
+
```bash
|
|
192
|
+
cd frontend-project
|
|
193
|
+
npm install
|
|
194
|
+
npm run dev
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### First Use
|
|
198
|
+
1. Open `http://localhost:5173`
|
|
199
|
+
2. Register a new account
|
|
200
|
+
3. Start managing [SUBJECT] records
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const connectDB = async () => {
|
|
4
|
+
try {
|
|
5
|
+
await mongoose.connect('mongodb://localhost:27017/SRMS');
|
|
6
|
+
console.log('MongoDB connected');
|
|
7
|
+
} catch (error) {
|
|
8
|
+
console.error('MongoDB connection error:', error);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
module.exports = connectDB;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const bcrypt = require('bcrypt');
|
|
2
|
+
const jwt = require('jsonwebtoken');
|
|
3
|
+
const User = require('../models/User');
|
|
4
|
+
const JWT_SECRET = 'change_this_to_a_long_random_secret';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
function createToken(userId) {
|
|
9
|
+
return jwt.sign({ userId },JWT_SECRET, { expiresIn: '7d' });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function formatUser(user) {
|
|
13
|
+
return {
|
|
14
|
+
id: user._id,
|
|
15
|
+
name: user.name,
|
|
16
|
+
email: user.email,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
exports.register = async (req, res) => {
|
|
21
|
+
try {
|
|
22
|
+
const { name, email, password } = req.body;
|
|
23
|
+
|
|
24
|
+
if (!name || !email || !password) {
|
|
25
|
+
return res.status(400).json({ message: 'Name, email, and password are required' });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (password.length < 6) {
|
|
29
|
+
return res.status(400).json({ message: 'Password must be at least 6 characters' });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const existing = await User.findOne({ email: email.toLowerCase() });
|
|
33
|
+
if (existing) {
|
|
34
|
+
return res.status(409).json({ message: 'Email already registered' });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const hashedPassword = await bcrypt.hash(password, 10);
|
|
38
|
+
const user = await User.create({
|
|
39
|
+
name,
|
|
40
|
+
email: email.toLowerCase(),//to ensure the email is always lowercase
|
|
41
|
+
password: hashedPassword,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const token = createToken(user._id);
|
|
45
|
+
|
|
46
|
+
res.status(201).json({
|
|
47
|
+
token,
|
|
48
|
+
user: formatUser(user),
|
|
49
|
+
});
|
|
50
|
+
} catch (err) {
|
|
51
|
+
res.status(500).json({ message: err.message });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
exports.login = async (req, res) => {
|
|
56
|
+
try {
|
|
57
|
+
const { email, password } = req.body;
|
|
58
|
+
|
|
59
|
+
if (!email || !password) {
|
|
60
|
+
return res.status(400).json({ message: 'Email and password are required' });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const user = await User.findOne({ email: email.toLowerCase() });
|
|
64
|
+
if (!user) {
|
|
65
|
+
return res.status(401).json({ message: 'Invalid email or password' });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const match = await bcrypt.compare(password, user.password);
|
|
69
|
+
if (!match) {
|
|
70
|
+
return res.status(401).json({ message: 'Invalid email or password' });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const token = createToken(user._id);
|
|
74
|
+
|
|
75
|
+
res.json({
|
|
76
|
+
token,
|
|
77
|
+
user: formatUser(user),
|
|
78
|
+
});
|
|
79
|
+
} catch (err) {
|
|
80
|
+
res.status(500).json({ message: err.message });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
exports.getMe = async (req, res) => {
|
|
85
|
+
try {
|
|
86
|
+
res.json({ user: formatUser(req.user) });
|
|
87
|
+
} catch (err) {
|
|
88
|
+
res.status(500).json({ message: err.message });
|
|
89
|
+
}
|
|
90
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const Customer = require('../models/customer');
|
|
2
|
+
|
|
3
|
+
exports.addcustomer = async (req, res) => {
|
|
4
|
+
try {
|
|
5
|
+
const { customerNumber, firstName, lastName, telephone, address } = req.body;
|
|
6
|
+
|
|
7
|
+
const existingCustomer = await Customer.findOne({ customerNumber });
|
|
8
|
+
if (existingCustomer) {
|
|
9
|
+
return res.status(409).json({ message: 'Customer number already exists' });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const customer = await Customer.create({
|
|
13
|
+
customerNumber,
|
|
14
|
+
firstName,
|
|
15
|
+
lastName,
|
|
16
|
+
telephone,
|
|
17
|
+
address,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
res.status(201).json(customer);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
res.status(500).json({ message: err.message });
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
exports.getcustomers = async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const customers = await Customer.find().sort({ createdAt: -1 });
|
|
29
|
+
res.json(customers);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
res.status(500).json({ message: err.message });
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
exports.getcustomerbyid = async (req, res) => {
|
|
36
|
+
try {
|
|
37
|
+
const customer = await Customer.findById(req.params.id);
|
|
38
|
+
if (!customer) {
|
|
39
|
+
return res.status(404).json({ message: 'Customer not found' });
|
|
40
|
+
}
|
|
41
|
+
res.json(customer);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
res.status(500).json({ message: err.message });
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const Product = require('../models/product');
|
|
2
|
+
|
|
3
|
+
exports.getproduct = async (req, res) => {
|
|
4
|
+
try {
|
|
5
|
+
const products = await Product.find().sort({ createdAt: -1 });
|
|
6
|
+
res.json(products);
|
|
7
|
+
} catch (err) {
|
|
8
|
+
res.status(500).json({ message: err.message });
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
exports.getproductbyid = async (req, res) => {
|
|
13
|
+
try {
|
|
14
|
+
const product = await Product.findById(req.params.id);
|
|
15
|
+
if (!product) {
|
|
16
|
+
return res.status(404).json({ message: 'Product not found' });
|
|
17
|
+
}
|
|
18
|
+
res.json(product);
|
|
19
|
+
} catch (err) {
|
|
20
|
+
res.status(500).json({ message: err.message });
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
exports.addproduct = async (req, res) => {
|
|
25
|
+
try {
|
|
26
|
+
const { productCode, productName, quantitySold, unitPrice } = req.body;
|
|
27
|
+
|
|
28
|
+
const existing = await Product.findOne({ productCode });
|
|
29
|
+
if (existing) {
|
|
30
|
+
return res.status(409).json({ message: 'Product code already exists' });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const newproduct = await Product.create({
|
|
34
|
+
productCode,
|
|
35
|
+
productName,
|
|
36
|
+
quantitySold,
|
|
37
|
+
unitPrice,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
res.status(201).json(newproduct);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
res.status(500).json({ message: err.message });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const Sales = require('../models/sales');
|
|
2
|
+
const Product = require('../models/product');
|
|
3
|
+
const Customer = require('../models/customer');
|
|
4
|
+
|
|
5
|
+
const sumTotal = (items, field) => items.reduce((total, item) => total + (item[field] || 0), 0);
|
|
6
|
+
|
|
7
|
+
exports.summary = async (req, res) => {
|
|
8
|
+
try {
|
|
9
|
+
const now = new Date();
|
|
10
|
+
const startOfDay = new Date(now);
|
|
11
|
+
startOfDay.setHours(0, 0, 0, 0);
|
|
12
|
+
|
|
13
|
+
const startOfWeek = new Date(startOfDay);
|
|
14
|
+
startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay());
|
|
15
|
+
|
|
16
|
+
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
17
|
+
|
|
18
|
+
const [productCount, customerCount, salesCount] = await Promise.all([
|
|
19
|
+
Product.countDocuments(),
|
|
20
|
+
Customer.countDocuments(),
|
|
21
|
+
Sales.countDocuments(),
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const [dailySales, weeklySales, monthlySales] = await Promise.all([
|
|
25
|
+
Sales.find({ salesDate: { $gte: startOfDay } }),
|
|
26
|
+
Sales.find({ salesDate: { $gte: startOfWeek } }),
|
|
27
|
+
Sales.find({ salesDate: { $gte: startOfMonth } }),
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
res.json({
|
|
31
|
+
productCount,
|
|
32
|
+
customerCount,
|
|
33
|
+
salesCount,
|
|
34
|
+
dailySalesTotal: sumTotal(dailySales, 'totalAmountPaid'),
|
|
35
|
+
weeklySalesTotal: sumTotal(weeklySales, 'totalAmountPaid'),
|
|
36
|
+
monthlySalesTotal: sumTotal(monthlySales, 'totalAmountPaid'),
|
|
37
|
+
dailySalesCount: dailySales.length,
|
|
38
|
+
weeklySalesCount: weeklySales.length,
|
|
39
|
+
monthlySalesCount: monthlySales.length,
|
|
40
|
+
});
|
|
41
|
+
} catch (err) {
|
|
42
|
+
res.status(500).json({ message: err.message });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const Sales = require('../models/sales');
|
|
2
|
+
const Product = require('../models/product');
|
|
3
|
+
const Customer = require('../models/customer');
|
|
4
|
+
|
|
5
|
+
exports.addsales = async (req, res) => {
|
|
6
|
+
try {
|
|
7
|
+
const { invoiceNumber, salesDate, paymentMethod, totalAmountPaid, quantitySold, productId, customerId } = req.body;
|
|
8
|
+
|
|
9
|
+
const product = await Product.findById(productId);
|
|
10
|
+
const customer = await Customer.findById(customerId);
|
|
11
|
+
|
|
12
|
+
if (!product) return res.status(400).json({ message: 'Product selection is required' });
|
|
13
|
+
if (!customer) return res.status(400).json({ message: 'Customer selection is required' });
|
|
14
|
+
|
|
15
|
+
const existing = await Sales.findOne({ invoiceNumber });
|
|
16
|
+
if (existing) {
|
|
17
|
+
return res.status(409).json({ message: 'Invoice number already exists' });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const sales = await Sales.create({
|
|
21
|
+
invoiceNumber,
|
|
22
|
+
salesDate,
|
|
23
|
+
paymentMethod,
|
|
24
|
+
totalAmountPaid,
|
|
25
|
+
quantitySold,
|
|
26
|
+
product_FK: productId,
|
|
27
|
+
customer_FK: customerId,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
res.status(201).json(sales);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
res.status(500).json({ message: error.message });
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
exports.getsales = async (req, res) => {
|
|
37
|
+
try {
|
|
38
|
+
const sales = await Sales.find().populate('product_FK').populate('customer_FK').sort({ salesDate: -1 });
|
|
39
|
+
res.status(200).json(sales);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
res.status(404).json({ message: 'Sales not found' });
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
exports.getsalesbyid = async (req, res) => {
|
|
46
|
+
try {
|
|
47
|
+
const sale = await Sales.findById(req.params.id).populate('product_FK').populate('customer_FK');
|
|
48
|
+
if (!sale) {
|
|
49
|
+
return res.status(404).json({ message: 'Sale not found' });
|
|
50
|
+
}
|
|
51
|
+
res.status(200).json(sale);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
res.status(404).json({ message: 'Failed to fetch sale' });
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
exports.updatesales = async (req, res) => {
|
|
58
|
+
try {
|
|
59
|
+
const { invoiceNumber, salesDate, paymentMethod, totalAmountPaid, quantitySold, productId, customerId } = req.body;
|
|
60
|
+
const sale = await Sales.findById(req.params.id);
|
|
61
|
+
if (!sale) return res.status(404).json({ message: 'Sale not found' });
|
|
62
|
+
|
|
63
|
+
sale.invoiceNumber = invoiceNumber;
|
|
64
|
+
sale.salesDate = salesDate;
|
|
65
|
+
sale.paymentMethod = paymentMethod;
|
|
66
|
+
sale.totalAmountPaid = totalAmountPaid;
|
|
67
|
+
sale.quantitySold = quantitySold;
|
|
68
|
+
sale.product_FK = productId;
|
|
69
|
+
sale.customer_FK = customerId;
|
|
70
|
+
|
|
71
|
+
await sale.save();
|
|
72
|
+
|
|
73
|
+
const updatedSale = await Sales.findById(req.params.id).populate('product_FK').populate('customer_FK');
|
|
74
|
+
res.status(200).json(updatedSale);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
res.status(500).json({ message: error.message });
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
exports.deletesales = async (req, res) => {
|
|
81
|
+
try {
|
|
82
|
+
const deleted = await Sales.findByIdAndDelete(req.params.id);
|
|
83
|
+
if (!deleted) return res.status(404).json({ message: 'Sale not found' });
|
|
84
|
+
res.status(200).json({ message: 'Sale deleted successfully' });
|
|
85
|
+
} catch (error) {
|
|
86
|
+
res.status(500).json({ message: error.message });
|
|
87
|
+
}
|
|
88
|
+
};
|
|
@@ -1,17 +1,32 @@
|
|
|
1
|
+
// this middleware is helping us to protect our routes so that only authenticated users can access the routes
|
|
2
|
+
//it is checking if the user is authenticated by checking the token in the header(the header is the part of the request that contains the token)
|
|
3
|
+
//if the user is not authenticated, it will return a 401 status code and a message that the user is not authorized
|
|
4
|
+
//if the user is authenticated, it will return the user object
|
|
5
|
+
//and the user object is the user that is authenticated
|
|
1
6
|
|
|
2
|
-
|
|
7
|
+
const jwt = require('jsonwebtoken');
|
|
8
|
+
const User = require('../models/User');
|
|
9
|
+
const JWT_SECRET = 'change_this_to_a_long_random_secret';
|
|
3
10
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
exports.protect = async (req, res, next) => {
|
|
12
|
+
try {
|
|
13
|
+
const header = req.headers.authorization;
|
|
14
|
+
|
|
15
|
+
if (!header || !header.startsWith('Bearer ')) {
|
|
16
|
+
return res.status(401).json({ message: 'Not authorized' });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const token = header.split(' ')[1];
|
|
20
|
+
const decoded = jwt.verify(token, JWT_SECRET);
|
|
21
|
+
const user = await User.findById(decoded.userId).select('-password');
|
|
22
|
+
|
|
23
|
+
if (!user) {
|
|
24
|
+
return res.status(401).json({ message: 'Not authorized' });
|
|
14
25
|
}
|
|
15
|
-
}
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
req.user = user;
|
|
28
|
+
next();
|
|
29
|
+
} catch (err) {
|
|
30
|
+
return res.status(401).json({ message: 'Not authorized' });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -1,8 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
2
|
|
|
3
|
-
const userSchema = new mongoose.Schema(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const userSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
name: {
|
|
6
|
+
type: String,
|
|
7
|
+
required: true,
|
|
8
|
+
trim: true,
|
|
9
|
+
},
|
|
10
|
+
email: {
|
|
11
|
+
type: String,
|
|
12
|
+
required: true,
|
|
13
|
+
unique: true,
|
|
14
|
+
lowercase: true,
|
|
15
|
+
trim: true,
|
|
16
|
+
},
|
|
17
|
+
password: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: true,
|
|
20
|
+
minlength: 6,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{ timestamps: true }
|
|
24
|
+
);
|
|
7
25
|
|
|
8
|
-
|
|
26
|
+
const User = mongoose.model('User', userSchema);
|
|
27
|
+
|
|
28
|
+
module.exports = User;
|