create-jinmankn-app 1.0.13 → 1.0.15

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 (49) hide show
  1. package/package.json +1 -1
  2. package/templates/HRMS (reba kuri iyi bro)/README.md +1 -0
  3. package/templates/{index/backend/controllers/authController.js → HRMS (reba kuri iyi bro)/backend/controllers/authController.js } +60 -10
  4. package/templates/HRMS (reba kuri iyi bro)/backend/cookiejar.txt +4 -0
  5. package/templates/HRMS (reba kuri iyi bro)/backend/middleware/auth.js +28 -0
  6. package/templates/{index/backend/models/User.js → HRMS (reba kuri iyi bro)/backend/models/User.js } +5 -0
  7. package/templates/{index/backend/package-lock.json → HRMS (reba kuri iyi bro)/backend/package-lock.json } +54 -13
  8. package/templates/{index/backend/package.json → HRMS (reba kuri iyi bro)/backend/package.json } +1 -0
  9. package/templates/HRMS (reba kuri iyi bro)/backend/routes/authRoutes.js +13 -0
  10. package/templates/{index/backend/routes/departmentRoutes.js → HRMS (reba kuri iyi bro)/backend/routes/departmentRoutes.js } +4 -4
  11. package/templates/{index/backend/routes/employeeRoutes.js → HRMS (reba kuri iyi bro)/backend/routes/employeeRoutes.js } +4 -4
  12. package/templates/{index/backend/routes/positionRoutes.js → HRMS (reba kuri iyi bro)/backend/routes/positionRoutes.js } +4 -4
  13. package/templates/{index/backend/routes/reportRoutes.js → HRMS (reba kuri iyi bro)/backend/routes/reportRoutes.js } +2 -2
  14. package/templates/{index/backend/server.js → HRMS (reba kuri iyi bro)/backend/server.js } +23 -1
  15. package/templates/{index/frontend/src/App.jsx → HRMS (reba kuri iyi bro)/frontend/src/App.jsx } +3 -1
  16. package/templates/{index/frontend/src/components/DashboardLayout.jsx → HRMS (reba kuri iyi bro)/frontend/src/components/DashboardLayout.jsx } +24 -5
  17. package/templates/{index/frontend/src/components/ProtectedRoute.jsx → HRMS (reba kuri iyi bro)/frontend/src/components/ProtectedRoute.jsx } +2 -2
  18. package/templates/{index/frontend/src/main.jsx → HRMS (reba kuri iyi bro)/frontend/src/main.jsx } +4 -1
  19. package/templates/HRMS (reba kuri iyi bro)/frontend/src/pages/CreateUser.jsx +150 -0
  20. package/templates/{index/frontend/src/pages/DashboardHome.jsx → HRMS (reba kuri iyi bro)/frontend/src/pages/DashboardHome.jsx } +4 -4
  21. package/templates/{index/frontend/src/pages/Department.jsx → HRMS (reba kuri iyi bro)/frontend/src/pages/Department.jsx } +47 -36
  22. package/templates/{index/frontend/src/pages/Employee.jsx → HRMS (reba kuri iyi bro)/frontend/src/pages/Employee.jsx } +27 -16
  23. package/templates/{index/frontend/src/pages/Login.jsx → HRMS (reba kuri iyi bro)/frontend/src/pages/Login.jsx } +7 -3
  24. package/templates/{index/frontend/src/pages/Position.jsx → HRMS (reba kuri iyi bro)/frontend/src/pages/Position.jsx } +27 -16
  25. package/templates/{index/frontend/src/pages/Register.jsx → HRMS (reba kuri iyi bro)/frontend/src/pages/Register.jsx } +10 -6
  26. package/templates/{index/frontend/src/pages/Reports.jsx → HRMS (reba kuri iyi bro)/frontend/src/pages/Reports.jsx } +2 -3
  27. package/templates/HRMS (reba kuri iyi bro)/frontend/vite.config.js +18 -0
  28. package/templates/index/README.md +0 -34
  29. package/templates/index/backend/middleware/auth.js +0 -27
  30. package/templates/index/backend/routes/authRoutes.js +0 -11
  31. package/templates/index/frontend/vite.config.js +0 -7
  32. /package/templates/{index → HRMS (reba kuri iyi bro)}/backend/config/db.js +0 -0
  33. /package/templates/{index → HRMS (reba kuri iyi bro)}/backend/controllers/departmentController.js +0 -0
  34. /package/templates/{index → HRMS (reba kuri iyi bro)}/backend/controllers/employeeController.js +0 -0
  35. /package/templates/{index → HRMS (reba kuri iyi bro)}/backend/controllers/positionController.js +0 -0
  36. /package/templates/{index → HRMS (reba kuri iyi bro)}/backend/controllers/reportController.js +0 -0
  37. /package/templates/{index → HRMS (reba kuri iyi bro)}/backend/models/Department.js +0 -0
  38. /package/templates/{index → HRMS (reba kuri iyi bro)}/backend/models/Employee.js +0 -0
  39. /package/templates/{index → HRMS (reba kuri iyi bro)}/backend/models/Position.js +0 -0
  40. /package/templates/{index → HRMS (reba kuri iyi bro)}/backend/routes/protectedRoutes.js +0 -0
  41. /package/templates/{index → HRMS (reba kuri iyi bro)}/frontend/README.md +0 -0
  42. /package/templates/{index → HRMS (reba kuri iyi bro)}/frontend/eslint.config.js +0 -0
  43. /package/templates/{index → HRMS (reba kuri iyi bro)}/frontend/index.html +0 -0
  44. /package/templates/{index → HRMS (reba kuri iyi bro)}/frontend/package-lock.json +0 -0
  45. /package/templates/{index → HRMS (reba kuri iyi bro)}/frontend/package.json +0 -0
  46. /package/templates/{index → HRMS (reba kuri iyi bro)}/frontend/public/favicon.svg +0 -0
  47. /package/templates/{index → HRMS (reba kuri iyi bro)}/frontend/src/App.css +0 -0
  48. /package/templates/{index → HRMS (reba kuri iyi bro)}/frontend/src/assets/hero.png +0 -0
  49. /package/templates/{index → HRMS (reba kuri iyi bro)}/frontend/src/index.css +0 -0
