create-jinmankn-app 1.0.11 → 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.
Files changed (89) hide show
  1. package/package.json +1 -1
  2. package/templates/EPMS(Employee Payroll Managment System)/backend/routes/protectedRoutes.js +10 -0
  3. package/templates/EPMS(Employee Payroll Managment System)/backend/server.js +2 -0
  4. package/templates/EPMS(Employee Payroll Managment System)/frontend/src/App.jsx +2 -1
  5. package/templates/EPMS(Employee Payroll Managment System)/frontend/src/components/ProtectedRoute.jsx +8 -0
  6. package/templates/EPMS(Employee Payroll Managment System)/package-lock.json +13 -0
  7. package/templates/Faisal Hospital Managment System (FHMS) mysql/backend/routes/secureRoutes.js +14 -0
  8. package/templates/Faisal Hospital Managment System (FHMS) mysql/backend/server.js +2 -0
  9. package/templates/Faisal Hospital Managment System (FHMS) mysql/frontend/src/components/ProtectedRoute.jsx +2 -3
  10. package/templates/Faisal Hospital Managment System (FHMS) mysql/frontend/src/utils/auth.js +13 -0
  11. package/templates/LMS(Library Managment System)/backend/controllers/authController.js +32 -0
  12. package/templates/LMS(Library Managment System)/backend/routes/authRoutes.js +2 -1
  13. package/templates/LMS(Library Managment System)/backend/routes/protectedRoutes.js +19 -0
  14. package/templates/LMS(Library Managment System)/backend/server.js +2 -0
  15. package/templates/LMS(Library Managment System)/frontend/src/App.jsx +74 -15
  16. package/templates/LMS(Library Managment System)/frontend/src/ProtectedRoute.jsx +13 -0
  17. package/templates/LMS(Library Managment System)/frontend/src/pages/Login.jsx +4 -1
  18. package/templates/LMS(Library Managment System)/frontend/src/pages/Register.jsx +87 -0
  19. package/templates/SCMS(Supply Chain Managment System)/backend/routes/protectedRoutes.js +10 -0
  20. package/templates/SCMS(Supply Chain Managment System)/backend/server.js +2 -0
  21. package/templates/SCMS(Supply Chain Managment System)/frontend/src/App.jsx +2 -1
  22. package/templates/SCMS(Supply Chain Managment System)/frontend/src/components/ProtectedRoute.jsx +30 -0
  23. package/templates/SFM(School Fee Managment)/backend/middleware/protectedRoutes.js +10 -0
  24. package/templates/SFM(School Fee Managment)/frontend/src/App.jsx +7 -5
  25. package/templates/SFM(School Fee Managment)/frontend/src/components/ProtectedRoute.jsx +13 -0
  26. package/templates/SIMS(Stock Inventory Managment System)/backend/controllers/protectedController.js +14 -0
  27. package/templates/SIMS(Stock Inventory Managment System)/backend/routes/protectedRoutes.js +10 -0
  28. package/templates/SIMS(Stock Inventory Managment System)/backend/server.js +2 -1
  29. package/templates/SIMS(Stock Inventory Managment System)/frontend/src/App.jsx +57 -7
  30. package/templates/SIMS(Stock Inventory Managment System)/frontend/src/components/ProtectedRoute.jsx +8 -0
  31. package/templates/SMS(Stock Managment System)/backend-project/routes/protectedRoutes.js +18 -0
  32. package/templates/SMS(Stock Managment System)/backend-project/server.js +3 -1
  33. package/templates/SMS(Stock Managment System)/frontend-project/src/App.jsx +36 -7
  34. package/templates/SMS(Stock Managment System)/frontend-project/src/components/AppLayout.jsx +1 -1
  35. package/templates/SMS(Stock Managment System)/frontend-project/src/components/ProtectedRoute.jsx +13 -0
  36. package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Login.jsx +1 -1
  37. package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Product.jsx +211 -78
  38. package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Profile.jsx +1 -1
  39. package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Register.jsx +1 -1
  40. package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Reports.jsx +1 -1
  41. package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Sales.jsx +6 -6
  42. package/templates/SMS(Stock Managment System)/frontend-project/src/pages/Warehouse.jsx +150 -44
  43. package/templates/SMS(Stock Managment System)/package-lock.json +13 -0
  44. package/templates/SRMS(Sales Record Management System)/backend/routes/protectedRoutes.js +13 -0
  45. package/templates/SRMS(Sales Record Management System)/backend/server.js +2 -0
  46. package/templates/SRMS(Sales Record Management System)/frontend/src/App.jsx +2 -1
  47. package/templates/SRMS(Sales Record Management System)/frontend/src/components/ProtectedRoute.jsx +22 -0
  48. package/templates/index/README.md +34 -0
  49. package/templates/index/backend/config/db.js +12 -0
  50. package/templates/index/backend/controllers/authController.js +88 -0
  51. package/templates/index/backend/controllers/departmentController.js +74 -0
  52. package/templates/index/backend/controllers/employeeController.js +204 -0
  53. package/templates/index/backend/controllers/positionController.js +78 -0
  54. package/templates/index/backend/controllers/reportController.js +45 -0
  55. package/templates/index/backend/middleware/auth.js +27 -0
  56. package/templates/index/backend/models/Department.js +15 -0
  57. package/templates/index/backend/models/Employee.js +73 -0
  58. package/templates/index/backend/models/Position.js +20 -0
  59. package/templates/index/backend/models/User.js +32 -0
  60. package/templates/index/backend/package-lock.json +2190 -0
  61. package/templates/index/backend/package.json +22 -0
  62. package/templates/index/backend/routes/authRoutes.js +11 -0
  63. package/templates/index/backend/routes/departmentRoutes.js +17 -0
  64. package/templates/index/backend/routes/employeeRoutes.js +19 -0
  65. package/templates/index/backend/routes/positionRoutes.js +17 -0
  66. package/templates/index/backend/routes/protectedRoutes.js +10 -0
  67. package/templates/index/backend/routes/reportRoutes.js +9 -0
  68. package/templates/index/backend/server.js +30 -0
  69. package/templates/index/frontend/README.md +16 -0
  70. package/templates/index/frontend/eslint.config.js +21 -0
  71. package/templates/index/frontend/index.html +13 -0
  72. package/templates/index/frontend/package-lock.json +3095 -0
  73. package/templates/index/frontend/package.json +31 -0
  74. package/templates/index/frontend/public/favicon.svg +1 -0
  75. package/templates/index/frontend/src/App.css +0 -0
  76. package/templates/index/frontend/src/App.jsx +35 -0
  77. package/templates/index/frontend/src/assets/hero.png +0 -0
  78. package/templates/index/frontend/src/components/DashboardLayout.jsx +90 -0
  79. package/templates/index/frontend/src/components/ProtectedRoute.jsx +9 -0
  80. package/templates/index/frontend/src/index.css +59 -0
  81. package/templates/index/frontend/src/main.jsx +11 -0
  82. package/templates/index/frontend/src/pages/DashboardHome.jsx +58 -0
  83. package/templates/index/frontend/src/pages/Department.jsx +150 -0
  84. package/templates/index/frontend/src/pages/Employee.jsx +366 -0
  85. package/templates/index/frontend/src/pages/Login.jsx +81 -0
  86. package/templates/index/frontend/src/pages/Position.jsx +169 -0
  87. package/templates/index/frontend/src/pages/Register.jsx +102 -0
  88. package/templates/index/frontend/src/pages/Reports.jsx +94 -0
  89. package/templates/index/frontend/vite.config.js +7 -0
