create-nodemin-app 1.0.16
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/cli.js +82 -0
- package/package.json +25 -0
- package/templates/HRMS_Mongodb/README.md +331 -0
- package/templates/HRMS_Mongodb/backend/.env.example +6 -0
- package/templates/HRMS_Mongodb/backend/package-lock.json +1646 -0
- package/templates/HRMS_Mongodb/backend/package.json +26 -0
- package/templates/HRMS_Mongodb/backend/src/config/db.js +9 -0
- package/templates/HRMS_Mongodb/backend/src/controllers/authController.js +187 -0
- package/templates/HRMS_Mongodb/backend/src/controllers/departmentController.js +70 -0
- package/templates/HRMS_Mongodb/backend/src/controllers/employeeController.js +178 -0
- package/templates/HRMS_Mongodb/backend/src/controllers/positionController.js +66 -0
- package/templates/HRMS_Mongodb/backend/src/middleware/auth.js +57 -0
- package/templates/HRMS_Mongodb/backend/src/middleware/errorHandler.js +32 -0
- package/templates/HRMS_Mongodb/backend/src/middleware/restrictToAdmin.js +5 -0
- package/templates/HRMS_Mongodb/backend/src/middleware/validate.js +13 -0
- package/templates/HRMS_Mongodb/backend/src/models/Department.js +19 -0
- package/templates/HRMS_Mongodb/backend/src/models/Employee.js +81 -0
- package/templates/HRMS_Mongodb/backend/src/models/Position.js +19 -0
- package/templates/HRMS_Mongodb/backend/src/models/User.js +40 -0
- package/templates/HRMS_Mongodb/backend/src/routes/authRoutes.js +27 -0
- package/templates/HRMS_Mongodb/backend/src/routes/departmentRoutes.js +33 -0
- package/templates/HRMS_Mongodb/backend/src/routes/employeeRoutes.js +39 -0
- package/templates/HRMS_Mongodb/backend/src/routes/positionRoutes.js +32 -0
- package/templates/HRMS_Mongodb/backend/src/server.js +74 -0
- package/templates/HRMS_Mongodb/backend/src/utils/roles.js +5 -0
- package/templates/HRMS_Mongodb/backend/src/utils/seed.js +78 -0
- package/templates/HRMS_Mongodb/backend/src/validators/authValidator.js +61 -0
- package/templates/HRMS_Mongodb/backend/src/validators/departmentValidator.js +21 -0
- package/templates/HRMS_Mongodb/backend/src/validators/employeeValidator.js +27 -0
- package/templates/HRMS_Mongodb/backend/src/validators/positionValidator.js +26 -0
- package/templates/HRMS_Mongodb/frontend/index.html +19 -0
- package/templates/HRMS_Mongodb/frontend/package-lock.json +2812 -0
- package/templates/HRMS_Mongodb/frontend/package.json +25 -0
- package/templates/HRMS_Mongodb/frontend/public/favicon.svg +4 -0
- package/templates/HRMS_Mongodb/frontend/src/App.jsx +50 -0
- package/templates/HRMS_Mongodb/frontend/src/api/axios.js +54 -0
- package/templates/HRMS_Mongodb/frontend/src/components/ProtectedRoute.jsx +26 -0
- package/templates/HRMS_Mongodb/frontend/src/components/layout/DashboardLayout.jsx +16 -0
- package/templates/HRMS_Mongodb/frontend/src/components/layout/Sidebar.jsx +108 -0
- package/templates/HRMS_Mongodb/frontend/src/components/ui/Button.jsx +33 -0
- package/templates/HRMS_Mongodb/frontend/src/components/ui/Input.jsx +20 -0
- package/templates/HRMS_Mongodb/frontend/src/components/ui/Modal.jsx +48 -0
- package/templates/HRMS_Mongodb/frontend/src/components/ui/Select.jsx +27 -0
- package/templates/HRMS_Mongodb/frontend/src/context/AuthContext.jsx +97 -0
- package/templates/HRMS_Mongodb/frontend/src/index.css +34 -0
- package/templates/HRMS_Mongodb/frontend/src/main.jsx +16 -0
- package/templates/HRMS_Mongodb/frontend/src/pages/Dashboard.jsx +78 -0
- package/templates/HRMS_Mongodb/frontend/src/pages/Departments.jsx +144 -0
- package/templates/HRMS_Mongodb/frontend/src/pages/Employees.jsx +297 -0
- package/templates/HRMS_Mongodb/frontend/src/pages/LeaveReport.jsx +113 -0
- package/templates/HRMS_Mongodb/frontend/src/pages/Login.jsx +92 -0
- package/templates/HRMS_Mongodb/frontend/src/pages/Positions.jsx +157 -0
- package/templates/HRMS_Mongodb/frontend/src/pages/Register.jsx +93 -0
- package/templates/HRMS_Mongodb/frontend/src/pages/ResetPassword.jsx +135 -0
- package/templates/HRMS_Mongodb/frontend/src/utils/roles.js +1 -0
- package/templates/HRMS_Mongodb/frontend/src/utils/session.js +5 -0
- package/templates/HRMS_Mongodb/frontend/src/utils/validation.js +66 -0
- package/templates/HRMS_Mongodb/frontend/vite.config.js +16 -0
- package/templates/HRMS_Mysql/backend/db.js +13 -0
- package/templates/HRMS_Mysql/backend/package-lock.json +1614 -0
- package/templates/HRMS_Mysql/backend/package.json +21 -0
- package/templates/HRMS_Mysql/backend/server.js +421 -0
- package/templates/HRMS_Mysql/frontend/dist/assets/index-CtLtQf3_.js +75 -0
- package/templates/HRMS_Mysql/frontend/dist/assets/index-Dq1AXlEY.css +1 -0
- package/templates/HRMS_Mysql/frontend/dist/index.html +14 -0
- package/templates/HRMS_Mysql/frontend/dist/vite.svg +1 -0
- package/templates/HRMS_Mysql/frontend/index.html +13 -0
- package/templates/HRMS_Mysql/frontend/package-lock.json +2978 -0
- package/templates/HRMS_Mysql/frontend/package.json +25 -0
- package/templates/HRMS_Mysql/frontend/postcss.config.js +6 -0
- package/templates/HRMS_Mysql/frontend/public/vite.svg +1 -0
- package/templates/HRMS_Mysql/frontend/src/App.jsx +55 -0
- package/templates/HRMS_Mysql/frontend/src/api.js +11 -0
- package/templates/HRMS_Mysql/frontend/src/components/Layout.jsx +59 -0
- package/templates/HRMS_Mysql/frontend/src/index.css +7 -0
- package/templates/HRMS_Mysql/frontend/src/main.jsx +13 -0
- package/templates/HRMS_Mysql/frontend/src/pages/Dashboard.jsx +45 -0
- package/templates/HRMS_Mysql/frontend/src/pages/Departments.jsx +108 -0
- package/templates/HRMS_Mysql/frontend/src/pages/EmployeeStatusReport.jsx +72 -0
- package/templates/HRMS_Mysql/frontend/src/pages/Employees.jsx +252 -0
- package/templates/HRMS_Mysql/frontend/src/pages/ForgotPassword.jsx +66 -0
- package/templates/HRMS_Mysql/frontend/src/pages/Login.jsx +79 -0
- package/templates/HRMS_Mysql/frontend/src/pages/Positions.jsx +109 -0
- package/templates/HRMS_Mysql/frontend/src/pages/Register.jsx +95 -0
- package/templates/HRMS_Mysql/frontend/src/pages/Users.jsx +133 -0
- package/templates/HRMS_Mysql/frontend/tailwind.config.js +26 -0
- package/templates/HRMS_Mysql/frontend/vite.config.js +15 -0
- package/templates/HRMS_Mysql/hrms_schema.sql +57 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hrms-backend",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "HRMS Backend API",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node server.js",
|
|
8
|
+
"dev": "nodemon server.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"axios": "^1.6.0",
|
|
12
|
+
"bcryptjs": "^2.4.3",
|
|
13
|
+
"cors": "^2.8.5",
|
|
14
|
+
"express": "^4.18.2",
|
|
15
|
+
"express-session": "^1.17.3",
|
|
16
|
+
"mysql2": "^3.6.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"nodemon": "^3.0.1"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const session = require('express-session');
|
|
3
|
+
const cors = require('cors');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const bcrypt = require('bcryptjs');
|
|
6
|
+
const db = require('./db');
|
|
7
|
+
|
|
8
|
+
const app = express();
|
|
9
|
+
const PORT = process.env.PORT || 5000;
|
|
10
|
+
|
|
11
|
+
app.use(cors({
|
|
12
|
+
origin: 'http://localhost:5173',
|
|
13
|
+
credentials: true
|
|
14
|
+
}));
|
|
15
|
+
app.use(express.json());
|
|
16
|
+
app.use(express.urlencoded({ extended: true }));
|
|
17
|
+
|
|
18
|
+
app.use(session({
|
|
19
|
+
secret: 'hrms_secret_key_2024',
|
|
20
|
+
resave: false,
|
|
21
|
+
saveUninitialized: false,
|
|
22
|
+
cookie: {
|
|
23
|
+
secure: false,
|
|
24
|
+
maxAge: 24 * 60 * 60 * 1000
|
|
25
|
+
}
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
function requireAuth(req, res, next) {
|
|
29
|
+
if (!req.session.userId) {
|
|
30
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
31
|
+
}
|
|
32
|
+
next();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ─── AUTH ROUTES ───
|
|
36
|
+
|
|
37
|
+
app.post('/api/register', async (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const { firstName, lastName, email, username, password } = req.body;
|
|
40
|
+
if (!firstName || !lastName || !email || !username || !password) {
|
|
41
|
+
return res.status(400).json({ error: 'All fields are required' });
|
|
42
|
+
}
|
|
43
|
+
const [existing] = await db.query('SELECT UserID FROM Users WHERE UserName = ?', [username]);
|
|
44
|
+
if (existing.length > 0) {
|
|
45
|
+
return res.status(400).json({ error: 'Username already exists' });
|
|
46
|
+
}
|
|
47
|
+
const [emailExists] = await db.query('SELECT EmpID FROM Employee WHERE EmpEmail = ?', [email]);
|
|
48
|
+
if (emailExists.length > 0) {
|
|
49
|
+
return res.status(400).json({ error: 'Email already in use' });
|
|
50
|
+
}
|
|
51
|
+
const [empResult] = await db.query(
|
|
52
|
+
'INSERT INTO Employee (EmpFirstName, EmpLastName, EmpEmail, EmpStatus) VALUES (?, ?, ?, ?)',
|
|
53
|
+
[firstName, lastName, email, 'on leave']
|
|
54
|
+
);
|
|
55
|
+
const hashed = await bcrypt.hash(password, 10);
|
|
56
|
+
const [userResult] = await db.query(
|
|
57
|
+
'INSERT INTO Users (EmpID, UserName, Password) VALUES (?, ?, ?)',
|
|
58
|
+
[empResult.insertId, username, hashed]
|
|
59
|
+
);
|
|
60
|
+
req.session.userId = userResult.insertId;
|
|
61
|
+
req.session.userName = username;
|
|
62
|
+
req.session.empName = firstName;
|
|
63
|
+
res.json({
|
|
64
|
+
success: true,
|
|
65
|
+
user: { id: userResult.insertId, username, name: firstName }
|
|
66
|
+
});
|
|
67
|
+
} catch (err) {
|
|
68
|
+
res.status(500).json({ error: err.message });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
app.post('/api/login', async (req, res) => {
|
|
73
|
+
try {
|
|
74
|
+
const { username, password } = req.body;
|
|
75
|
+
const [rows] = await db.query('SELECT u.*, e.EmpFirstName, e.EmpEmail FROM Users u JOIN Employee e ON u.EmpID = e.EmpID WHERE u.UserName = ?', [username]);
|
|
76
|
+
if (rows.length === 0) {
|
|
77
|
+
return res.status(401).json({ error: 'Invalid credentials' });
|
|
78
|
+
}
|
|
79
|
+
const user = rows[0];
|
|
80
|
+
const valid = await bcrypt.compare(password, user.Password);
|
|
81
|
+
if (!valid) {
|
|
82
|
+
return res.status(401).json({ error: 'Invalid credentials' });
|
|
83
|
+
}
|
|
84
|
+
req.session.userId = user.UserID;
|
|
85
|
+
req.session.userName = user.UserName;
|
|
86
|
+
req.session.empName = user.EmpFirstName;
|
|
87
|
+
res.json({ success: true, user: { id: user.UserID, username: user.UserName, name: user.EmpFirstName } });
|
|
88
|
+
} catch (err) {
|
|
89
|
+
res.status(500).json({ error: err.message });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
app.post('/api/logout', (req, res) => {
|
|
94
|
+
req.session.destroy();
|
|
95
|
+
res.json({ success: true });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
app.get('/api/me', requireAuth, async (req, res) => {
|
|
99
|
+
try {
|
|
100
|
+
const [rows] = await db.query(
|
|
101
|
+
'SELECT u.UserID, u.UserName, e.EmpFirstName, e.EmpLastName, e.EmpEmail FROM Users u JOIN Employee e ON u.EmpID = e.EmpID WHERE u.UserID = ?',
|
|
102
|
+
[req.session.userId]
|
|
103
|
+
);
|
|
104
|
+
if (rows.length === 0) return res.status(404).json({ error: 'User not found' });
|
|
105
|
+
res.json(rows[0]);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
res.status(500).json({ error: err.message });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
app.post('/api/forgot-password', async (req, res) => {
|
|
112
|
+
try {
|
|
113
|
+
const { email } = req.body;
|
|
114
|
+
const [rows] = await db.query(
|
|
115
|
+
'SELECT u.UserID, u.UserName, e.EmpEmail FROM Users u JOIN Employee e ON u.EmpID = e.EmpID WHERE e.EmpEmail = ?',
|
|
116
|
+
[email]
|
|
117
|
+
);
|
|
118
|
+
if (rows.length === 0) {
|
|
119
|
+
return res.status(404).json({ error: 'Email not found' });
|
|
120
|
+
}
|
|
121
|
+
const user = rows[0];
|
|
122
|
+
const tempPassword = Math.random().toString(36).slice(-8);
|
|
123
|
+
const hashed = await bcrypt.hash(tempPassword, 10);
|
|
124
|
+
await db.query('UPDATE Users SET Password = ? WHERE UserID = ?', [hashed, user.UserID]);
|
|
125
|
+
res.json({
|
|
126
|
+
success: true,
|
|
127
|
+
message: `Your temporary password has been sent to ${email}`,
|
|
128
|
+
tempPassword: tempPassword
|
|
129
|
+
});
|
|
130
|
+
} catch (err) {
|
|
131
|
+
res.status(500).json({ error: err.message });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
app.post('/api/reset-password', requireAuth, async (req, res) => {
|
|
136
|
+
try {
|
|
137
|
+
const { currentPassword, newPassword } = req.body;
|
|
138
|
+
const [rows] = await db.query('SELECT Password FROM Users WHERE UserID = ?', [req.session.userId]);
|
|
139
|
+
if (rows.length === 0) return res.status(404).json({ error: 'User not found' });
|
|
140
|
+
const valid = await bcrypt.compare(currentPassword, rows[0].Password);
|
|
141
|
+
if (!valid) return res.status(401).json({ error: 'Current password is incorrect' });
|
|
142
|
+
const hashed = await bcrypt.hash(newPassword, 10);
|
|
143
|
+
await db.query('UPDATE Users SET Password = ? WHERE UserID = ?', [hashed, req.session.userId]);
|
|
144
|
+
res.json({ success: true, message: 'Password updated successfully' });
|
|
145
|
+
} catch (err) {
|
|
146
|
+
res.status(500).json({ error: err.message });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ─── DEPARTMENT CRUD ───
|
|
151
|
+
|
|
152
|
+
app.get('/api/departments', requireAuth, async (req, res) => {
|
|
153
|
+
try {
|
|
154
|
+
const [rows] = await db.query('SELECT * FROM Department ORDER BY DepartName');
|
|
155
|
+
res.json(rows);
|
|
156
|
+
} catch (err) {
|
|
157
|
+
res.status(500).json({ error: err.message });
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
app.get('/api/departments/:id', requireAuth, async (req, res) => {
|
|
162
|
+
try {
|
|
163
|
+
const [rows] = await db.query('SELECT * FROM Department WHERE DepartID = ?', [req.params.id]);
|
|
164
|
+
if (rows.length === 0) return res.status(404).json({ error: 'Department not found' });
|
|
165
|
+
res.json(rows[0]);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
res.status(500).json({ error: err.message });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
app.post('/api/departments', requireAuth, async (req, res) => {
|
|
172
|
+
try {
|
|
173
|
+
const { DepartName } = req.body;
|
|
174
|
+
const [result] = await db.query('INSERT INTO Department (DepartName) VALUES (?)', [DepartName]);
|
|
175
|
+
res.json({ success: true, id: result.insertId });
|
|
176
|
+
} catch (err) {
|
|
177
|
+
res.status(500).json({ error: err.message });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
app.put('/api/departments/:id', requireAuth, async (req, res) => {
|
|
182
|
+
try {
|
|
183
|
+
const { DepartName } = req.body;
|
|
184
|
+
await db.query('UPDATE Department SET DepartName = ? WHERE DepartID = ?', [DepartName, req.params.id]);
|
|
185
|
+
res.json({ success: true });
|
|
186
|
+
} catch (err) {
|
|
187
|
+
res.status(500).json({ error: err.message });
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
app.delete('/api/departments/:id', requireAuth, async (req, res) => {
|
|
192
|
+
try {
|
|
193
|
+
await db.query('DELETE FROM Department WHERE DepartID = ?', [req.params.id]);
|
|
194
|
+
res.json({ success: true });
|
|
195
|
+
} catch (err) {
|
|
196
|
+
res.status(500).json({ error: err.message });
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// ─── POSITION CRUD ───
|
|
201
|
+
|
|
202
|
+
app.get('/api/positions', requireAuth, async (req, res) => {
|
|
203
|
+
try {
|
|
204
|
+
const [rows] = await db.query('SELECT * FROM Positionemp ORDER BY PosName');
|
|
205
|
+
res.json(rows);
|
|
206
|
+
} catch (err) {
|
|
207
|
+
res.status(500).json({ error: err.message });
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
app.get('/api/positions/:id', requireAuth, async (req, res) => {
|
|
212
|
+
try {
|
|
213
|
+
const [rows] = await db.query('SELECT * FROM Positionemp WHERE PosID = ?', [req.params.id]);
|
|
214
|
+
if (rows.length === 0) return res.status(404).json({ error: 'Position not found' });
|
|
215
|
+
res.json(rows[0]);
|
|
216
|
+
} catch (err) {
|
|
217
|
+
res.status(500).json({ error: err.message });
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
app.post('/api/positions', requireAuth, async (req, res) => {
|
|
222
|
+
try {
|
|
223
|
+
const { PosName, RequiredQualification } = req.body;
|
|
224
|
+
const [result] = await db.query('INSERT INTO Positionemp (PosName, RequiredQualification) VALUES (?, ?)', [PosName, RequiredQualification]);
|
|
225
|
+
res.json({ success: true, id: result.insertId });
|
|
226
|
+
} catch (err) {
|
|
227
|
+
res.status(500).json({ error: err.message });
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
app.put('/api/positions/:id', requireAuth, async (req, res) => {
|
|
232
|
+
try {
|
|
233
|
+
const { PosName, RequiredQualification } = req.body;
|
|
234
|
+
await db.query('UPDATE Positionemp SET PosName = ?, RequiredQualification = ? WHERE PosID = ?', [PosName, RequiredQualification, req.params.id]);
|
|
235
|
+
res.json({ success: true });
|
|
236
|
+
} catch (err) {
|
|
237
|
+
res.status(500).json({ error: err.message });
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
app.delete('/api/positions/:id', requireAuth, async (req, res) => {
|
|
242
|
+
try {
|
|
243
|
+
await db.query('DELETE FROM Positionemp WHERE PosID = ?', [req.params.id]);
|
|
244
|
+
res.json({ success: true });
|
|
245
|
+
} catch (err) {
|
|
246
|
+
res.status(500).json({ error: err.message });
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// ─── EMPLOYEE CRUD ───
|
|
251
|
+
|
|
252
|
+
app.get('/api/employees', requireAuth, async (req, res) => {
|
|
253
|
+
try {
|
|
254
|
+
const { search } = req.query;
|
|
255
|
+
let query = `
|
|
256
|
+
SELECT e.*, d.DepartName, p.PosName
|
|
257
|
+
FROM Employee e
|
|
258
|
+
LEFT JOIN Department d ON e.DepartID = d.DepartID
|
|
259
|
+
LEFT JOIN Positionemp p ON e.PosID = p.PosID
|
|
260
|
+
`;
|
|
261
|
+
let params = [];
|
|
262
|
+
if (search) {
|
|
263
|
+
query += ' WHERE e.EmpFirstName LIKE ? OR e.EmpLastName LIKE ? OR e.EmpEmail LIKE ? OR e.EmpTelephone LIKE ?';
|
|
264
|
+
const s = `%${search}%`;
|
|
265
|
+
params = [s, s, s, s];
|
|
266
|
+
}
|
|
267
|
+
query += ' ORDER BY e.EmpLastName, e.EmpFirstName';
|
|
268
|
+
const [rows] = await db.query(query, params);
|
|
269
|
+
res.json(rows);
|
|
270
|
+
} catch (err) {
|
|
271
|
+
res.status(500).json({ error: err.message });
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
app.get('/api/employees/:id', requireAuth, async (req, res) => {
|
|
276
|
+
try {
|
|
277
|
+
const [rows] = await db.query(`
|
|
278
|
+
SELECT e.*, d.DepartName, p.PosName
|
|
279
|
+
FROM Employee e
|
|
280
|
+
LEFT JOIN Department d ON e.DepartID = d.DepartID
|
|
281
|
+
LEFT JOIN Positionemp p ON e.PosID = p.PosID
|
|
282
|
+
WHERE e.EmpID = ?
|
|
283
|
+
`, [req.params.id]);
|
|
284
|
+
if (rows.length === 0) return res.status(404).json({ error: 'Employee not found' });
|
|
285
|
+
res.json(rows[0]);
|
|
286
|
+
} catch (err) {
|
|
287
|
+
res.status(500).json({ error: err.message });
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
app.post('/api/employees', requireAuth, async (req, res) => {
|
|
292
|
+
try {
|
|
293
|
+
const { EmpFirstName, EmpLastName, EmpGender, EmpDateOfBirth, EmpEmail, EmpTelephone, EmpAddress, EmpHireDate, EmpStatus, DepartID, PosID } = req.body;
|
|
294
|
+
const [result] = await db.query(
|
|
295
|
+
'INSERT INTO Employee (EmpFirstName, EmpLastName, EmpGender, EmpDateOfBirth, EmpEmail, EmpTelephone, EmpAddress, EmpHireDate, EmpStatus, DepartID, PosID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
|
296
|
+
[EmpFirstName, EmpLastName, EmpGender, EmpDateOfBirth, EmpEmail, EmpTelephone, EmpAddress, EmpHireDate, EmpStatus || 'on leave', DepartID || null, PosID || null]
|
|
297
|
+
);
|
|
298
|
+
res.json({ success: true, id: result.insertId });
|
|
299
|
+
} catch (err) {
|
|
300
|
+
res.status(500).json({ error: err.message });
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
app.put('/api/employees/:id', requireAuth, async (req, res) => {
|
|
305
|
+
try {
|
|
306
|
+
const { EmpFirstName, EmpLastName, EmpGender, EmpDateOfBirth, EmpEmail, EmpTelephone, EmpAddress, EmpHireDate, EmpStatus, DepartID, PosID } = req.body;
|
|
307
|
+
await db.query(
|
|
308
|
+
'UPDATE Employee SET EmpFirstName=?, EmpLastName=?, EmpGender=?, EmpDateOfBirth=?, EmpEmail=?, EmpTelephone=?, EmpAddress=?, EmpHireDate=?, EmpStatus=?, DepartID=?, PosID=? WHERE EmpID=?',
|
|
309
|
+
[EmpFirstName, EmpLastName, EmpGender, EmpDateOfBirth, EmpEmail, EmpTelephone, EmpAddress, EmpHireDate, EmpStatus, DepartID || null, PosID || null, req.params.id]
|
|
310
|
+
);
|
|
311
|
+
res.json({ success: true });
|
|
312
|
+
} catch (err) {
|
|
313
|
+
res.status(500).json({ error: err.message });
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
app.delete('/api/employees/:id', requireAuth, async (req, res) => {
|
|
318
|
+
try {
|
|
319
|
+
await db.query('DELETE FROM Employee WHERE EmpID = ?', [req.params.id]);
|
|
320
|
+
res.json({ success: true });
|
|
321
|
+
} catch (err) {
|
|
322
|
+
res.status(500).json({ error: err.message });
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// ─── USERS CRUD ───
|
|
327
|
+
|
|
328
|
+
app.get('/api/users', requireAuth, async (req, res) => {
|
|
329
|
+
try {
|
|
330
|
+
const [rows] = await db.query(`
|
|
331
|
+
SELECT u.UserID, u.UserName, u.EmpID, e.EmpFirstName, e.EmpLastName, e.EmpEmail
|
|
332
|
+
FROM Users u
|
|
333
|
+
JOIN Employee e ON u.EmpID = e.EmpID
|
|
334
|
+
ORDER BY u.UserName
|
|
335
|
+
`);
|
|
336
|
+
res.json(rows);
|
|
337
|
+
} catch (err) {
|
|
338
|
+
res.status(500).json({ error: err.message });
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
app.get('/api/users/:id', requireAuth, async (req, res) => {
|
|
343
|
+
try {
|
|
344
|
+
const [rows] = await db.query(`
|
|
345
|
+
SELECT u.UserID, u.UserName, u.EmpID, e.EmpFirstName, e.EmpLastName, e.EmpEmail
|
|
346
|
+
FROM Users u
|
|
347
|
+
JOIN Employee e ON u.EmpID = e.EmpID
|
|
348
|
+
WHERE u.UserID = ?
|
|
349
|
+
`, [req.params.id]);
|
|
350
|
+
if (rows.length === 0) return res.status(404).json({ error: 'User not found' });
|
|
351
|
+
res.json(rows[0]);
|
|
352
|
+
} catch (err) {
|
|
353
|
+
res.status(500).json({ error: err.message });
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
app.post('/api/users', requireAuth, async (req, res) => {
|
|
358
|
+
try {
|
|
359
|
+
const { EmpID, UserName, Password } = req.body;
|
|
360
|
+
const hashed = await bcrypt.hash(Password, 10);
|
|
361
|
+
const [result] = await db.query('INSERT INTO Users (EmpID, UserName, Password) VALUES (?, ?, ?)', [EmpID, UserName, hashed]);
|
|
362
|
+
res.json({ success: true, id: result.insertId });
|
|
363
|
+
} catch (err) {
|
|
364
|
+
res.status(500).json({ error: err.message });
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
app.put('/api/users/:id', requireAuth, async (req, res) => {
|
|
369
|
+
try {
|
|
370
|
+
const { UserName, Password } = req.body;
|
|
371
|
+
if (Password) {
|
|
372
|
+
const hashed = await bcrypt.hash(Password, 10);
|
|
373
|
+
await db.query('UPDATE Users SET UserName = ?, Password = ? WHERE UserID = ?', [UserName, hashed, req.params.id]);
|
|
374
|
+
} else {
|
|
375
|
+
await db.query('UPDATE Users SET UserName = ? WHERE UserID = ?', [UserName, req.params.id]);
|
|
376
|
+
}
|
|
377
|
+
res.json({ success: true });
|
|
378
|
+
} catch (err) {
|
|
379
|
+
res.status(500).json({ error: err.message });
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
app.delete('/api/users/:id', requireAuth, async (req, res) => {
|
|
384
|
+
try {
|
|
385
|
+
await db.query('DELETE FROM Users WHERE UserID = ?', [req.params.id]);
|
|
386
|
+
res.json({ success: true });
|
|
387
|
+
} catch (err) {
|
|
388
|
+
res.status(500).json({ error: err.message });
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// ─── REPORTS ───
|
|
393
|
+
|
|
394
|
+
app.get('/api/reports/employees-on-leave', requireAuth, async (req, res) => {
|
|
395
|
+
try {
|
|
396
|
+
const [rows] = await db.query(`
|
|
397
|
+
SELECT e.*, d.DepartName, p.PosName
|
|
398
|
+
FROM Employee e
|
|
399
|
+
LEFT JOIN Department d ON e.DepartID = d.DepartID
|
|
400
|
+
LEFT JOIN Positionemp p ON e.PosID = p.PosID
|
|
401
|
+
WHERE e.EmpStatus = 'on leave'
|
|
402
|
+
ORDER BY d.DepartName, e.EmpLastName, e.EmpFirstName
|
|
403
|
+
`);
|
|
404
|
+
const departments = {};
|
|
405
|
+
for (const emp of rows) {
|
|
406
|
+
const dept = emp.DepartName || 'No Department';
|
|
407
|
+
if (!departments[dept]) departments[dept] = [];
|
|
408
|
+
departments[dept].push(emp);
|
|
409
|
+
}
|
|
410
|
+
res.json({
|
|
411
|
+
total: rows.length,
|
|
412
|
+
departments
|
|
413
|
+
});
|
|
414
|
+
} catch (err) {
|
|
415
|
+
res.status(500).json({ error: err.message });
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
app.listen(PORT, () => {
|
|
420
|
+
console.log(`HRMS Backend running on port ${PORT}`);
|
|
421
|
+
});
|