@@ -0,0 +1,150 @@
1
+ import { useEffect, useState } from 'react';
2
+ import axios from 'axios';
3
+
4
+ function CreateUser() {
5
+ const [employees, setEmployees] = useState([]);
6
+ const [selectedEmployeeId, setSelectedEmployeeId] = useState('');
7
+ const [password, setPassword] = useState('');
8
+ const [message, setMessage] = useState('');
9
+ const [error, setError] = useState('');
10
+ const [loading, setLoading] = useState(false);
11
+ const [user, setUser] = useState(null);
12
+
13
+ useEffect(() => {
14
+ const storedUser = localStorage.getItem('user');
15
+ if (storedUser) {
16
+ setUser(JSON.parse(storedUser));
17
+ }
18
+
19
+ axios
20
+ .get('/api/auth/me', {
21
+ withCredentials: true,
22
+ })
23
+ .then((response) => {
24
+ setUser(response.data.user);
25
+ localStorage.setItem('user', JSON.stringify(response.data.user));
26
+ })
27
+ .catch((err) => {
28
+ setError(err.response?.data?.message || 'Unable to verify admin rights');
29
+ });
30
+ }, []);
31
+
32
+ useEffect(() => {
33
+ if (!user || user.role !== 'admin') return;
34
+
35
+ axios
36
+ .get('/api/employees/list', {
37
+ withCredentials: true,
38
+ })
39
+ .then((response) => setEmployees(response.data))
40
+ .catch((err) => setError(err.response?.data?.message || 'Could not load employees'));
41
+ }, [user]);
42
+
43
+ const handleSubmit = async (e) => {
44
+ e.preventDefault();
45
+ setError('');
46
+ setMessage('');
47
+
48
+ if (!selectedEmployeeId) {
49
+ setError('Please select an existing employee.');
50
+ return;
51
+ }
52
+
53
+ if (password.length < 6) {
54
+ setError('Password must be at least 6 characters long.');
55
+ return;
56
+ }
57
+
58
+ setLoading(true);
59
+
60
+ try {
61
+ const response = await axios.post(
62
+ '/api/auth/users',
63
+ {
64
+ employeeId: selectedEmployeeId,
65
+ password,
66
+ },
67
+ {
68
+ withCredentials: true,
69
+ }
70
+ );
71
+
72
+ setMessage(`User account created for ${response.data.user.name}.`);
73
+ setSelectedEmployeeId('');
74
+ setPassword('');
75
+ } catch (err) {
76
+ setError(err.response?.data?.message || 'Failed to create user account');
77
+ console.error(err.response?.data || err.message);
78
+ } finally {
79
+ setLoading(false);
80
+ }
81
+ };
82
+
83
+ if (!user) {
84
+ return (
85
+ <div className="rounded-xl bg-white p-6 shadow-sm">
86
+ <h1 className="mb-4 text-2xl font-bold">Create User</h1>
87
+ <p className="text-sm text-gray-600">Loading your account...</p>
88
+ </div>
89
+ );
90
+ }
91
+
92
+ if (user.role !== 'admin') {
93
+ return (
94
+ <div className="rounded-xl bg-white p-6 shadow-sm">
95
+ <h1 className="mb-4 text-2xl font-bold">Access Denied</h1>
96
+ <p className="text-sm text-gray-600">Only admins can create dashboard users.</p>
97
+ </div>
98
+ );
99
+ }
100
+
101
+ return (
102
+ <div className="rounded-xl bg-white p-6 shadow-sm">
103
+ <h1 className="mb-4 text-2xl font-bold">Create Dashboard User</h1>
104
+ <p className="mb-4 text-sm text-gray-600">
105
+ Choose an existing employee and assign a password. New users will be created with the <strong>user</strong> role.
106
+ </p>
107
+
108
+ {message && <p className="alert-success mb-4">{message}</p>}
109
+ {error && <p className="alert-error mb-4">{error}</p>}
110
+
111
+ <form onSubmit={handleSubmit} className="space-y-4">
112
+ <div>
113
+ <label className="mb-1 block text-sm font-medium">Employee</label>
114
+ <select
115
+ value={selectedEmployeeId}
116
+ onChange={(e) => setSelectedEmployeeId(e.target.value)}
117
+ className="input-field w-full"
118
+ >
119
+ <option value="">Select an employee</option>
120
+ {employees.map((employee) => (
121
+ <option key={employee._id} value={employee._id}>
122
+ {employee.firstName} {employee.lastName} — {employee.email}
123
+ </option>
124
+ ))}
125
+ </select>
126
+ </div>
127
+
128
+ <div>
129
+ <label className="mb-1 block text-sm font-medium">Password</label>
130
+ <input
131
+ type="password"
132
+ required
133
+ minLength={6}
134
+ placeholder="Enter a password for this user"
135
+ value={password}
136
+ onChange={(e) => setPassword(e.target.value)}
137
+ className="input-field w-full"
138
+ />
139
+ </div>
140
+
141
+ <button type="submit" disabled={loading} className="btn-primary w-full">
142
+ {loading ? 'Creating user...' : 'Create User'}
143
+ </button>
144
+ </form>
145
+ </div>
146
+ );
147
+ }
148
+
149
+ export default CreateUser;
150
+
@@ -8,11 +8,10 @@ function DashboardHome() {
8
8
  const [positions, setPositions] = useState([]);
9
9
 
10
10
  useEffect(() => {
11
- const headers = { Authorization: `Bearer ${localStorage.getItem('token')}` };
12
11
  Promise.all([
13
- axios.get('http://localhost:5006/api/departments/list', { headers }),
14
- axios.get('http://localhost:5006/api/employees/list', { headers }),
15
- axios.get('http://localhost:5006/api/positions/list', { headers }),
12
+ axios.get('/api/departments/list', { withCredentials: true }),
13
+ axios.get('/api/employees/list', { withCredentials: true }),
14
+ axios.get('/api/positions/list', { withCredentials: true }),
16
15
  ])
17
16
  .then(([deptRes, empRes, posRes]) => {
18
17
  setDepartments(deptRes.data);
@@ -56,3 +55,4 @@ function DashboardHome() {
56
55
 
57
56
  export default DashboardHome;
58
57
 
58
+
@@ -6,12 +6,11 @@ function Department() {
6
6
  const [departmentName, setDepartmentName] = useState('');
7
7
  const [editingId, setEditingId] = useState(null);
8
8
  const [message, setMessage] = useState('');
9
-
10
- const headers = { Authorization: `Bearer ${localStorage.getItem('token')}` };
9
+ const currentUser = JSON.parse(localStorage.getItem('user') || 'null');
11
10
 
12
11
  const getDepartments = async () => {
13
12
  try {
14
- const res = await axios.get('http://localhost:5006/api/departments/list', { headers });
13
+ const res = await axios.get('/api/departments/list', { withCredentials: true });
15
14
  setDepartments(res.data);
16
15
  } catch (err) {
17
16
  console.error(err);
@@ -27,16 +26,16 @@ function Department() {
27
26
  try {
28
27
  if (editingId) {
29
28
  await axios.put(
30
- `http://localhost:5006/api/departments/${editingId}`,
29
+ `/api/departments/${editingId}`,
31
30
  { departmentName },
32
- { headers }
31
+ { withCredentials: true }
33
32
  );
34
33
  setMessage('Department updated successfully');
35
34
  } else {
36
35
  await axios.post(
37
- 'http://localhost:5006/api/departments/add',
36
+ '/api/departments/add',
38
37
  { departmentName },
39
- { headers }
38
+ { withCredentials: true }
40
39
  );
41
40
  setMessage('Department added successfully');
42
41
  }
@@ -57,7 +56,7 @@ function Department() {
57
56
  const handleDelete = async (id) => {
58
57
  if (!window.confirm('Delete this department?')) return;
59
58
  try {
60
- await axios.delete(`http://localhost:5006/api/departments/${id}`, { headers });
59
+ await axios.delete(`/api/departments/${id}`, { withCredentials: true });
61
60
  setMessage('Department deleted successfully');
62
61
  if (editingId === id) {
63
62
  setEditingId(null);
@@ -85,30 +84,37 @@ function Department() {
85
84
  <p className={isSuccess ? 'alert-success' : 'alert-error'}>{message}</p>
86
85
  )}
87
86
 
88
- <form onSubmit={handleSubmit} className="mb-6 box space-y-4">
89
- <h2 className="font-semibold">{editingId ? 'Edit Department' : 'Add Department'}</h2>
90
- <div>
91
- <label className="mb-1 block text-sm">Department Name</label>
92
- <input
93
- type="text"
94
- value={departmentName}
95
- onChange={(e) => setDepartmentName(e.target.value)}
96
- className="input-field"
97
- placeholder="e.g. Human Resources"
98
- required
99
- />
100
- </div>
101
- <div className="flex items-center gap-3">
102
- <button type="submit" className="btn-primary">
103
- {editingId ? 'Update Department' : 'Add Department'}
104
- </button>
105
- {editingId && (
106
- <button type="button" className="btn-secondary" onClick={handleCancel}>
107
- Cancel
87
+ {currentUser?.role === 'admin' ? (
88
+ <form onSubmit={handleSubmit} className="mb-6 box space-y-4">
89
+ <h2 className="font-semibold">{editingId ? 'Edit Department' : 'Add Department'}</h2>
90
+ <div>
91
+ <label className="mb-1 block text-sm">Department Name</label>
92
+ <input
93
+ type="text"
94
+ value={departmentName}
95
+ onChange={(e) => setDepartmentName(e.target.value)}
96
+ className="input-field"
97
+ placeholder="e.g. Human Resources"
98
+ required
99
+ />
100
+ </div>
101
+ <div className="flex items-center gap-3">
102
+ <button type="submit" className="btn-primary">
103
+ {editingId ? 'Update Department' : 'Add Department'}
108
104
  </button>
109
- )}
105
+ {editingId && (
106
+ <button type="button" className="btn-secondary" onClick={handleCancel}>
107
+ Cancel
108
+ </button>
109
+ )}
110
+ </div>
111
+ </form>
112
+ ) : (
113
+ <div className="mb-6 box">
114
+ <h2 className="font-semibold">Departments (read-only)</h2>
115
+ <p className="text-sm text-gray-600">You have read-only access. Contact an admin for changes.</p>
110
116
  </div>
111
- </form>
117
+ )}
112
118
 
113
119
  <div className="box">
114
120
  <h2 className="mb-3 font-semibold">Department List ({departments.length})</h2>
@@ -128,12 +134,16 @@ function Department() {
128
134
  <tr key={dept._id}>
129
135
  <td>{dept.departmentName}</td>
130
136
  <td className="space-x-2">
131
- <button type="button" className="btn-secondary btn-small" onClick={() => handleEdit(dept)}>
132
- Edit
133
- </button>
134
- <button type="button" className="btn-danger btn-small" onClick={() => handleDelete(dept._id)}>
135
- Delete
136
- </button>
137
+ {currentUser?.role === 'admin' && (
138
+ <>
139
+ <button type="button" className="btn-secondary btn-small" onClick={() => handleEdit(dept)}>
140
+ Edit
141
+ </button>
142
+ <button type="button" className="btn-danger btn-small" onClick={() => handleDelete(dept._id)}>
143
+ Delete
144
+ </button>
145
+ </>
146
+ )}
137
147
  </td>
138
148
  </tr>
139
149
  ))}
@@ -147,4 +157,5 @@ function Department() {
147
157
  }
148
158
 
149
159
  export default Department;
160
+
150
161
 
@@ -21,12 +21,11 @@ function Employee() {
21
21
  const [status, setStatus] = useState('on mission');
22
22
  const [department, setDepartment] = useState('');
23
23
  const [message, setMessage] = useState('');
24
-
25
- const headers = { Authorization: `Bearer ${localStorage.getItem('token')}` };
24
+ const currentUser = JSON.parse(localStorage.getItem('user') || 'null');
26
25
 
27
26
  const getEmployees = async () => {
28
27
  try {
29
- const res = await axios.get('http://localhost:5006/api/employees/list', { headers });
28
+ const res = await axios.get('/api/employees/list', { withCredentials: true });
30
29
  setEmployees(res.data);
31
30
  } catch (err) {
32
31
  console.error(err);
@@ -35,7 +34,7 @@ function Employee() {
35
34
 
36
35
  const getDepartments = async () => {
37
36
  try {
38
- const res = await axios.get('http://localhost:5006/api/departments/list', { headers });
37
+ const res = await axios.get('/api/departments/list', { withCredentials: true });
39
38
  setDepartments(res.data);
40
39
  } catch (err) {
41
40
  console.error(err);
@@ -44,7 +43,7 @@ function Employee() {
44
43
 
45
44
  const getPositions = async () => {
46
45
  try {
47
- const res = await axios.get('http://localhost:5006/api/positions/list', { headers });
46
+ const res = await axios.get('/api/positions/list', { withCredentials: true });
48
47
  setPositions(res.data);
49
48
  } catch (err) {
50
49
  console.error(err);
@@ -92,10 +91,10 @@ function Employee() {
92
91
  };
93
92
 
94
93
  if (editingId) {
95
- await axios.put(`http://localhost:5006/api/employees/${editingId}`, payload, { headers });
94
+ await axios.put(`/api/employees/${editingId}`, payload, { withCredentials: true });
96
95
  setMessage('Employee updated successfully');
97
96
  } else {
98
- await axios.post('http://localhost:5006/api/employees/add', payload, { headers });
97
+ await axios.post('/api/employees/add', payload, { withCredentials: true });
99
98
  setMessage('Employee added successfully');
100
99
  }
101
100
 
@@ -126,7 +125,7 @@ function Employee() {
126
125
  const handleDelete = async (id) => {
127
126
  if (!window.confirm('Delete this employee?')) return;
128
127
  try {
129
- await axios.delete(`http://localhost:5006/api/employees/${id}`, { headers });
128
+ await axios.delete(`/api/employees/${id}`, { withCredentials: true });
130
129
  setMessage('Employee deleted successfully');
131
130
  if (editingId === id) {
132
131
  resetForm();
@@ -152,7 +151,8 @@ function Employee() {
152
151
  <p className={isSuccess ? 'alert-success' : 'alert-error'}>{message}</p>
153
152
  )}
154
153
 
155
- <form onSubmit={handleSubmit} className="mb-6 box space-y-4">
154
+ {currentUser?.role === 'admin' ? (
155
+ <form onSubmit={handleSubmit} className="mb-6 box space-y-4">
156
156
  <h2 className="font-semibold">{editingId ? 'Edit Employee' : 'Add Employee'}</h2>
157
157
  <div className="grid gap-4 sm:grid-cols-2">
158
158
  <div>
@@ -304,7 +304,13 @@ function Employee() {
304
304
  </button>
305
305
  )}
306
306
  </div>
307
- </form>
307
+ </form>
308
+ ) : (
309
+ <div className="mb-6 box">
310
+ <h2 className="font-semibold">Employees (read-only)</h2>
311
+ <p className="text-sm text-gray-600">You have read-only access. Contact an admin for changes.</p>
312
+ </div>
313
+ )}
308
314
 
309
315
  <div className="box">
310
316
  <h2 className="mb-3 font-semibold">Employee List ({employees.length})</h2>
@@ -344,12 +350,16 @@ function Employee() {
344
350
  <td>{emp.position?.posName || 'N/A'}</td>
345
351
  <td className="capitalize">{emp.status}</td>
346
352
  <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
+ {currentUser?.role === 'admin' && (
354
+ <>
355
+ <button type="button" className="btn-secondary btn-small" onClick={() => handleEdit(emp)}>
356
+ Edit
357
+ </button>
358
+ <button type="button" className="btn-danger btn-small" onClick={() => handleDelete(emp._id)}>
359
+ Delete
360
+ </button>
361
+ </>
362
+ )}
353
363
  </td>
354
364
  </tr>
355
365
  ))}
@@ -364,3 +374,4 @@ function Employee() {
364
374
 
365
375
  export default Employee;
366
376
 
377
+
@@ -17,8 +17,11 @@ function Login() {
17
17
  setLoading(true);
18
18
 
19
19
  try {
20
- const response = await axios.post('http://localhost:5006/api/auth/login', { email, password });
21
- localStorage.setItem('token', response.data.token);
20
+ const response = await axios.post(
21
+ '/api/auth/login',
22
+ { email, password },
23
+ { withCredentials: true }
24
+ );
22
25
  localStorage.setItem('user', JSON.stringify(response.data.user));
23
26
  setMessage('Login successful');
24
27
  navigate('/dashboard');
@@ -70,7 +73,7 @@ function Login() {
70
73
  </form>
71
74
 
72
75
  <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>
76
+ If you do not have an account, ask your administrator to create one.
74
77
  </p>
75
78
  </div>
76
79
  </div>
@@ -78,4 +81,5 @@ function Login() {
78
81
  }
79
82
 
80
83
  export default Login;
84
+
81
85
 
@@ -7,12 +7,11 @@ function Position() {
7
7
  const [requiredQualification, setRequiredQualification] = useState('');
8
8
  const [editingId, setEditingId] = useState(null);
9
9
  const [message, setMessage] = useState('');
10
-
11
- const headers = { Authorization: `Bearer ${localStorage.getItem('token')}` };
10
+ const currentUser = JSON.parse(localStorage.getItem('user') || 'null');
12
11
 
13
12
  const getPositions = async () => {
14
13
  try {
15
- const res = await axios.get('http://localhost:5006/api/positions/list', { headers });
14
+ const res = await axios.get('/api/positions/list', { withCredentials: true });
16
15
  setPositions(res.data);
17
16
  } catch (err) {
18
17
  console.error(err);
@@ -28,16 +27,16 @@ function Position() {
28
27
  try {
29
28
  if (editingId) {
30
29
  await axios.put(
31
- `http://localhost:5006/api/positions/${editingId}`,
30
+ `/api/positions/${editingId}`,
32
31
  { posName, requiredQualification },
33
- { headers }
32
+ { withCredentials: true }
34
33
  );
35
34
  setMessage('Position updated successfully');
36
35
  } else {
37
36
  await axios.post(
38
- 'http://localhost:5006/api/positions/add',
37
+ '/api/positions/add',
39
38
  { posName, requiredQualification },
40
- { headers }
39
+ { withCredentials: true }
41
40
  );
42
41
  setMessage('Position added successfully');
43
42
  }
@@ -61,7 +60,7 @@ function Position() {
61
60
  const handleDelete = async (id) => {
62
61
  if (!window.confirm('Delete this position?')) return;
63
62
  try {
64
- await axios.delete(`http://localhost:5006/api/positions/${id}`, { headers });
63
+ await axios.delete(`/api/positions/${id}`, { withCredentials: true });
65
64
  setMessage('Position deleted successfully');
66
65
  if (editingId === id) {
67
66
  setEditingId(null);
@@ -91,7 +90,8 @@ function Position() {
91
90
  <p className={isSuccess ? 'alert-success' : 'alert-error'}>{message}</p>
92
91
  )}
93
92
 
94
- <form onSubmit={handleSubmit} className="mb-6 box space-y-4">
93
+ {currentUser?.role === 'admin' ? (
94
+ <form onSubmit={handleSubmit} className="mb-6 box space-y-4">
95
95
  <h2 className="font-semibold">{editingId ? 'Edit Position' : 'Add Position'}</h2>
96
96
  <div>
97
97
  <label className="mb-1 block text-sm">Position Name</label>
@@ -125,7 +125,13 @@ function Position() {
125
125
  </button>
126
126
  )}
127
127
  </div>
128
- </form>
128
+ </form>
129
+ ) : (
130
+ <div className="mb-6 box">
131
+ <h2 className="font-semibold">Positions (read-only)</h2>
132
+ <p className="text-sm text-gray-600">You have read-only access. Contact an admin for changes.</p>
133
+ </div>
134
+ )}
129
135
 
130
136
  <div className="box">
131
137
  <h2 className="mb-3 font-semibold">Position List ({positions.length})</h2>
@@ -147,12 +153,16 @@ function Position() {
147
153
  <td>{pos.posName}</td>
148
154
  <td>{pos.requiredQualification}</td>
149
155
  <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
+ {currentUser?.role === 'admin' && (
157
+ <>
158
+ <button type="button" className="btn-secondary btn-small" onClick={() => handleEdit(pos)}>
159
+ Edit
160
+ </button>
161
+ <button type="button" className="btn-danger btn-small" onClick={() => handleDelete(pos._id)}>
162
+ Delete
163
+ </button>
164
+ </>
165
+ )}
156
166
  </td>
157
167
  </tr>
158
168
  ))}
@@ -167,3 +177,4 @@ function Position() {
167
177
 
168
178
  export default Position;
169
179
 
180
+
@@ -18,12 +18,15 @@ function Register() {
18
18
  setLoading(true);
19
19
 
20
20
  try {
21
- const response = await axios.post('http://localhost:5006/api/auth/register', {
22
- name,
23
- email,
24
- password,
25
- });
26
- localStorage.setItem('token', response.data.token);
21
+ const response = await axios.post(
22
+ '/api/auth/register',
23
+ {
24
+ name,
25
+ email,
26
+ password,
27
+ },
28
+ { withCredentials: true }
29
+ );
27
30
  localStorage.setItem('user', JSON.stringify(response.data.user));
28
31
  setMessage('Account created successfully');
29
32
  navigate('/dashboard');
@@ -99,4 +102,5 @@ function Register() {
99
102
  }
100
103
 
101
104
  export default Register;
105
+
102
106
 
@@ -6,11 +6,9 @@ function Reports() {
6
6
  const [loading, setLoading] = useState(true);
7
7
  const [error, setError] = useState('');
8
8
 
9
- const headers = { Authorization: `Bearer ${localStorage.getItem('token')}` };
10
-
11
9
  useEffect(() => {
12
10
  axios
13
- .get('http://localhost:5006/api/reports/on-leave-by-department', { headers })
11
+ .get('/api/reports/on-leave-by-department', { withCredentials: true })
14
12
  .then((res) => setReport(res.data))
15
13
  .catch((err) => setError(err.response?.data?.message || 'Failed to load report'))
16
14
  .finally(() => setLoading(false));
@@ -91,4 +89,5 @@ function Reports() {
91
89
  }
92
90
 
93
91
  export default Reports;
92
+
94
93
 
@@ -0,0 +1,18 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import tailwindcss from '@tailwindcss/vite'
4
+ // https://vite.dev/config/
5
+ export default defineConfig({
6
+ plugins: [react(), tailwindcss()],
7
+ server: {
8
+ proxy: {
9
+ '/api': {
10
+ target: 'http://localhost:5006',
11
+ changeOrigin: true,
12
+ secure: false,
13
+ cookieDomainRewrite: 'localhost',
14
+ cookiePathRewrite: '/',
15
+ },
16
+ },
17
+ },
18
+ })
@@ -1,34 +0,0 @@
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
-
@@ -1,27 +0,0 @@
1
-
2
- const jwt = require('jsonwebtoken');
3
- const User = require('../models/User');
4
- const JWT_SECRET = 'change_this_to_a_long_random_secret';
5
-
6
- exports.protect = async (req, res, next) => {
7
- try {
8
- const header = req.headers.authorization;
9
-
10
- if (!header || !header.startsWith('Bearer ')) {
11
- return res.status(401).json({ message: 'Not authorized' });
12
- }
13
-
14
- const token = header.split(' ')[1];
15
- const decoded = jwt.verify(token, JWT_SECRET);
16
- const user = await User.findById(decoded.userId).select('-password');
17
-
18
- if (!user) {
19
- return res.status(401).json({ message: 'Not authorized' });
20
- }
21
-
22
- req.user = user;
23
- next();
24
- } catch (err) {
25
- return res.status(401).json({ message: 'Not authorized' });
26
- }
27
- };