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
package/package.json
CHANGED
|
@@ -25,7 +25,7 @@ app.use('/api/sales', salesRoutes);
|
|
|
25
25
|
app.use('/api/warehouse', warehouseRoutes);
|
|
26
26
|
app.use('/api/reports', reportRoutes);
|
|
27
27
|
|
|
28
|
-
const PORT =
|
|
28
|
+
const PORT = 1000;
|
|
29
29
|
app.listen(PORT, () => {
|
|
30
30
|
console.log(`Server running on http://localhost:${PORT}`);
|
|
31
31
|
});
|
|
@@ -23,7 +23,7 @@ function AppLayout({ title, subtitle, children }) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
axios
|
|
26
|
-
.get('http://localhost:
|
|
26
|
+
.get('http://localhost:1000/api/auth/me', {
|
|
27
27
|
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },
|
|
28
28
|
})
|
|
29
29
|
.then((response) => {
|
|
@@ -17,7 +17,7 @@ function Login() {
|
|
|
17
17
|
setLoading(true);
|
|
18
18
|
|
|
19
19
|
try {
|
|
20
|
-
const response = await axios.post('http://localhost:
|
|
20
|
+
const response = await axios.post('http://localhost:1000/api/auth/login', { email, password });
|
|
21
21
|
localStorage.setItem('token', response.data.token);
|
|
22
22
|
localStorage.setItem('user', JSON.stringify(response.data.user));
|
|
23
23
|
setMessage('Login successful');
|
|
@@ -1,8 +1,11 @@
|
|
|
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/product';
|
|
6
|
+
|
|
5
7
|
function Product() {
|
|
8
|
+
const [products, setProducts] = useState([]);
|
|
6
9
|
const [productCode, setProductCode] = useState('');
|
|
7
10
|
const [productName, setProductName] = useState('');
|
|
8
11
|
const [category, setCategory] = useState('');
|
|
@@ -11,6 +14,8 @@ function Product() {
|
|
|
11
14
|
const [supplierName, setSupplierName] = useState('');
|
|
12
15
|
const [dateReceived, setDateReceived] = useState('');
|
|
13
16
|
const [message, setMessage] = useState('');
|
|
17
|
+
const [error, setError] = useState('');
|
|
18
|
+
const [editingId, setEditingId] = useState(null);
|
|
14
19
|
|
|
15
20
|
const headers = {
|
|
16
21
|
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
|
@@ -24,96 +29,224 @@ function Product() {
|
|
|
24
29
|
setQuantityInStock('');
|
|
25
30
|
setSupplierName('');
|
|
26
31
|
setDateReceived('');
|
|
32
|
+
setEditingId(null);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const fetchProducts = async () => {
|
|
36
|
+
try {
|
|
37
|
+
const response = await axios.get(`${API_BASE}/getproduct`, { headers });
|
|
38
|
+
setProducts(response.data);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
setError('Failed to load products');
|
|
41
|
+
console.error(err);
|
|
42
|
+
}
|
|
27
43
|
};
|
|
28
44
|
|
|
29
45
|
const handleSubmit = async (e) => {
|
|
30
46
|
e.preventDefault();
|
|
47
|
+
setError('');
|
|
48
|
+
setMessage('');
|
|
49
|
+
|
|
31
50
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
if (editingId) {
|
|
52
|
+
await axios.put(
|
|
53
|
+
`${API_BASE}/updateproduct/${editingId}`,
|
|
54
|
+
{
|
|
55
|
+
productCode,
|
|
56
|
+
productName,
|
|
57
|
+
category,
|
|
58
|
+
unitPrice,
|
|
59
|
+
quantityInStock,
|
|
60
|
+
supplierName,
|
|
61
|
+
dateReceived,
|
|
62
|
+
},
|
|
63
|
+
{ headers }
|
|
64
|
+
);
|
|
65
|
+
setMessage('Product updated successfully');
|
|
66
|
+
} else {
|
|
67
|
+
await axios.post(
|
|
68
|
+
`${API_BASE}/addproduct`,
|
|
69
|
+
{
|
|
70
|
+
productCode,
|
|
71
|
+
productName,
|
|
72
|
+
category,
|
|
73
|
+
unitPrice,
|
|
74
|
+
quantityInStock,
|
|
75
|
+
supplierName,
|
|
76
|
+
dateReceived,
|
|
77
|
+
},
|
|
78
|
+
{ headers }
|
|
79
|
+
);
|
|
80
|
+
setMessage('Product added successfully');
|
|
81
|
+
}
|
|
46
82
|
clearForm();
|
|
83
|
+
fetchProducts();
|
|
84
|
+
} catch (err) {
|
|
85
|
+
setError(err.response?.data?.message || 'Failed to save product');
|
|
86
|
+
console.error(err);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const handleEdit = (product) => {
|
|
91
|
+
setEditingId(product._id);
|
|
92
|
+
setProductCode(product.productCode || '');
|
|
93
|
+
setProductName(product.productName || '');
|
|
94
|
+
setCategory(product.category || '');
|
|
95
|
+
setUnitPrice(product.unitPrice || '');
|
|
96
|
+
setQuantityInStock(product.quantityInStock || '');
|
|
97
|
+
setSupplierName(product.supplierName || '');
|
|
98
|
+
setDateReceived(product.dateReceived ? product.dateReceived.split('T')[0] : '');
|
|
99
|
+
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const handleDelete = async (id) => {
|
|
103
|
+
try {
|
|
104
|
+
await axios.delete(`${API_BASE}/deleteproduct/${id}`, { headers });
|
|
105
|
+
setMessage('Product deleted successfully');
|
|
106
|
+
fetchProducts();
|
|
47
107
|
} catch (err) {
|
|
48
|
-
|
|
108
|
+
setError('Failed to delete product');
|
|
109
|
+
console.error(err);
|
|
49
110
|
}
|
|
50
111
|
};
|
|
51
112
|
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
fetchProducts();
|
|
115
|
+
}, []);
|
|
116
|
+
|
|
52
117
|
return (
|
|
53
118
|
<AppLayout title="Product management" subtitle="Inventory">
|
|
54
|
-
<div className="mx-auto max-w-
|
|
55
|
-
|
|
119
|
+
<div className="mx-auto max-w-6xl space-y-6">
|
|
120
|
+
<div className="card space-y-4 p-5">
|
|
121
|
+
{message && <p className="alert-success">{message}</p>}
|
|
122
|
+
{error && <p className="alert-error">{error}</p>}
|
|
123
|
+
|
|
124
|
+
<form onSubmit={handleSubmit} className="space-y-3">
|
|
125
|
+
<h2 className="text-lg font-semibold text-slate-800">
|
|
126
|
+
{editingId ? 'Edit product' : 'Add new product'}
|
|
127
|
+
</h2>
|
|
128
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
129
|
+
<input
|
|
130
|
+
type="text"
|
|
131
|
+
placeholder="Product code"
|
|
132
|
+
value={productCode}
|
|
133
|
+
onChange={(e) => setProductCode(e.target.value)}
|
|
134
|
+
className="input-field"
|
|
135
|
+
required
|
|
136
|
+
/>
|
|
137
|
+
<input
|
|
138
|
+
type="text"
|
|
139
|
+
placeholder="Product name"
|
|
140
|
+
value={productName}
|
|
141
|
+
onChange={(e) => setProductName(e.target.value)}
|
|
142
|
+
className="input-field"
|
|
143
|
+
required
|
|
144
|
+
/>
|
|
145
|
+
<input
|
|
146
|
+
type="text"
|
|
147
|
+
placeholder="Category"
|
|
148
|
+
value={category}
|
|
149
|
+
onChange={(e) => setCategory(e.target.value)}
|
|
150
|
+
className="input-field"
|
|
151
|
+
required
|
|
152
|
+
/>
|
|
153
|
+
<input
|
|
154
|
+
type="number"
|
|
155
|
+
placeholder="Unit price"
|
|
156
|
+
value={unitPrice}
|
|
157
|
+
onChange={(e) => setUnitPrice(e.target.value)}
|
|
158
|
+
className="input-field"
|
|
159
|
+
required
|
|
160
|
+
/>
|
|
161
|
+
<input
|
|
162
|
+
type="number"
|
|
163
|
+
placeholder="Quantity in stock"
|
|
164
|
+
value={quantityInStock}
|
|
165
|
+
onChange={(e) => setQuantityInStock(e.target.value)}
|
|
166
|
+
className="input-field"
|
|
167
|
+
required
|
|
168
|
+
/>
|
|
169
|
+
<input
|
|
170
|
+
type="text"
|
|
171
|
+
placeholder="Supplier name"
|
|
172
|
+
value={supplierName}
|
|
173
|
+
onChange={(e) => setSupplierName(e.target.value)}
|
|
174
|
+
className="input-field"
|
|
175
|
+
required
|
|
176
|
+
/>
|
|
177
|
+
<input
|
|
178
|
+
type="date"
|
|
179
|
+
value={dateReceived}
|
|
180
|
+
onChange={(e) => setDateReceived(e.target.value)}
|
|
181
|
+
className="input-field"
|
|
182
|
+
required
|
|
183
|
+
/>
|
|
184
|
+
</div>
|
|
185
|
+
<div className="flex flex-wrap gap-3">
|
|
186
|
+
<button type="submit" className="btn-primary">
|
|
187
|
+
{editingId ? 'Save changes' : 'Add product'}
|
|
188
|
+
</button>
|
|
189
|
+
{editingId && (
|
|
190
|
+
<button type="button" onClick={clearForm} className="btn-secondary">
|
|
191
|
+
Cancel
|
|
192
|
+
</button>
|
|
193
|
+
)}
|
|
194
|
+
</div>
|
|
195
|
+
</form>
|
|
196
|
+
</div>
|
|
56
197
|
|
|
57
|
-
<
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
onChange={(e) => setDateReceived(e.target.value)}
|
|
110
|
-
className="input-field"
|
|
111
|
-
required
|
|
112
|
-
/>
|
|
113
|
-
<button type="submit" className="btn-primary">
|
|
114
|
-
Add product
|
|
115
|
-
</button>
|
|
116
|
-
</form>
|
|
198
|
+
<div className="card p-5">
|
|
199
|
+
<h2 className="text-lg font-semibold text-slate-800">Products</h2>
|
|
200
|
+
{products.length === 0 ? (
|
|
201
|
+
<p className="mt-4 text-sm text-slate-500">No products available yet.</p>
|
|
202
|
+
) : (
|
|
203
|
+
<div className="mt-4 overflow-x-auto">
|
|
204
|
+
<table className="min-w-full divide-y divide-slate-200 text-sm">
|
|
205
|
+
<thead>
|
|
206
|
+
<tr className="bg-slate-50 text-left text-slate-600">
|
|
207
|
+
<th className="px-4 py-3">Code</th>
|
|
208
|
+
<th className="px-4 py-3">Name</th>
|
|
209
|
+
<th className="px-4 py-3">Category</th>
|
|
210
|
+
<th className="px-4 py-3">Price</th>
|
|
211
|
+
<th className="px-4 py-3">Stock</th>
|
|
212
|
+
<th className="px-4 py-3">Supplier</th>
|
|
213
|
+
<th className="px-4 py-3">Received</th>
|
|
214
|
+
<th className="px-4 py-3">Actions</th>
|
|
215
|
+
</tr>
|
|
216
|
+
</thead>
|
|
217
|
+
<tbody className="divide-y divide-slate-200">
|
|
218
|
+
{products.map((product) => (
|
|
219
|
+
<tr key={product._id} className="hover:bg-slate-50">
|
|
220
|
+
<td className="px-4 py-3">{product.productCode}</td>
|
|
221
|
+
<td className="px-4 py-3">{product.productName}</td>
|
|
222
|
+
<td className="px-4 py-3">{product.category}</td>
|
|
223
|
+
<td className="px-4 py-3">{product.unitPrice}</td>
|
|
224
|
+
<td className="px-4 py-3">{product.quantityInStock}</td>
|
|
225
|
+
<td className="px-4 py-3">{product.supplierName}</td>
|
|
226
|
+
<td className="px-4 py-3">{product.dateReceived?.split('T')[0] || '-'}</td>
|
|
227
|
+
<td className="px-4 py-3 space-x-2">
|
|
228
|
+
<button
|
|
229
|
+
type="button"
|
|
230
|
+
onClick={() => handleEdit(product)}
|
|
231
|
+
className="btn-secondary"
|
|
232
|
+
>
|
|
233
|
+
Edit
|
|
234
|
+
</button>
|
|
235
|
+
<button
|
|
236
|
+
type="button"
|
|
237
|
+
onClick={() => handleDelete(product._id)}
|
|
238
|
+
className="btn-danger"
|
|
239
|
+
>
|
|
240
|
+
Delete
|
|
241
|
+
</button>
|
|
242
|
+
</td>
|
|
243
|
+
</tr>
|
|
244
|
+
))}
|
|
245
|
+
</tbody>
|
|
246
|
+
</table>
|
|
247
|
+
</div>
|
|
248
|
+
)}
|
|
249
|
+
</div>
|
|
117
250
|
</div>
|
|
118
251
|
</AppLayout>
|
|
119
252
|
);
|
|
@@ -8,7 +8,7 @@ function Profile() {
|
|
|
8
8
|
|
|
9
9
|
const handleRetrieve = async () => {
|
|
10
10
|
try {
|
|
11
|
-
const response = await axios.get('http://localhost:
|
|
11
|
+
const response = await axios.get('http://localhost:1000/api/auth/me', {
|
|
12
12
|
headers: {
|
|
13
13
|
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
|
14
14
|
},
|
|
@@ -12,7 +12,7 @@ function Reports() {
|
|
|
12
12
|
|
|
13
13
|
const fetchReports = async () => {
|
|
14
14
|
try {
|
|
15
|
-
const res = await axios.get('http://localhost:
|
|
15
|
+
const res = await axios.get('http://localhost:1000/api/reports/summary', { headers });
|
|
16
16
|
setReports(res.data);
|
|
17
17
|
} catch (err) {
|
|
18
18
|
setError('Could not load reports');
|
|
@@ -29,7 +29,7 @@ function Sales() {
|
|
|
29
29
|
|
|
30
30
|
const fetchProducts = async () => {
|
|
31
31
|
try {
|
|
32
|
-
const res = await axios.get('http://localhost:
|
|
32
|
+
const res = await axios.get('http://localhost:1000/api/product/getproduct', { headers });
|
|
33
33
|
setProducts(res.data);
|
|
34
34
|
} catch (error) {
|
|
35
35
|
console.log(error);
|
|
@@ -38,7 +38,7 @@ function Sales() {
|
|
|
38
38
|
|
|
39
39
|
const fetchWarehouses = async () => {
|
|
40
40
|
try {
|
|
41
|
-
const res = await axios.get('http://localhost:
|
|
41
|
+
const res = await axios.get('http://localhost:1000/api/warehouse/getwarehouse', { headers });
|
|
42
42
|
setWarehouses(res.data);
|
|
43
43
|
} catch (error) {
|
|
44
44
|
console.log(error);
|
|
@@ -47,7 +47,7 @@ function Sales() {
|
|
|
47
47
|
|
|
48
48
|
const handleGet = async () => {
|
|
49
49
|
try {
|
|
50
|
-
const res = await axios.get('http://localhost:
|
|
50
|
+
const res = await axios.get('http://localhost:1000/api/sales/getsales', { headers });
|
|
51
51
|
setTransactions(res.data);
|
|
52
52
|
} catch (error) {
|
|
53
53
|
console.log(error);
|
|
@@ -65,14 +65,14 @@ function Sales() {
|
|
|
65
65
|
try {
|
|
66
66
|
if (editId) {
|
|
67
67
|
await axios.put(
|
|
68
|
-
`http://localhost:
|
|
68
|
+
`http://localhost:1000/api/sales/updatesales/${editId}`,
|
|
69
69
|
{ transactionDate, quantityMoved, transactionType, productId, warehouseId },
|
|
70
70
|
{ headers }
|
|
71
71
|
);
|
|
72
72
|
setMessage('Transaction updated successfully');
|
|
73
73
|
} else {
|
|
74
74
|
await axios.post(
|
|
75
|
-
'http://localhost:
|
|
75
|
+
'http://localhost:1000/api/sales/addsales',
|
|
76
76
|
{ transactionDate, quantityMoved, transactionType, productId, warehouseId },
|
|
77
77
|
{ headers }
|
|
78
78
|
);
|
|
@@ -87,7 +87,7 @@ function Sales() {
|
|
|
87
87
|
|
|
88
88
|
const handledelete = async (id) => {
|
|
89
89
|
try {
|
|
90
|
-
await axios.delete(`http://localhost:
|
|
90
|
+
await axios.delete(`http://localhost:1000/api/sales/deletesales/${id}`, { headers });
|
|
91
91
|
handleGet();
|
|
92
92
|
} catch (error) {
|
|
93
93
|
console.log(error);
|