create-jinmankn-app 1.0.12 → 1.0.13
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/package.json +1 -1
- package/templates/EPMS(Employee Payroll Managment System)/package-lock.json +13 -0
- package/templates/SMS(Stock Managment System)/backend-project/server.js +1 -1
- package/templates/SMS(Stock Managment System)/frontend-project/src/components/AppLayout.jsx +1 -1
- package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Login.jsx +1 -1
- package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Product.jsx +211 -78
- package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Profile.jsx +1 -1
- package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Register.jsx +1 -1
- package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Reports.jsx +1 -1
- package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Sales.jsx +6 -6
- package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Warehouse.jsx +150 -44
- package/templates/SMS(Stock Managment System)/package-lock.json +13 -0
- package/templates/index/README.md +34 -0
- package/templates/index/backend/config/db.js +12 -0
- package/templates/index/backend/controllers/authController.js +88 -0
- package/templates/index/backend/controllers/departmentController.js +74 -0
- package/templates/index/backend/controllers/employeeController.js +204 -0
- package/templates/index/backend/controllers/positionController.js +78 -0
- package/templates/index/backend/controllers/reportController.js +45 -0
- package/templates/index/backend/middleware/auth.js +27 -0
- package/templates/index/backend/models/Department.js +15 -0
- package/templates/index/backend/models/Employee.js +73 -0
- package/templates/index/backend/models/Position.js +20 -0
- package/templates/index/backend/models/User.js +32 -0
- package/templates/index/backend/package-lock.json +2190 -0
- package/templates/index/backend/package.json +22 -0
- package/templates/index/backend/routes/authRoutes.js +11 -0
- package/templates/index/backend/routes/departmentRoutes.js +17 -0
- package/templates/index/backend/routes/employeeRoutes.js +19 -0
- package/templates/index/backend/routes/positionRoutes.js +17 -0
- package/templates/index/backend/routes/protectedRoutes.js +10 -0
- package/templates/index/backend/routes/reportRoutes.js +9 -0
- package/templates/index/backend/server.js +30 -0
- package/templates/index/frontend/README.md +16 -0
- package/templates/index/frontend/eslint.config.js +21 -0
- package/templates/index/frontend/index.html +13 -0
- package/templates/index/frontend/package-lock.json +3095 -0
- package/templates/index/frontend/package.json +31 -0
- package/templates/index/frontend/public/favicon.svg +1 -0
- package/templates/index/frontend/src/App.css +0 -0
- package/templates/index/frontend/src/App.jsx +35 -0
- package/templates/index/frontend/src/assets/hero.png +0 -0
- package/templates/index/frontend/src/components/DashboardLayout.jsx +90 -0
- package/templates/index/frontend/src/components/ProtectedRoute.jsx +9 -0
- package/templates/index/frontend/src/index.css +59 -0
- package/templates/index/frontend/src/main.jsx +11 -0
- package/templates/index/frontend/src/pages/DashboardHome.jsx +58 -0
- package/templates/index/frontend/src/pages/Department.jsx +150 -0
- package/templates/index/frontend/src/pages/Employee.jsx +366 -0
- package/templates/index/frontend/src/pages/Login.jsx +81 -0
- package/templates/index/frontend/src/pages/Position.jsx +169 -0
- package/templates/index/frontend/src/pages/Register.jsx +102 -0
- package/templates/index/frontend/src/pages/Reports.jsx +94 -0
- package/templates/index/frontend/vite.config.js +7 -0
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
2
|
import axios from 'axios';
|
|
3
3
|
import AppLayout from '../components/AppLayout';
|
|
4
4
|
|
|
5
|
+
const API_BASE = 'http://localhost:1000/api/warehouse';
|
|
6
|
+
|
|
5
7
|
function Warehouse() {
|
|
8
|
+
const [warehouses, setWarehouses] = useState([]);
|
|
6
9
|
const [warehouseCode, setWarehouseCode] = useState('');
|
|
7
10
|
const [warehouseName, setWarehouseName] = useState('');
|
|
8
11
|
const [warehouseLocation, setWarehouseLocation] = useState('');
|
|
9
12
|
const [message, setMessage] = useState('');
|
|
13
|
+
const [error, setError] = useState('');
|
|
14
|
+
const [editingId, setEditingId] = useState(null);
|
|
10
15
|
|
|
11
16
|
const headers = {
|
|
12
17
|
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
|
@@ -16,64 +21,165 @@ function Warehouse() {
|
|
|
16
21
|
setWarehouseCode('');
|
|
17
22
|
setWarehouseName('');
|
|
18
23
|
setWarehouseLocation('');
|
|
24
|
+
setEditingId(null);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const fetchWarehouses = async () => {
|
|
28
|
+
try {
|
|
29
|
+
const response = await axios.get(`${API_BASE}/getwarehouse`, { headers });
|
|
30
|
+
setWarehouses(response.data);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
setError('Failed to load warehouses');
|
|
33
|
+
console.error(err);
|
|
34
|
+
}
|
|
19
35
|
};
|
|
20
36
|
|
|
21
37
|
const handleSubmit = async (e) => {
|
|
22
38
|
e.preventDefault();
|
|
23
39
|
setMessage('');
|
|
40
|
+
setError('');
|
|
24
41
|
|
|
25
42
|
try {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
43
|
+
if (editingId) {
|
|
44
|
+
await axios.put(
|
|
45
|
+
`${API_BASE}/updatewarehouse/${editingId}`,
|
|
46
|
+
{ warehouseCode, warehouseName, warehouseLocation },
|
|
47
|
+
{ headers }
|
|
48
|
+
);
|
|
49
|
+
setMessage('Warehouse updated successfully');
|
|
50
|
+
} else {
|
|
51
|
+
await axios.post(
|
|
52
|
+
`${API_BASE}/addwarehouse`,
|
|
53
|
+
{ warehouseCode, warehouseName, warehouseLocation },
|
|
54
|
+
{ headers }
|
|
55
|
+
);
|
|
56
|
+
setMessage('Warehouse added successfully');
|
|
57
|
+
}
|
|
32
58
|
clearForm();
|
|
59
|
+
fetchWarehouses();
|
|
33
60
|
} catch (err) {
|
|
34
|
-
|
|
35
|
-
console.
|
|
61
|
+
setError(err.response?.data?.message || 'Failed to save warehouse');
|
|
62
|
+
console.error(err);
|
|
36
63
|
}
|
|
37
64
|
};
|
|
38
65
|
|
|
66
|
+
const handleEdit = (warehouse) => {
|
|
67
|
+
setEditingId(warehouse._id);
|
|
68
|
+
setWarehouseCode(warehouse.warehouseCode || '');
|
|
69
|
+
setWarehouseName(warehouse.warehouseName || '');
|
|
70
|
+
setWarehouseLocation(warehouse.warehouseLocation || '');
|
|
71
|
+
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const handleDelete = async (id) => {
|
|
75
|
+
try {
|
|
76
|
+
await axios.delete(`${API_BASE}/deletewarehouse/${id}`, { headers });
|
|
77
|
+
setMessage('Warehouse deleted successfully');
|
|
78
|
+
fetchWarehouses();
|
|
79
|
+
} catch (err) {
|
|
80
|
+
setError('Failed to delete warehouse');
|
|
81
|
+
console.error(err);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
fetchWarehouses();
|
|
87
|
+
}, []);
|
|
88
|
+
|
|
39
89
|
return (
|
|
40
90
|
<AppLayout title="Warehouse management" subtitle="Locations">
|
|
41
|
-
<div className="mx-auto max-w-
|
|
42
|
-
|
|
43
|
-
<p className=
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
91
|
+
<div className="mx-auto max-w-6xl space-y-6">
|
|
92
|
+
<div className="card space-y-4 p-5">
|
|
93
|
+
{message && <p className="alert-success">{message}</p>}
|
|
94
|
+
{error && <p className="alert-error">{error}</p>}
|
|
95
|
+
|
|
96
|
+
<form onSubmit={handleSubmit} className="space-y-3">
|
|
97
|
+
<h2 className="text-lg font-semibold text-slate-800">
|
|
98
|
+
{editingId ? 'Edit warehouse' : 'Add new warehouse'}
|
|
99
|
+
</h2>
|
|
100
|
+
<div className="grid gap-4 sm:grid-cols-3">
|
|
101
|
+
<input
|
|
102
|
+
type="text"
|
|
103
|
+
placeholder="Warehouse code"
|
|
104
|
+
value={warehouseCode}
|
|
105
|
+
onChange={(e) => setWarehouseCode(e.target.value)}
|
|
106
|
+
className="input-field"
|
|
107
|
+
required
|
|
108
|
+
/>
|
|
109
|
+
<input
|
|
110
|
+
type="text"
|
|
111
|
+
placeholder="Warehouse name"
|
|
112
|
+
value={warehouseName}
|
|
113
|
+
onChange={(e) => setWarehouseName(e.target.value)}
|
|
114
|
+
className="input-field"
|
|
115
|
+
required
|
|
116
|
+
/>
|
|
117
|
+
<input
|
|
118
|
+
type="text"
|
|
119
|
+
placeholder="Warehouse location"
|
|
120
|
+
value={warehouseLocation}
|
|
121
|
+
onChange={(e) => setWarehouseLocation(e.target.value)}
|
|
122
|
+
className="input-field"
|
|
123
|
+
required
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
126
|
+
<div className="flex flex-wrap gap-3">
|
|
127
|
+
<button type="submit" className="btn-primary">
|
|
128
|
+
{editingId ? 'Save changes' : 'Add warehouse'}
|
|
129
|
+
</button>
|
|
130
|
+
{editingId && (
|
|
131
|
+
<button type="button" onClick={clearForm} className="btn-secondary">
|
|
132
|
+
Cancel
|
|
133
|
+
</button>
|
|
134
|
+
)}
|
|
135
|
+
</div>
|
|
136
|
+
</form>
|
|
137
|
+
</div>
|
|
47
138
|
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
139
|
+
<div className="card p-5">
|
|
140
|
+
<h2 className="text-lg font-semibold text-slate-800">Warehouses</h2>
|
|
141
|
+
{warehouses.length === 0 ? (
|
|
142
|
+
<p className="mt-4 text-sm text-slate-500">No warehouses available.</p>
|
|
143
|
+
) : (
|
|
144
|
+
<div className="mt-4 overflow-x-auto">
|
|
145
|
+
<table className="min-w-full divide-y divide-slate-200 text-sm">
|
|
146
|
+
<thead>
|
|
147
|
+
<tr className="bg-slate-50 text-left text-slate-600">
|
|
148
|
+
<th className="px-4 py-3">Code</th>
|
|
149
|
+
<th className="px-4 py-3">Name</th>
|
|
150
|
+
<th className="px-4 py-3">Location</th>
|
|
151
|
+
<th className="px-4 py-3">Actions</th>
|
|
152
|
+
</tr>
|
|
153
|
+
</thead>
|
|
154
|
+
<tbody className="divide-y divide-slate-200">
|
|
155
|
+
{warehouses.map((warehouse) => (
|
|
156
|
+
<tr key={warehouse._id} className="hover:bg-slate-50">
|
|
157
|
+
<td className="px-4 py-3">{warehouse.warehouseCode}</td>
|
|
158
|
+
<td className="px-4 py-3">{warehouse.warehouseName}</td>
|
|
159
|
+
<td className="px-4 py-3">{warehouse.warehouseLocation}</td>
|
|
160
|
+
<td className="px-4 py-3 space-x-2">
|
|
161
|
+
<button
|
|
162
|
+
type="button"
|
|
163
|
+
onClick={() => handleEdit(warehouse)}
|
|
164
|
+
className="btn-secondary"
|
|
165
|
+
>
|
|
166
|
+
Edit
|
|
167
|
+
</button>
|
|
168
|
+
<button
|
|
169
|
+
type="button"
|
|
170
|
+
onClick={() => handleDelete(warehouse._id)}
|
|
171
|
+
className="btn-danger"
|
|
172
|
+
>
|
|
173
|
+
Delete
|
|
174
|
+
</button>
|
|
175
|
+
</td>
|
|
176
|
+
</tr>
|
|
177
|
+
))}
|
|
178
|
+
</tbody>
|
|
179
|
+
</table>
|
|
180
|
+
</div>
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
77
183
|
</div>
|
|
78
184
|
</AppLayout>
|
|
79
185
|
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Problems the new system is fixing
|
|
2
|
+
|
|
3
|
+
Data duplication
|
|
4
|
+
Slow manual processes
|
|
5
|
+
Poor record tracking
|
|
6
|
+
Data inconsistency
|
|
7
|
+
Delayed reporting
|
|
8
|
+
Difficulty retrieving information
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
What the new system will do
|
|
12
|
+
|
|
13
|
+
Manage employee records
|
|
14
|
+
Record purchases
|
|
15
|
+
Record sales
|
|
16
|
+
Store data centrally
|
|
17
|
+
Retrieve information quickly
|
|
18
|
+
Generate accurate reports
|
|
19
|
+
Support management decision-making
|
|
20
|
+
Simple summary
|
|
21
|
+
|
|
22
|
+
The web-based system will replace the manual system by centralizing employee, purchase, and sales records, reducing duplication, improving data consistency, speeding up operations, and producing accurate reports.
|
|
23
|
+
|
|
24
|
+
//middleware
|
|
25
|
+
Middleware in the MERN stack is a function that runs between a client's request and the server's response.
|
|
26
|
+
|
|
27
|
+
It is used to:
|
|
28
|
+
|
|
29
|
+
Check authentication (JWT tokens)
|
|
30
|
+
Validate data
|
|
31
|
+
Handle errors
|
|
32
|
+
Log requests
|
|
33
|
+
Modify request/response objects
|
|
34
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const connectDB = async () => {
|
|
4
|
+
try {
|
|
5
|
+
await mongoose.connect('mongodb://localhost:27017/HRMS');
|
|
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,88 @@
|
|
|
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
|
+
function createToken(userId) {
|
|
7
|
+
return jwt.sign({ userId }, JWT_SECRET, { expiresIn: '7d' });
|
|
8
|
+
}
|
|
9
|
+
//this function is formatting the user object to return the id, name, and email when
|
|
10
|
+
function formatUser(user) {
|
|
11
|
+
return {
|
|
12
|
+
id: user._id,
|
|
13
|
+
name: user.name,
|
|
14
|
+
email: user.email,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exports.register = async (req, res) => {
|
|
19
|
+
try {
|
|
20
|
+
const { name, email, password } = req.body;
|
|
21
|
+
|
|
22
|
+
if (!name || !email || !password) {
|
|
23
|
+
return res.status(400).json({ message: 'Name, email, and password are required' });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (password.length < 6) {
|
|
27
|
+
return res.status(400).json({ message: 'Password must be at least 6 characters' });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const existing = await User.findOne({ email: email.trim().toLowerCase() });
|
|
31
|
+
if (existing) {
|
|
32
|
+
return res.status(409).json({ message: 'Email already registered' });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const hashedPassword = await bcrypt.hash(password, 10);
|
|
36
|
+
const user = await User.create({
|
|
37
|
+
name: name.trim(),
|
|
38
|
+
email: email.trim().toLowerCase(),
|
|
39
|
+
password: hashedPassword,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const token = createToken(user._id);
|
|
43
|
+
|
|
44
|
+
res.status(201).json({
|
|
45
|
+
token,
|
|
46
|
+
user: formatUser(user),
|
|
47
|
+
});
|
|
48
|
+
} catch (err) {
|
|
49
|
+
res.status(500).json({ message: err.message });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
exports.login = async (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const { email, password } = req.body;
|
|
56
|
+
|
|
57
|
+
if (!email || !password) {
|
|
58
|
+
return res.status(400).json({ message: 'Email and password are required' });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const user = await User.findOne({ email: email.trim().toLowerCase() });
|
|
62
|
+
if (!user) {
|
|
63
|
+
return res.status(401).json({ message: 'Invalid email or password' });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const match = await bcrypt.compare(password, user.password);
|
|
67
|
+
if (!match) {
|
|
68
|
+
return res.status(401).json({ message: 'Invalid email or password' });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const token = createToken(user._id);
|
|
72
|
+
|
|
73
|
+
res.json({
|
|
74
|
+
token,
|
|
75
|
+
user: formatUser(user),
|
|
76
|
+
});
|
|
77
|
+
} catch (err) {
|
|
78
|
+
res.status(500).json({ message: err.message });
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
exports.getMe = async (req, res) => {
|
|
83
|
+
try {
|
|
84
|
+
res.json({ user: formatUser(req.user) });
|
|
85
|
+
} catch (err) {
|
|
86
|
+
res.status(500).json({ message: err.message });
|
|
87
|
+
}
|
|
88
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const Department = require('../models/Department');
|
|
2
|
+
|
|
3
|
+
exports.addDepartment = async (req, res) => {
|
|
4
|
+
try {
|
|
5
|
+
const { departmentName } = req.body;
|
|
6
|
+
|
|
7
|
+
if (!departmentName) {
|
|
8
|
+
return res.status(400).json({ message: 'Department name is required' });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const existing = await Department.findOne({ departmentName: departmentName.trim() });
|
|
12
|
+
if (existing) {
|
|
13
|
+
return res.status(409).json({ message: 'Department already exists' });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const dept = await Department.create({ departmentName: departmentName.trim() });
|
|
17
|
+
res.status(201).json(dept);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
res.status(500).json({ message: err.message });
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
exports.getDepartments = async (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const departments = await Department.find();
|
|
26
|
+
res.json(departments);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
res.status(500).json({ message: err.message });
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
exports.updateDepartment = async (req, res) => {
|
|
33
|
+
try {
|
|
34
|
+
const { departmentName } = req.body;
|
|
35
|
+
|
|
36
|
+
if (!departmentName) {
|
|
37
|
+
return res.status(400).json({ message: 'Department name is required' });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const department = await Department.findById(req.params.id);
|
|
41
|
+
if (!department) {
|
|
42
|
+
return res.status(404).json({ message: 'Department not found' });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const existing = await Department.findOne({
|
|
46
|
+
departmentName: departmentName.trim(),
|
|
47
|
+
_id: { $ne: req.params.id },
|
|
48
|
+
});
|
|
49
|
+
if (existing) {
|
|
50
|
+
return res.status(409).json({ message: 'Department already exists' });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
department.departmentName = departmentName.trim();
|
|
54
|
+
await department.save();
|
|
55
|
+
|
|
56
|
+
res.json(department);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
res.status(500).json({ message: err.message });
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
exports.deleteDepartment = async (req, res) => {
|
|
63
|
+
try {
|
|
64
|
+
const department = await Department.findById(req.params.id);
|
|
65
|
+
if (!department) {
|
|
66
|
+
return res.status(404).json({ message: 'Department not found' });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await Department.findByIdAndDelete(req.params.id);
|
|
70
|
+
res.json({ message: 'Department deleted successfully' });
|
|
71
|
+
} catch (err) {
|
|
72
|
+
res.status(500).json({ message: err.message });
|
|
73
|
+
}
|
|
74
|
+
};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
const Employee = require('../models/Employee');
|
|
2
|
+
const Department = require('../models/Department');
|
|
3
|
+
const Position = require('../models/Position');
|
|
4
|
+
const User = require('../models/User');
|
|
5
|
+
|
|
6
|
+
exports.addEmployee = async (req, res) => {
|
|
7
|
+
try {
|
|
8
|
+
const {
|
|
9
|
+
userName,
|
|
10
|
+
firstName,
|
|
11
|
+
lastName,
|
|
12
|
+
email,
|
|
13
|
+
gender,
|
|
14
|
+
dateOfBirth,
|
|
15
|
+
telephone,
|
|
16
|
+
address,
|
|
17
|
+
hireDate,
|
|
18
|
+
status,
|
|
19
|
+
department,
|
|
20
|
+
position,
|
|
21
|
+
} = req.body;
|
|
22
|
+
|
|
23
|
+
if (
|
|
24
|
+
!firstName ||
|
|
25
|
+
!lastName ||
|
|
26
|
+
!email ||
|
|
27
|
+
!gender ||
|
|
28
|
+
!dateOfBirth ||
|
|
29
|
+
!telephone ||
|
|
30
|
+
!address ||
|
|
31
|
+
!hireDate ||
|
|
32
|
+
!status ||
|
|
33
|
+
!department ||
|
|
34
|
+
!position
|
|
35
|
+
) {
|
|
36
|
+
return res.status(400).json({ message: 'All employee fields are required' });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const existing = await Employee.findOne({ email: email.toLowerCase() });
|
|
40
|
+
if (existing) {
|
|
41
|
+
return res.status(409).json({ message: 'Employee email already exists' });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const departmentExists = await Department.findById(department);
|
|
45
|
+
if (!departmentExists) {
|
|
46
|
+
return res.status(404).json({ message: 'Department not found' });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const positionExists = await Position.findById(position);
|
|
50
|
+
if (!positionExists) {
|
|
51
|
+
return res.status(404).json({ message: 'Position not found' });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const employee = await Employee.create({
|
|
55
|
+
userName,
|
|
56
|
+
firstName: firstName.trim(),
|
|
57
|
+
lastName: lastName.trim(),
|
|
58
|
+
email: email.toLowerCase(),
|
|
59
|
+
gender,
|
|
60
|
+
dateOfBirth,
|
|
61
|
+
telephone: telephone.trim(),
|
|
62
|
+
address: address.trim(),
|
|
63
|
+
hireDate,
|
|
64
|
+
status,
|
|
65
|
+
department,
|
|
66
|
+
position,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (email) {
|
|
70
|
+
const user = await User.findOne({ email: email.toLowerCase() });
|
|
71
|
+
if (user) {
|
|
72
|
+
user.employee = employee._id;
|
|
73
|
+
await user.save();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
res.status(201).json(employee);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
res.status(500).json({ message: err.message });
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
exports.getEmployees = async (req, res) => {
|
|
84
|
+
try {
|
|
85
|
+
const employees = await Employee.find().populate('department position');
|
|
86
|
+
res.json(employees);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
res.status(500).json({ message: err.message });
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
exports.getEmployee = async (req, res) => {
|
|
93
|
+
try {
|
|
94
|
+
const employee = await Employee.findById(req.params.id).populate('department position');
|
|
95
|
+
if (!employee) {
|
|
96
|
+
return res.status(404).json({ message: 'Employee not found' });
|
|
97
|
+
}
|
|
98
|
+
res.json(employee);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
res.status(500).json({ message: err.message });
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
exports.updateEmployee = async (req, res) => {
|
|
105
|
+
try {
|
|
106
|
+
const employee = await Employee.findById(req.params.id);
|
|
107
|
+
if (!employee) {
|
|
108
|
+
return res.status(404).json({ message: 'Employee not found' });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const {
|
|
112
|
+
userName,
|
|
113
|
+
firstName,
|
|
114
|
+
lastName,
|
|
115
|
+
email,
|
|
116
|
+
gender,
|
|
117
|
+
dateOfBirth,
|
|
118
|
+
telephone,
|
|
119
|
+
address,
|
|
120
|
+
hireDate,
|
|
121
|
+
status,
|
|
122
|
+
department,
|
|
123
|
+
position,
|
|
124
|
+
} = req.body;
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
!firstName ||
|
|
128
|
+
!lastName ||
|
|
129
|
+
!email ||
|
|
130
|
+
!gender ||
|
|
131
|
+
!dateOfBirth ||
|
|
132
|
+
!telephone ||
|
|
133
|
+
!address ||
|
|
134
|
+
!hireDate ||
|
|
135
|
+
!status ||
|
|
136
|
+
!department ||
|
|
137
|
+
!position
|
|
138
|
+
) {
|
|
139
|
+
return res.status(400).json({ message: 'All employee fields are required' });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const existing = await Employee.findOne({
|
|
143
|
+
email: email.toLowerCase(),
|
|
144
|
+
_id: { $ne: req.params.id },
|
|
145
|
+
});
|
|
146
|
+
if (existing) {
|
|
147
|
+
return res.status(409).json({ message: 'Employee email already exists' });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const departmentExists = await Department.findById(department);
|
|
151
|
+
if (!departmentExists) {
|
|
152
|
+
return res.status(404).json({ message: 'Department not found' });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const positionExists = await Position.findById(position);
|
|
156
|
+
if (!positionExists) {
|
|
157
|
+
return res.status(404).json({ message: 'Position not found' });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const oldEmail = employee.email;
|
|
161
|
+
|
|
162
|
+
employee.userName = userName;
|
|
163
|
+
employee.firstName = firstName.trim();
|
|
164
|
+
employee.lastName = lastName.trim();
|
|
165
|
+
employee.email = email.toLowerCase();
|
|
166
|
+
employee.gender = gender;
|
|
167
|
+
employee.dateOfBirth = dateOfBirth;
|
|
168
|
+
employee.telephone = telephone.trim();
|
|
169
|
+
employee.address = address.trim();
|
|
170
|
+
employee.hireDate = hireDate;
|
|
171
|
+
employee.status = status;
|
|
172
|
+
employee.department = department;
|
|
173
|
+
employee.position = position;
|
|
174
|
+
|
|
175
|
+
await employee.save();
|
|
176
|
+
|
|
177
|
+
if (oldEmail !== email.toLowerCase()) {
|
|
178
|
+
const user = await User.findOne({ email: oldEmail });
|
|
179
|
+
if (user) {
|
|
180
|
+
user.email = email.toLowerCase();
|
|
181
|
+
await user.save();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const updatedEmployee = await Employee.findById(req.params.id).populate('department position');
|
|
186
|
+
res.json(updatedEmployee);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
res.status(500).json({ message: err.message });
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
exports.deleteEmployee = async (req, res) => {
|
|
193
|
+
try {
|
|
194
|
+
const employee = await Employee.findById(req.params.id);
|
|
195
|
+
if (!employee) {
|
|
196
|
+
return res.status(404).json({ message: 'Employee not found' });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
await Employee.findByIdAndDelete(req.params.id);
|
|
200
|
+
res.json({ message: 'Employee deleted successfully' });
|
|
201
|
+
} catch (err) {
|
|
202
|
+
res.status(500).json({ message: err.message });
|
|
203
|
+
}
|
|
204
|
+
};
|