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.
Files changed (88) hide show
  1. package/bin/cli.js +82 -0
  2. package/package.json +25 -0
  3. package/templates/HRMS_Mongodb/README.md +331 -0
  4. package/templates/HRMS_Mongodb/backend/.env.example +6 -0
  5. package/templates/HRMS_Mongodb/backend/package-lock.json +1646 -0
  6. package/templates/HRMS_Mongodb/backend/package.json +26 -0
  7. package/templates/HRMS_Mongodb/backend/src/config/db.js +9 -0
  8. package/templates/HRMS_Mongodb/backend/src/controllers/authController.js +187 -0
  9. package/templates/HRMS_Mongodb/backend/src/controllers/departmentController.js +70 -0
  10. package/templates/HRMS_Mongodb/backend/src/controllers/employeeController.js +178 -0
  11. package/templates/HRMS_Mongodb/backend/src/controllers/positionController.js +66 -0
  12. package/templates/HRMS_Mongodb/backend/src/middleware/auth.js +57 -0
  13. package/templates/HRMS_Mongodb/backend/src/middleware/errorHandler.js +32 -0
  14. package/templates/HRMS_Mongodb/backend/src/middleware/restrictToAdmin.js +5 -0
  15. package/templates/HRMS_Mongodb/backend/src/middleware/validate.js +13 -0
  16. package/templates/HRMS_Mongodb/backend/src/models/Department.js +19 -0
  17. package/templates/HRMS_Mongodb/backend/src/models/Employee.js +81 -0
  18. package/templates/HRMS_Mongodb/backend/src/models/Position.js +19 -0
  19. package/templates/HRMS_Mongodb/backend/src/models/User.js +40 -0
  20. package/templates/HRMS_Mongodb/backend/src/routes/authRoutes.js +27 -0
  21. package/templates/HRMS_Mongodb/backend/src/routes/departmentRoutes.js +33 -0
  22. package/templates/HRMS_Mongodb/backend/src/routes/employeeRoutes.js +39 -0
  23. package/templates/HRMS_Mongodb/backend/src/routes/positionRoutes.js +32 -0
  24. package/templates/HRMS_Mongodb/backend/src/server.js +74 -0
  25. package/templates/HRMS_Mongodb/backend/src/utils/roles.js +5 -0
  26. package/templates/HRMS_Mongodb/backend/src/utils/seed.js +78 -0
  27. package/templates/HRMS_Mongodb/backend/src/validators/authValidator.js +61 -0
  28. package/templates/HRMS_Mongodb/backend/src/validators/departmentValidator.js +21 -0
  29. package/templates/HRMS_Mongodb/backend/src/validators/employeeValidator.js +27 -0
  30. package/templates/HRMS_Mongodb/backend/src/validators/positionValidator.js +26 -0
  31. package/templates/HRMS_Mongodb/frontend/index.html +19 -0
  32. package/templates/HRMS_Mongodb/frontend/package-lock.json +2812 -0
  33. package/templates/HRMS_Mongodb/frontend/package.json +25 -0
  34. package/templates/HRMS_Mongodb/frontend/public/favicon.svg +4 -0
  35. package/templates/HRMS_Mongodb/frontend/src/App.jsx +50 -0
  36. package/templates/HRMS_Mongodb/frontend/src/api/axios.js +54 -0
  37. package/templates/HRMS_Mongodb/frontend/src/components/ProtectedRoute.jsx +26 -0
  38. package/templates/HRMS_Mongodb/frontend/src/components/layout/DashboardLayout.jsx +16 -0
  39. package/templates/HRMS_Mongodb/frontend/src/components/layout/Sidebar.jsx +108 -0
  40. package/templates/HRMS_Mongodb/frontend/src/components/ui/Button.jsx +33 -0
  41. package/templates/HRMS_Mongodb/frontend/src/components/ui/Input.jsx +20 -0
  42. package/templates/HRMS_Mongodb/frontend/src/components/ui/Modal.jsx +48 -0
  43. package/templates/HRMS_Mongodb/frontend/src/components/ui/Select.jsx +27 -0
  44. package/templates/HRMS_Mongodb/frontend/src/context/AuthContext.jsx +97 -0
  45. package/templates/HRMS_Mongodb/frontend/src/index.css +34 -0
  46. package/templates/HRMS_Mongodb/frontend/src/main.jsx +16 -0
  47. package/templates/HRMS_Mongodb/frontend/src/pages/Dashboard.jsx +78 -0
  48. package/templates/HRMS_Mongodb/frontend/src/pages/Departments.jsx +144 -0
  49. package/templates/HRMS_Mongodb/frontend/src/pages/Employees.jsx +297 -0
  50. package/templates/HRMS_Mongodb/frontend/src/pages/LeaveReport.jsx +113 -0
  51. package/templates/HRMS_Mongodb/frontend/src/pages/Login.jsx +92 -0
  52. package/templates/HRMS_Mongodb/frontend/src/pages/Positions.jsx +157 -0
  53. package/templates/HRMS_Mongodb/frontend/src/pages/Register.jsx +93 -0
  54. package/templates/HRMS_Mongodb/frontend/src/pages/ResetPassword.jsx +135 -0
  55. package/templates/HRMS_Mongodb/frontend/src/utils/roles.js +1 -0
  56. package/templates/HRMS_Mongodb/frontend/src/utils/session.js +5 -0
  57. package/templates/HRMS_Mongodb/frontend/src/utils/validation.js +66 -0
  58. package/templates/HRMS_Mongodb/frontend/vite.config.js +16 -0
  59. package/templates/HRMS_Mysql/backend/db.js +13 -0
  60. package/templates/HRMS_Mysql/backend/package-lock.json +1614 -0
  61. package/templates/HRMS_Mysql/backend/package.json +21 -0
  62. package/templates/HRMS_Mysql/backend/server.js +421 -0
  63. package/templates/HRMS_Mysql/frontend/dist/assets/index-CtLtQf3_.js +75 -0
  64. package/templates/HRMS_Mysql/frontend/dist/assets/index-Dq1AXlEY.css +1 -0
  65. package/templates/HRMS_Mysql/frontend/dist/index.html +14 -0
  66. package/templates/HRMS_Mysql/frontend/dist/vite.svg +1 -0
  67. package/templates/HRMS_Mysql/frontend/index.html +13 -0
  68. package/templates/HRMS_Mysql/frontend/package-lock.json +2978 -0
  69. package/templates/HRMS_Mysql/frontend/package.json +25 -0
  70. package/templates/HRMS_Mysql/frontend/postcss.config.js +6 -0
  71. package/templates/HRMS_Mysql/frontend/public/vite.svg +1 -0
  72. package/templates/HRMS_Mysql/frontend/src/App.jsx +55 -0
  73. package/templates/HRMS_Mysql/frontend/src/api.js +11 -0
  74. package/templates/HRMS_Mysql/frontend/src/components/Layout.jsx +59 -0
  75. package/templates/HRMS_Mysql/frontend/src/index.css +7 -0
  76. package/templates/HRMS_Mysql/frontend/src/main.jsx +13 -0
  77. package/templates/HRMS_Mysql/frontend/src/pages/Dashboard.jsx +45 -0
  78. package/templates/HRMS_Mysql/frontend/src/pages/Departments.jsx +108 -0
  79. package/templates/HRMS_Mysql/frontend/src/pages/EmployeeStatusReport.jsx +72 -0
  80. package/templates/HRMS_Mysql/frontend/src/pages/Employees.jsx +252 -0
  81. package/templates/HRMS_Mysql/frontend/src/pages/ForgotPassword.jsx +66 -0
  82. package/templates/HRMS_Mysql/frontend/src/pages/Login.jsx +79 -0
  83. package/templates/HRMS_Mysql/frontend/src/pages/Positions.jsx +109 -0
  84. package/templates/HRMS_Mysql/frontend/src/pages/Register.jsx +95 -0
  85. package/templates/HRMS_Mysql/frontend/src/pages/Users.jsx +133 -0
  86. package/templates/HRMS_Mysql/frontend/tailwind.config.js +26 -0
  87. package/templates/HRMS_Mysql/frontend/vite.config.js +15 -0
  88. package/templates/HRMS_Mysql/hrms_schema.sql +57 -0