@@ -0,0 +1,366 @@
1
+ import { useEffect, useState } from 'react';
2
+ import axios from 'axios';
3
+
4
+ const EMPLOYEE_STATUSES = ['on leave', 'left', 'blacklisted', 'deceased', 'on mission'];
5
+
6
+ function Employee() {
7
+ const [employees, setEmployees] = useState([]);
8
+ const [departments, setDepartments] = useState([]);
9
+ const [positions, setPositions] = useState([]);
10
+ const [editingId, setEditingId] = useState(null);
11
+ const [userName, setUserName] = useState('');
12
+ const [firstName, setFirstName] = useState('');
13
+ const [lastName, setLastName] = useState('');
14
+ const [email, setEmail] = useState('');
15
+ const [address, setAddress] = useState('');
16
+ const [position, setPosition] = useState('');
17
+ const [telephone, setTelephone] = useState('');
18
+ const [gender, setGender] = useState('Male');
19
+ const [dateOfBirth, setDateOfBirth] = useState('');
20
+ const [hireDate, setHireDate] = useState('');
21
+ const [status, setStatus] = useState('on mission');
22
+ const [department, setDepartment] = useState('');
23
+ const [message, setMessage] = useState('');
24
+
25
+ const headers = { Authorization: `Bearer ${localStorage.getItem('token')}` };
26
+
27
+ const getEmployees = async () => {
28
+ try {
29
+ const res = await axios.get('http://localhost:5006/api/employees/list', { headers });
30
+ setEmployees(res.data);
31
+ } catch (err) {
32
+ console.error(err);
33
+ }
34
+ };
35
+
36
+ const getDepartments = async () => {
37
+ try {
38
+ const res = await axios.get('http://localhost:5006/api/departments/list', { headers });
39
+ setDepartments(res.data);
40
+ } catch (err) {
41
+ console.error(err);
42
+ }
43
+ };
44
+
45
+ const getPositions = async () => {
46
+ try {
47
+ const res = await axios.get('http://localhost:5006/api/positions/list', { headers });
48
+ setPositions(res.data);
49
+ } catch (err) {
50
+ console.error(err);
51
+ }
52
+ };
53
+
54
+ useEffect(() => {
55
+ getEmployees();
56
+ getDepartments();
57
+ getPositions();
58
+ }, []);
59
+
60
+ const resetForm = () => {
61
+ setEditingId(null);
62
+ setUserName('');
63
+ setFirstName('');
64
+ setLastName('');
65
+ setEmail('');
66
+ setAddress('');
67
+ setPosition('');
68
+ setTelephone('');
69
+ setGender('Male');
70
+ setDateOfBirth('');
71
+ setHireDate('');
72
+ setStatus('on mission');
73
+ setDepartment('');
74
+ };
75
+
76
+ const handleSubmit = async (e) => {
77
+ e.preventDefault();
78
+ try {
79
+ const payload = {
80
+ userName,
81
+ firstName,
82
+ lastName,
83
+ email,
84
+ gender,
85
+ dateOfBirth,
86
+ telephone,
87
+ address,
88
+ hireDate,
89
+ status,
90
+ department,
91
+ position,
92
+ };
93
+
94
+ if (editingId) {
95
+ await axios.put(`http://localhost:5006/api/employees/${editingId}`, payload, { headers });
96
+ setMessage('Employee updated successfully');
97
+ } else {
98
+ await axios.post('http://localhost:5006/api/employees/add', payload, { headers });
99
+ setMessage('Employee added successfully');
100
+ }
101
+
102
+ resetForm();
103
+ getEmployees();
104
+ } catch (err) {
105
+ setMessage(err.response?.data?.message || 'Failed to save employee');
106
+ }
107
+ };
108
+
109
+ const handleEdit = (emp) => {
110
+ setEditingId(emp._id);
111
+ setUserName(emp.userName || '');
112
+ setFirstName(emp.firstName);
113
+ setLastName(emp.lastName);
114
+ setEmail(emp.email);
115
+ setAddress(emp.address);
116
+ setPosition(emp.position?._id || '');
117
+ setTelephone(emp.telephone);
118
+ setGender(emp.gender);
119
+ setDateOfBirth(emp.dateOfBirth ? emp.dateOfBirth.split('T')[0] : '');
120
+ setHireDate(emp.hireDate ? emp.hireDate.split('T')[0] : '');
121
+ setStatus(EMPLOYEE_STATUSES.includes(emp.status) ? emp.status : 'on mission');
122
+ setDepartment(emp.department?._id || '');
123
+ setMessage('');
124
+ };
125
+
126
+ const handleDelete = async (id) => {
127
+ if (!window.confirm('Delete this employee?')) return;
128
+ try {
129
+ await axios.delete(`http://localhost:5006/api/employees/${id}`, { headers });
130
+ setMessage('Employee deleted successfully');
131
+ if (editingId === id) {
132
+ resetForm();
133
+ }
134
+ getEmployees();
135
+ } catch (err) {
136
+ setMessage(err.response?.data?.message || 'Failed to delete employee');
137
+ }
138
+ };
139
+
140
+ const handleCancel = () => {
141
+ resetForm();
142
+ setMessage('');
143
+ };
144
+
145
+ const isSuccess = message.includes('successfully');
146
+
147
+ return (
148
+ <div>
149
+ <h1 className="mb-6 text-2xl font-bold">Employee Management</h1>
150
+
151
+ {message && (
152
+ <p className={isSuccess ? 'alert-success' : 'alert-error'}>{message}</p>
153
+ )}
154
+
155
+ <form onSubmit={handleSubmit} className="mb-6 box space-y-4">
156
+ <h2 className="font-semibold">{editingId ? 'Edit Employee' : 'Add Employee'}</h2>
157
+ <div className="grid gap-4 sm:grid-cols-2">
158
+ <div>
159
+ <label className="mb-1 block text-sm">Username (optional)</label>
160
+ <input
161
+ type="text"
162
+ value={userName}
163
+ onChange={(e) => setUserName(e.target.value)}
164
+ className="input-field"
165
+ placeholder="Optional user handle"
166
+ />
167
+ </div>
168
+ <div>
169
+ <label className="mb-1 block text-sm">First Name</label>
170
+ <input
171
+ type="text"
172
+ value={firstName}
173
+ onChange={(e) => setFirstName(e.target.value)}
174
+ className="input-field"
175
+ placeholder="Enter first name"
176
+ required
177
+ />
178
+ </div>
179
+ <div>
180
+ <label className="mb-1 block text-sm">Last Name</label>
181
+ <input
182
+ type="text"
183
+ value={lastName}
184
+ onChange={(e) => setLastName(e.target.value)}
185
+ className="input-field"
186
+ placeholder="Enter last name"
187
+ required
188
+ />
189
+ </div>
190
+ <div>
191
+ <label className="mb-1 block text-sm">Email</label>
192
+ <input
193
+ type="email"
194
+ value={email}
195
+ onChange={(e) => setEmail(e.target.value)}
196
+ className="input-field"
197
+ placeholder="Enter employee email"
198
+ required
199
+ />
200
+ </div>
201
+ <div>
202
+ <label className="mb-1 block text-sm">Telephone</label>
203
+ <input
204
+ type="tel"
205
+ value={telephone}
206
+ onChange={(e) => setTelephone(e.target.value)}
207
+ className="input-field"
208
+ placeholder="Enter phone number"
209
+ required
210
+ />
211
+ </div>
212
+ <div>
213
+ <label className="mb-1 block text-sm">Gender</label>
214
+ <select
215
+ value={gender}
216
+ onChange={(e) => setGender(e.target.value)}
217
+ className="input-field"
218
+ >
219
+ <option value="Male">Male</option>
220
+ <option value="Female">Female</option>
221
+ <option value="Other">Other</option>
222
+ </select>
223
+ </div>
224
+ <div>
225
+ <label className="mb-1 block text-sm">Position</label>
226
+ <select
227
+ value={position}
228
+ onChange={(e) => setPosition(e.target.value)}
229
+ className="input-field"
230
+ required
231
+ >
232
+ <option value="">Select position</option>
233
+ {positions.map((pos) => (
234
+ <option key={pos._id} value={pos._id}>{pos.posName}</option>
235
+ ))}
236
+ </select>
237
+ </div>
238
+ <div>
239
+ <label className="mb-1 block text-sm">Status</label>
240
+ <select
241
+ value={status}
242
+ onChange={(e) => setStatus(e.target.value)}
243
+ className="input-field"
244
+ required
245
+ >
246
+ {EMPLOYEE_STATUSES.map((s) => (
247
+ <option key={s} value={s}>{s}</option>
248
+ ))}
249
+ </select>
250
+ </div>
251
+ <div>
252
+ <label className="mb-1 block text-sm">Date of Birth</label>
253
+ <input
254
+ type="date"
255
+ value={dateOfBirth}
256
+ onChange={(e) => setDateOfBirth(e.target.value)}
257
+ className="input-field"
258
+ required
259
+ />
260
+ </div>
261
+ <div>
262
+ <label className="mb-1 block text-sm">Hire Date</label>
263
+ <input
264
+ type="date"
265
+ value={hireDate}
266
+ onChange={(e) => setHireDate(e.target.value)}
267
+ className="input-field"
268
+ required
269
+ />
270
+ </div>
271
+ <div className="sm:col-span-2">
272
+ <label className="mb-1 block text-sm">Address</label>
273
+ <input
274
+ type="text"
275
+ value={address}
276
+ onChange={(e) => setAddress(e.target.value)}
277
+ className="input-field"
278
+ placeholder="Enter employee address"
279
+ required
280
+ />
281
+ </div>
282
+ <div>
283
+ <label className="mb-1 block text-sm">Department</label>
284
+ <select
285
+ value={department}
286
+ onChange={(e) => setDepartment(e.target.value)}
287
+ className="input-field"
288
+ required
289
+ >
290
+ <option value="">Select department</option>
291
+ {departments.map((dept) => (
292
+ <option key={dept._id} value={dept._id}>{dept.departmentName}</option>
293
+ ))}
294
+ </select>
295
+ </div>
296
+ </div>
297
+ <div className="flex items-center gap-3">
298
+ <button type="submit" className="btn-primary">
299
+ {editingId ? 'Update Employee' : 'Add Employee'}
300
+ </button>
301
+ {editingId && (
302
+ <button type="button" className="btn-secondary" onClick={handleCancel}>
303
+ Cancel
304
+ </button>
305
+ )}
306
+ </div>
307
+ </form>
308
+
309
+ <div className="box">
310
+ <h2 className="mb-3 font-semibold">Employee List ({employees.length})</h2>
311
+ {employees.length === 0 ? (
312
+ <p className="text-gray-600">No employees yet.</p>
313
+ ) : (
314
+ <div className="overflow-x-auto">
315
+ <table className="data-table">
316
+ <thead>
317
+ <tr>
318
+ <th>Username</th>
319
+ <th>Name</th>
320
+ <th>Email</th>
321
+ <th>Telephone</th>
322
+ <th>Address</th>
323
+ <th>Gender</th>
324
+ <th>DOB</th>
325
+ <th>Hire Date</th>
326
+ <th>Department</th>
327
+ <th>Position</th>
328
+ <th>Status</th>
329
+ <th>Actions</th>
330
+ </tr>
331
+ </thead>
332
+ <tbody>
333
+ {employees.map((emp) => (
334
+ <tr key={emp._id}>
335
+ <td>{emp.userName || 'N/A'}</td>
336
+ <td>{emp.firstName} {emp.lastName}</td>
337
+ <td>{emp.email}</td>
338
+ <td>{emp.telephone}</td>
339
+ <td>{emp.address}</td>
340
+ <td>{emp.gender}</td>
341
+ <td>{emp.dateOfBirth ? emp.dateOfBirth.split('T')[0] : 'N/A'}</td>
342
+ <td>{emp.hireDate ? emp.hireDate.split('T')[0] : 'N/A'}</td>
343
+ <td>{emp.department?.departmentName || 'N/A'}</td>
344
+ <td>{emp.position?.posName || 'N/A'}</td>
345
+ <td className="capitalize">{emp.status}</td>
346
+ <td className="space-x-2">
347
+ <button type="button" className="btn-secondary btn-small" onClick={() => handleEdit(emp)}>
348
+ Edit
349
+ </button>
350
+ <button type="button" className="btn-danger btn-small" onClick={() => handleDelete(emp._id)}>
351
+ Delete
352
+ </button>
353
+ </td>
354
+ </tr>
355
+ ))}
356
+ </tbody>
357
+ </table>
358
+ </div>
359
+ )}
360
+ </div>
361
+ </div>
362
+ );
363
+ }
364
+
365
+ export default Employee;
366
+
@@ -0,0 +1,81 @@
1
+ import { useState } from 'react';
2
+ import axios from 'axios';
3
+ import { Link, useNavigate } from 'react-router-dom';
4
+
5
+ function Login() {
6
+ const navigate = useNavigate();
7
+ const [email, setEmail] = useState('');
8
+ const [password, setPassword] = useState('');
9
+ const [error, setError] = useState('');
10
+ const [message, setMessage] = useState('');
11
+ const [loading, setLoading] = useState(false);
12
+
13
+ const handleSubmit = async (e) => {
14
+ e.preventDefault();
15
+ setError('');
16
+ setMessage('');
17
+ setLoading(true);
18
+
19
+ try {
20
+ const response = await axios.post('http://localhost:5006/api/auth/login', { email, password });
21
+ localStorage.setItem('token', response.data.token);
22
+ localStorage.setItem('user', JSON.stringify(response.data.user));
23
+ setMessage('Login successful');
24
+ navigate('/dashboard');
25
+ } catch (err) {
26
+ setError(err.response?.data?.message || 'Login failed');
27
+ console.error('Server Error:', err.response?.data || err.message);
28
+ } finally {
29
+ setLoading(false);
30
+ }
31
+ };
32
+
33
+ return (
34
+ <div className="flex min-h-screen items-center justify-center bg-gray-100 px-4">
35
+ <div className="w-full max-w-md box p-6">
36
+ <h1 className="mb-1 text-2xl font-bold text-teal-900">DAB HRMS</h1>
37
+ <p className="mb-6 text-sm text-gray-600">Sign in to your account</p>
38
+
39
+ {message && <p className="alert-success">{message}</p>}
40
+ {error && <p className="alert-error">{error}</p>}
41
+
42
+ <form onSubmit={handleSubmit} className="space-y-4">
43
+ <div>
44
+ <label className="mb-1 block text-sm font-medium">Email</label>
45
+ <input
46
+ type="email"
47
+ required
48
+ placeholder="Enter your email address"
49
+ value={email}
50
+ onChange={(e) => setEmail(e.target.value)}
51
+ className="input-field"
52
+ />
53
+ </div>
54
+
55
+ <div>
56
+ <label className="mb-1 block text-sm font-medium">Password</label>
57
+ <input
58
+ type="password"
59
+ required
60
+ placeholder="Enter your password"
61
+ value={password}
62
+ onChange={(e) => setPassword(e.target.value)}
63
+ className="input-field"
64
+ />
65
+ </div>
66
+
67
+ <button type="submit" disabled={loading} className="btn-primary w-full">
68
+ {loading ? 'Signing in...' : 'Sign in'}
69
+ </button>
70
+ </form>
71
+
72
+ <p className="mt-4 text-center text-sm text-gray-600">
73
+ No account? <Link to="/register" className="text-blue-600 hover:underline">Register</Link>
74
+ </p>
75
+ </div>
76
+ </div>
77
+ );
78
+ }
79
+
80
+ export default Login;
81
+
@@ -0,0 +1,169 @@
1
+ import { useEffect, useState } from 'react';
2
+ import axios from 'axios';
3
+
4
+ function Position() {
5
+ const [positions, setPositions] = useState([]);
6
+ const [posName, setPosName] = useState('');
7
+ const [requiredQualification, setRequiredQualification] = useState('');
8
+ const [editingId, setEditingId] = useState(null);
9
+ const [message, setMessage] = useState('');
10
+
11
+ const headers = { Authorization: `Bearer ${localStorage.getItem('token')}` };
12
+
13
+ const getPositions = async () => {
14
+ try {
15
+ const res = await axios.get('http://localhost:5006/api/positions/list', { headers });
16
+ setPositions(res.data);
17
+ } catch (err) {
18
+ console.error(err);
19
+ }
20
+ };
21
+
22
+ useEffect(() => {
23
+ getPositions();
24
+ }, []);
25
+
26
+ const handleSubmit = async (e) => {
27
+ e.preventDefault();
28
+ try {
29
+ if (editingId) {
30
+ await axios.put(
31
+ `http://localhost:5006/api/positions/${editingId}`,
32
+ { posName, requiredQualification },
33
+ { headers }
34
+ );
35
+ setMessage('Position updated successfully');
36
+ } else {
37
+ await axios.post(
38
+ 'http://localhost:5006/api/positions/add',
39
+ { posName, requiredQualification },
40
+ { headers }
41
+ );
42
+ setMessage('Position added successfully');
43
+ }
44
+
45
+ setPosName('');
46
+ setRequiredQualification('');
47
+ setEditingId(null);
48
+ getPositions();
49
+ } catch (err) {
50
+ setMessage(err.response?.data?.message || 'Failed to save position');
51
+ }
52
+ };
53
+
54
+ const handleEdit = (position) => {
55
+ setPosName(position.posName);
56
+ setRequiredQualification(position.requiredQualification);
57
+ setEditingId(position._id);
58
+ setMessage('');
59
+ };
60
+
61
+ const handleDelete = async (id) => {
62
+ if (!window.confirm('Delete this position?')) return;
63
+ try {
64
+ await axios.delete(`http://localhost:5006/api/positions/${id}`, { headers });
65
+ setMessage('Position deleted successfully');
66
+ if (editingId === id) {
67
+ setEditingId(null);
68
+ setPosName('');
69
+ setRequiredQualification('');
70
+ }
71
+ getPositions();
72
+ } catch (err) {
73
+ setMessage(err.response?.data?.message || 'Failed to delete position');
74
+ }
75
+ };
76
+
77
+ const handleCancel = () => {
78
+ setEditingId(null);
79
+ setPosName('');
80
+ setRequiredQualification('');
81
+ setMessage('');
82
+ };
83
+
84
+ const isSuccess = message.includes('successfully');
85
+
86
+ return (
87
+ <div>
88
+ <h1 className="mb-6 text-2xl font-bold">Position Management</h1>
89
+
90
+ {message && (
91
+ <p className={isSuccess ? 'alert-success' : 'alert-error'}>{message}</p>
92
+ )}
93
+
94
+ <form onSubmit={handleSubmit} className="mb-6 box space-y-4">
95
+ <h2 className="font-semibold">{editingId ? 'Edit Position' : 'Add Position'}</h2>
96
+ <div>
97
+ <label className="mb-1 block text-sm">Position Name</label>
98
+ <input
99
+ type="text"
100
+ value={posName}
101
+ onChange={(e) => setPosName(e.target.value)}
102
+ className="input-field"
103
+ placeholder="e.g. Software Engineer"
104
+ required
105
+ />
106
+ </div>
107
+ <div>
108
+ <label className="mb-1 block text-sm">Required Qualification</label>
109
+ <input
110
+ type="text"
111
+ value={requiredQualification}
112
+ onChange={(e) => setRequiredQualification(e.target.value)}
113
+ className="input-field"
114
+ placeholder="e.g. Bachelor’s in Computer Science"
115
+ required
116
+ />
117
+ </div>
118
+ <div className="flex items-center gap-3">
119
+ <button type="submit" className="btn-primary">
120
+ {editingId ? 'Update Position' : 'Add Position'}
121
+ </button>
122
+ {editingId && (
123
+ <button type="button" className="btn-secondary" onClick={handleCancel}>
124
+ Cancel
125
+ </button>
126
+ )}
127
+ </div>
128
+ </form>
129
+
130
+ <div className="box">
131
+ <h2 className="mb-3 font-semibold">Position List ({positions.length})</h2>
132
+ {positions.length === 0 ? (
133
+ <p className="text-gray-600">No positions yet.</p>
134
+ ) : (
135
+ <div className="overflow-x-auto">
136
+ <table className="data-table">
137
+ <thead>
138
+ <tr>
139
+ <th>Position</th>
140
+ <th>Qualification</th>
141
+ <th>Actions</th>
142
+ </tr>
143
+ </thead>
144
+ <tbody>
145
+ {positions.map((pos) => (
146
+ <tr key={pos._id}>
147
+ <td>{pos.posName}</td>
148
+ <td>{pos.requiredQualification}</td>
149
+ <td className="space-x-2">
150
+ <button type="button" className="btn-secondary btn-small" onClick={() => handleEdit(pos)}>
151
+ Edit
152
+ </button>
153
+ <button type="button" className="btn-danger btn-small" onClick={() => handleDelete(pos._id)}>
154
+ Delete
155
+ </button>
156
+ </td>
157
+ </tr>
158
+ ))}
159
+ </tbody>
160
+ </table>
161
+ </div>
162
+ )}
163
+ </div>
164
+ </div>
165
+ );
166
+ }
167
+
168
+ export default Position;
169
+