@@ -0,0 +1,66 @@
1
+ import React, { useState } from 'react'
2
+ import { useNavigate } from 'react-router-dom'
3
+ import api from '../api'
4
+
5
+ export default function ForgotPassword() {
6
+ const [email, setEmail] = useState('');
7
+ const [message, setMessage] = useState('');
8
+ const [error, setError] = useState('');
9
+ const navigate = useNavigate();
10
+
11
+ const handleSubmit = async (e) => {
12
+ e.preventDefault();
13
+ setError('');
14
+ setMessage('');
15
+ try {
16
+ const res = await api.post('/forgot-password', { email });
17
+ setMessage(`Temporary password sent: ${res.data.tempPassword}`);
18
+ } catch (err) {
19
+ setError(err.response?.data?.error || 'Email not found');
20
+ }
21
+ };
22
+
23
+ return (
24
+ <div className="min-h-screen bg-primary-50 flex items-center justify-center">
25
+ <div className="bg-white rounded-lg shadow-lg p-8 w-full max-w-md">
26
+ <div className="text-center mb-6">
27
+ <h1 className="text-2xl font-bold text-primary-700">Reset Password</h1>
28
+ <p className="text-primary-500">Enter your registered email</p>
29
+ </div>
30
+ <form onSubmit={handleSubmit} className="space-y-4">
31
+ {error && (
32
+ <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-2 rounded text-sm">
33
+ {error}
34
+ </div>
35
+ )}
36
+ {message && (
37
+ <div className="bg-green-100 border border-green-400 text-green-700 px-4 py-2 rounded text-sm">
38
+ {message}
39
+ </div>
40
+ )}
41
+ <div>
42
+ <label className="block text-sm font-medium text-gray-700 mb-1">Email</label>
43
+ <input
44
+ type="email"
45
+ value={email}
46
+ onChange={(e) => setEmail(e.target.value)}
47
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent"
48
+ required
49
+ />
50
+ </div>
51
+ <button
52
+ type="submit"
53
+ className="w-full bg-primary-600 text-white py-2 rounded hover:bg-primary-500 transition-colors"
54
+ >
55
+ Send Temporary Password
56
+ </button>
57
+ </form>
58
+ <div className="mt-4 text-center">
59
+ <a href="/login" className="text-sm text-primary-600 hover:underline">
60
+ Back to Login
61
+ </a>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ );
66
+ }
@@ -0,0 +1,79 @@
1
+ import React, { useState } from 'react'
2
+ import { useNavigate, Link } from 'react-router-dom'
3
+ import api from '../api'
4
+
5
+ export default function Login() {
6
+ const [username, setUsername] = useState('');
7
+ const [password, setPassword] = useState('');
8
+ const [error, setError] = useState('');
9
+ const navigate = useNavigate();
10
+
11
+ const handleSubmit = async (e) => {
12
+ e.preventDefault();
13
+ setError('');
14
+ try {
15
+ const res = await api.post('/login', { username, password });
16
+ if (res.data.success) {
17
+ navigate('/');
18
+ }
19
+ } catch (err) {
20
+ setError(err.response?.data?.error || 'Login failed');
21
+ }
22
+ };
23
+
24
+ return (
25
+ <div className="min-h-screen bg-primary-50 flex items-center justify-center">
26
+ <div className="bg-white rounded-lg shadow-lg p-8 w-full max-w-md">
27
+ <div className="text-center mb-6">
28
+ <h1 className="text-2xl font-bold text-primary-700">HRMS</h1>
29
+ <p className="text-primary-500">DAB Enterprise LTD</p>
30
+ </div>
31
+ <form onSubmit={handleSubmit} className="space-y-4">
32
+ {error && (
33
+ <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-2 rounded text-sm">
34
+ {error}
35
+ </div>
36
+ )}
37
+ <div>
38
+ <label className="block text-sm font-medium text-gray-700 mb-1">Username</label>
39
+ <input
40
+ type="text"
41
+ value={username}
42
+ onChange={(e) => setUsername(e.target.value)}
43
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent"
44
+ required
45
+ />
46
+ </div>
47
+ <div>
48
+ <label className="block text-sm font-medium text-gray-700 mb-1">Password</label>
49
+ <input
50
+ type="password"
51
+ value={password}
52
+ onChange={(e) => setPassword(e.target.value)}
53
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent"
54
+ required
55
+ />
56
+ </div>
57
+ <button
58
+ type="submit"
59
+ className="w-full bg-primary-600 text-white py-2 rounded hover:bg-primary-500 transition-colors"
60
+ >
61
+ Login
62
+ </button>
63
+ </form>
64
+ <div className="mt-4 text-center space-y-2">
65
+ <div>
66
+ <Link to="/forgot-password" className="text-sm text-primary-600 hover:underline">
67
+ Forgot Password?
68
+ </Link>
69
+ </div>
70
+ <div>
71
+ <Link to="/register" className="text-sm text-primary-600 hover:underline">
72
+ Don't have an account? Register
73
+ </Link>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ );
79
+ }
@@ -0,0 +1,109 @@
1
+ import React, { useState, useEffect } from 'react'
2
+ import api from '../api'
3
+
4
+ export default function Positions() {
5
+ const [positions, setPositions] = useState([]);
6
+ const [form, setForm] = useState({ PosName: '', RequiredQualification: '' });
7
+ const [editing, setEditing] = useState(null);
8
+ const [showForm, setShowForm] = useState(false);
9
+
10
+ useEffect(() => { load(); }, []);
11
+
12
+ const load = async () => {
13
+ const res = await api.get('/positions');
14
+ setPositions(res.data);
15
+ };
16
+
17
+ const handleSubmit = async (e) => {
18
+ e.preventDefault();
19
+ if (editing) {
20
+ await api.put(`/positions/${editing}`, form);
21
+ } else {
22
+ await api.post('/positions', form);
23
+ }
24
+ setForm({ PosName: '', RequiredQualification: '' });
25
+ setEditing(null);
26
+ setShowForm(false);
27
+ load();
28
+ };
29
+
30
+ const handleEdit = (pos) => {
31
+ setForm({ PosName: pos.PosName, RequiredQualification: pos.RequiredQualification });
32
+ setEditing(pos.PosID);
33
+ setShowForm(true);
34
+ };
35
+
36
+ const handleDelete = async (id) => {
37
+ if (confirm('Delete this position?')) {
38
+ await api.delete(`/positions/${id}`);
39
+ load();
40
+ }
41
+ };
42
+
43
+ return (
44
+ <div>
45
+ <div className="flex justify-between items-center mb-6">
46
+ <h1 className="text-2xl font-bold text-primary-800">Positions</h1>
47
+ <button onClick={() => { setShowForm(true); setEditing(null); setForm({ PosName: '', RequiredQualification: '' }); }}
48
+ className="bg-primary-600 text-white px-4 py-2 rounded hover:bg-primary-500 transition-colors">
49
+ Add Position
50
+ </button>
51
+ </div>
52
+
53
+ {showForm && (
54
+ <form onSubmit={handleSubmit} className="bg-white rounded-lg shadow p-4 mb-6">
55
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
56
+ <div>
57
+ <label className="block text-sm font-medium text-gray-700 mb-1">Position Name</label>
58
+ <input type="text" value={form.PosName}
59
+ onChange={(e) => setForm({ ...form, PosName: e.target.value })}
60
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500" required />
61
+ </div>
62
+ <div>
63
+ <label className="block text-sm font-medium text-gray-700 mb-1">Required Qualification</label>
64
+ <input type="text" value={form.RequiredQualification}
65
+ onChange={(e) => setForm({ ...form, RequiredQualification: e.target.value })}
66
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500" />
67
+ </div>
68
+ </div>
69
+ <div className="mt-4 space-x-2">
70
+ <button type="submit" className="bg-primary-600 text-white px-4 py-2 rounded hover:bg-primary-500">
71
+ {editing ? 'Update' : 'Save'}
72
+ </button>
73
+ <button type="button" onClick={() => setShowForm(false)}
74
+ className="bg-gray-400 text-white px-4 py-2 rounded hover:bg-gray-500">Cancel</button>
75
+ </div>
76
+ </form>
77
+ )}
78
+
79
+ <div className="bg-white rounded-lg shadow overflow-hidden">
80
+ <table className="w-full">
81
+ <thead className="bg-primary-600 text-white">
82
+ <tr>
83
+ <th className="px-4 py-3 text-left">ID</th>
84
+ <th className="px-4 py-3 text-left">Position Name</th>
85
+ <th className="px-4 py-3 text-left">Required Qualification</th>
86
+ <th className="px-4 py-3 text-right">Actions</th>
87
+ </tr>
88
+ </thead>
89
+ <tbody>
90
+ {positions.map(pos => (
91
+ <tr key={pos.PosID} className="border-t hover:bg-primary-50">
92
+ <td className="px-4 py-3">{pos.PosID}</td>
93
+ <td className="px-4 py-3">{pos.PosName}</td>
94
+ <td className="px-4 py-3">{pos.RequiredQualification || '-'}</td>
95
+ <td className="px-4 py-3 text-right space-x-2">
96
+ <button onClick={() => handleEdit(pos)} className="text-primary-600 hover:underline text-sm">Edit</button>
97
+ <button onClick={() => handleDelete(pos.PosID)} className="text-red-600 hover:underline text-sm">Delete</button>
98
+ </td>
99
+ </tr>
100
+ ))}
101
+ {positions.length === 0 && (
102
+ <tr><td colSpan="4" className="px-4 py-8 text-center text-gray-500">No positions found</td></tr>
103
+ )}
104
+ </tbody>
105
+ </table>
106
+ </div>
107
+ </div>
108
+ );
109
+ }
@@ -0,0 +1,95 @@
1
+ import React, { useState } from 'react'
2
+ import { useNavigate, Link } from 'react-router-dom'
3
+ import api from '../api'
4
+
5
+ export default function Register() {
6
+ const [form, setForm] = useState({ firstName: '', lastName: '', email: '', username: '', password: '' });
7
+ const [error, setError] = useState('');
8
+ const navigate = useNavigate();
9
+
10
+ const handleChange = (e) => setForm({ ...form, [e.target.name]: e.target.value });
11
+
12
+ const handleSubmit = async (e) => {
13
+ e.preventDefault();
14
+ setError('');
15
+ try {
16
+ const res = await api.post('/register', form);
17
+ if (res.data.success) {
18
+ navigate('/');
19
+ }
20
+ } catch (err) {
21
+ setError(err.response?.data?.error || 'Registration failed');
22
+ }
23
+ };
24
+
25
+ return (
26
+ <div className="min-h-screen bg-primary-50 flex items-center justify-center">
27
+ <div className="bg-white rounded-lg shadow-lg p-8 w-full max-w-md">
28
+ <div className="text-center mb-6">
29
+ <h1 className="text-2xl font-bold text-primary-700">HRMS</h1>
30
+ <p className="text-primary-500">Create Account</p>
31
+ </div>
32
+ <form onSubmit={handleSubmit} className="space-y-4">
33
+ {error && (
34
+ <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-2 rounded text-sm">
35
+ {error}
36
+ </div>
37
+ )}
38
+ <div className="grid grid-cols-2 gap-4">
39
+ <div>
40
+ <label className="block text-sm font-medium text-gray-700 mb-1">First Name</label>
41
+ <input
42
+ type="text" name="firstName" value={form.firstName} onChange={handleChange}
43
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent"
44
+ required
45
+ />
46
+ </div>
47
+ <div>
48
+ <label className="block text-sm font-medium text-gray-700 mb-1">Last Name</label>
49
+ <input
50
+ type="text" name="lastName" value={form.lastName} onChange={handleChange}
51
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent"
52
+ required
53
+ />
54
+ </div>
55
+ </div>
56
+ <div>
57
+ <label className="block text-sm font-medium text-gray-700 mb-1">Email</label>
58
+ <input
59
+ type="email" name="email" value={form.email} onChange={handleChange}
60
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent"
61
+ required
62
+ />
63
+ </div>
64
+ <div>
65
+ <label className="block text-sm font-medium text-gray-700 mb-1">Username</label>
66
+ <input
67
+ type="text" name="username" value={form.username} onChange={handleChange}
68
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent"
69
+ required
70
+ />
71
+ </div>
72
+ <div>
73
+ <label className="block text-sm font-medium text-gray-700 mb-1">Password</label>
74
+ <input
75
+ type="password" name="password" value={form.password} onChange={handleChange}
76
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent"
77
+ required
78
+ />
79
+ </div>
80
+ <button
81
+ type="submit"
82
+ className="w-full bg-primary-600 text-white py-2 rounded hover:bg-primary-500 transition-colors"
83
+ >
84
+ Register
85
+ </button>
86
+ </form>
87
+ <div className="mt-4 text-center">
88
+ <Link to="/login" className="text-sm text-primary-600 hover:underline">
89
+ Already have an account? Login
90
+ </Link>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ );
95
+ }
@@ -0,0 +1,133 @@
1
+ import React, { useState, useEffect } from 'react'
2
+ import api from '../api'
3
+
4
+ export default function Users() {
5
+ const [users, setUsers] = useState([]);
6
+ const [employees, setEmployees] = useState([]);
7
+ const [form, setForm] = useState({ EmpID: '', UserName: '', Password: '' });
8
+ const [editing, setEditing] = useState(null);
9
+ const [showForm, setShowForm] = useState(false);
10
+
11
+ useEffect(() => {
12
+ load();
13
+ api.get('/employees').then(r => setEmployees(r.data));
14
+ }, []);
15
+
16
+ const load = async () => {
17
+ const res = await api.get('/users');
18
+ setUsers(res.data);
19
+ };
20
+
21
+ const handleSubmit = async (e) => {
22
+ e.preventDefault();
23
+ if (editing) {
24
+ await api.put(`/users/${editing}`, { UserName: form.UserName, Password: form.Password || undefined });
25
+ } else {
26
+ await api.post('/users', form);
27
+ }
28
+ setForm({ EmpID: '', UserName: '', Password: '' });
29
+ setEditing(null);
30
+ setShowForm(false);
31
+ load();
32
+ };
33
+
34
+ const handleEdit = (user) => {
35
+ setForm({ EmpID: user.EmpID, UserName: user.UserName, Password: '' });
36
+ setEditing(user.UserID);
37
+ setShowForm(true);
38
+ };
39
+
40
+ const handleDelete = async (id) => {
41
+ if (confirm('Delete this user?')) {
42
+ await api.delete(`/users/${id}`);
43
+ load();
44
+ }
45
+ };
46
+
47
+ const availableEmployees = employees.filter(emp =>
48
+ !users.some(u => u.EmpID === emp.EmpID && u.UserID !== editing)
49
+ );
50
+
51
+ return (
52
+ <div>
53
+ <div className="flex justify-between items-center mb-6">
54
+ <h1 className="text-2xl font-bold text-primary-800">Users</h1>
55
+ <button onClick={() => { setShowForm(true); setEditing(null); setForm({ EmpID: '', UserName: '', Password: '' }); }}
56
+ className="bg-primary-600 text-white px-4 py-2 rounded hover:bg-primary-500 transition-colors">
57
+ Add User
58
+ </button>
59
+ </div>
60
+
61
+ {showForm && (
62
+ <form onSubmit={handleSubmit} className="bg-white rounded-lg shadow p-4 mb-6">
63
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
64
+ <div>
65
+ <label className="block text-sm font-medium text-gray-700 mb-1">Employee *</label>
66
+ <select value={form.EmpID}
67
+ onChange={(e) => setForm({ ...form, EmpID: e.target.value })}
68
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500" required>
69
+ <option value="">Select Employee</option>
70
+ {(editing ? employees : availableEmployees).map(emp => (
71
+ <option key={emp.EmpID} value={emp.EmpID}>{emp.EmpFirstName} {emp.EmpLastName} - {emp.EmpEmail}</option>
72
+ ))}
73
+ </select>
74
+ </div>
75
+ <div>
76
+ <label className="block text-sm font-medium text-gray-700 mb-1">Username *</label>
77
+ <input type="text" value={form.UserName}
78
+ onChange={(e) => setForm({ ...form, UserName: e.target.value })}
79
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500" required />
80
+ </div>
81
+ <div>
82
+ <label className="block text-sm font-medium text-gray-700 mb-1">
83
+ Password {editing ? '(leave blank to keep current)' : '*'}
84
+ </label>
85
+ <input type="password" value={form.Password}
86
+ onChange={(e) => setForm({ ...form, Password: e.target.value })}
87
+ className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-primary-500"
88
+ required={!editing} />
89
+ </div>
90
+ </div>
91
+ <div className="mt-4 space-x-2">
92
+ <button type="submit" className="bg-primary-600 text-white px-4 py-2 rounded hover:bg-primary-500">
93
+ {editing ? 'Update' : 'Save'}
94
+ </button>
95
+ <button type="button" onClick={() => setShowForm(false)}
96
+ className="bg-gray-400 text-white px-4 py-2 rounded hover:bg-gray-500">Cancel</button>
97
+ </div>
98
+ </form>
99
+ )}
100
+
101
+ <div className="bg-white rounded-lg shadow overflow-hidden">
102
+ <table className="w-full">
103
+ <thead className="bg-primary-600 text-white">
104
+ <tr>
105
+ <th className="px-4 py-3 text-left">ID</th>
106
+ <th className="px-4 py-3 text-left">Username</th>
107
+ <th className="px-4 py-3 text-left">Employee Name</th>
108
+ <th className="px-4 py-3 text-left">Email</th>
109
+ <th className="px-4 py-3 text-right">Actions</th>
110
+ </tr>
111
+ </thead>
112
+ <tbody>
113
+ {users.map(user => (
114
+ <tr key={user.UserID} className="border-t hover:bg-primary-50">
115
+ <td className="px-4 py-3">{user.UserID}</td>
116
+ <td className="px-4 py-3">{user.UserName}</td>
117
+ <td className="px-4 py-3">{user.EmpFirstName} {user.EmpLastName}</td>
118
+ <td className="px-4 py-3">{user.EmpEmail}</td>
119
+ <td className="px-4 py-3 text-right space-x-2">
120
+ <button onClick={() => handleEdit(user)} className="text-primary-600 hover:underline text-sm">Edit</button>
121
+ <button onClick={() => handleDelete(user.UserID)} className="text-red-600 hover:underline text-sm">Delete</button>
122
+ </td>
123
+ </tr>
124
+ ))}
125
+ {users.length === 0 && (
126
+ <tr><td colSpan="5" className="px-4 py-8 text-center text-gray-500">No users found</td></tr>
127
+ )}
128
+ </tbody>
129
+ </table>
130
+ </div>
131
+ </div>
132
+ );
133
+ }
@@ -0,0 +1,26 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: [
4
+ "./index.html",
5
+ "./src/**/*.{js,ts,jsx,tsx}",
6
+ ],
7
+ theme: {
8
+ extend: {
9
+ colors: {
10
+ primary: {
11
+ 50: '#fdf8f0',
12
+ 100: '#f9edd6',
13
+ 200: '#f2d9a8',
14
+ 300: '#e9c070',
15
+ 400: '#e0a645',
16
+ 500: '#d48f2a',
17
+ 600: '#b87322',
18
+ 700: '#96571d',
19
+ 800: '#7a4720',
20
+ 900: '#653c1d',
21
+ }
22
+ }
23
+ },
24
+ },
25
+ plugins: [],
26
+ }
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: {
7
+ port: 5173,
8
+ proxy: {
9
+ '/api': {
10
+ target: 'http://localhost:5000',
11
+ changeOrigin: true
12
+ }
13
+ }
14
+ }
15
+ })
@@ -0,0 +1,57 @@
1
+ -- HRMS Database Schema
2
+ -- Database: HRMS
3
+ -- Entity Relationship Diagram (ERD):
4
+ --
5
+ -- Department 1---* Employee *---1 Position
6
+ -- Employee 1---1 Users
7
+ --
8
+ -- Tables:
9
+ -- Department (DepartID INT PK, DepartName VARCHAR)
10
+ -- Position (PosID INT PK, PosName VARCHAR, RequiredQualification VARCHAR)
11
+ -- Employee (EmpID INT PK, EmpFirstName, EmpLastName, EmpGender, EmpDateOfBirth,
12
+ -- EmpEmail, EmpTelephone, EmpAddress, EmpHireDate, EmpStatus,
13
+ -- DepartID FK, PosID FK)
14
+ -- Users (UserID INT PK, EmpID FK UNIQUE, UserName, Password)
15
+
16
+ CREATE DATABASE IF NOT EXISTS HRMS;
17
+ USE HRMS;
18
+
19
+ -- Department table
20
+ CREATE TABLE IF NOT EXISTS Department (
21
+ DepartID INT AUTO_INCREMENT PRIMARY KEY,
22
+ DepartName VARCHAR(100) NOT NULL
23
+ ) ENGINE=InnoDB;
24
+
25
+ -- Position table
26
+ CREATE TABLE IF NOT EXISTS Positionemp(
27
+ PosID INT AUTO_INCREMENT PRIMARY KEY,
28
+ PosName VARCHAR(100) NOT NULL,
29
+ RequiredQualification VARCHAR(255)
30
+ ) ENGINE=InnoDB;
31
+
32
+ -- Employee table
33
+ CREATE TABLE IF NOT EXISTS Employee (
34
+ EmpID INT AUTO_INCREMENT PRIMARY KEY,
35
+ EmpFirstName VARCHAR(100) NOT NULL,
36
+ EmpLastName VARCHAR(100) NOT NULL,
37
+ EmpGender VARCHAR(10),
38
+ EmpDateOfBirth DATE,
39
+ EmpEmail VARCHAR(100) UNIQUE,
40
+ EmpTelephone VARCHAR(20),
41
+ EmpAddress TEXT,
42
+ EmpHireDate DATE,
43
+ EmpStatus ENUM('on leave','left','blacklisted','deceased','on mission') DEFAULT 'on leave',
44
+ DepartID INT,
45
+ PosID INT,
46
+ FOREIGN KEY (DepartID) REFERENCES Department(DepartID) ON DELETE SET NULL,
47
+ FOREIGN KEY (PosID) REFERENCES Positionemp(PosID) ON DELETE SET NULL
48
+ ) ENGINE=InnoDB;
49
+
50
+ -- Users table
51
+ CREATE TABLE IF NOT EXISTS Users (
52
+ UserID INT AUTO_INCREMENT PRIMARY KEY,
53
+ EmpID INT UNIQUE,
54
+ UserName VARCHAR(50) UNIQUE NOT NULL,
55
+ Password VARCHAR(255) NOT NULL,
56
+ FOREIGN KEY (EmpID) REFERENCES Employee(EmpID) ON DELETE CASCADE
57
+ ) ENGINE=InnoDB;