inventrack 3.0.0
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/README.md +25 -0
- package/api/index.js +13 -0
- package/backend/README.md +35 -0
- package/backend/data/db.json +1239 -0
- package/backend/package-lock.json +532 -0
- package/backend/package.json +8 -0
- package/frontend/README.md +22 -0
- package/frontend/assets/Icon.png +0 -0
- package/frontend/assets/IconSort.png +0 -0
- package/frontend/assets/activity-1.png +0 -0
- package/frontend/assets/activity-2.png +0 -0
- package/frontend/assets/activity-3.png +0 -0
- package/frontend/assets/activity-4.png +0 -0
- package/frontend/assets/card-icon-1.png +0 -0
- package/frontend/assets/card-icon-2.png +0 -0
- package/frontend/assets/card-icon-3.png +0 -0
- package/frontend/assets/card-icon-4.png +0 -0
- package/frontend/assets/login.png +0 -0
- package/frontend/assets/logo.png +0 -0
- package/frontend/categories.html +143 -0
- package/frontend/css/all.min.css +9 -0
- package/frontend/css/bootstrap.min.css +6 -0
- package/frontend/css/categories.css +359 -0
- package/frontend/css/dashboard.css +373 -0
- package/frontend/css/inventoryInsights.css +308 -0
- package/frontend/css/inventoryOverview.css +353 -0
- package/frontend/css/orders.css +632 -0
- package/frontend/css/products.css +364 -0
- package/frontend/css/signin.css +120 -0
- package/frontend/css/style.css +282 -0
- package/frontend/css/suppliers.css +136 -0
- package/frontend/dashboard.html +160 -0
- package/frontend/index.html +124 -0
- package/frontend/inventoryInsights.html +182 -0
- package/frontend/inventoryOverview.html +187 -0
- package/frontend/js/api.js +55 -0
- package/frontend/js/auth.js +70 -0
- package/frontend/js/bootstrap.bundle.min.js +7 -0
- package/frontend/js/categories.js +356 -0
- package/frontend/js/dashboard.js +341 -0
- package/frontend/js/inventoryInsights.js +396 -0
- package/frontend/js/inventoryOverview.js +503 -0
- package/frontend/js/orders.js +662 -0
- package/frontend/js/products.js +650 -0
- package/frontend/js/suppliers.js +535 -0
- package/frontend/js/utils.js +234 -0
- package/frontend/orders.html +216 -0
- package/frontend/products.html +152 -0
- package/frontend/suppliers.html +175 -0
- package/frontend/webfonts/fa-brands-400.woff2 +0 -0
- package/frontend/webfonts/fa-regular-400.woff2 +0 -0
- package/frontend/webfonts/fa-solid-900.woff2 +0 -0
- package/frontend/webfonts/fa-v4compatibility.woff2 +0 -0
- package/package.json +38 -0
- package/vercel.json +18 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
// selectors
|
|
2
|
+
let showDatainTable = document.querySelector("tbody");
|
|
3
|
+
let searchSuppliersByName = document.querySelector("#searchBySupplierName");
|
|
4
|
+
let formSelect = document.querySelector("#formSelect");
|
|
5
|
+
let exportData = document.getElementById("downloadData");
|
|
6
|
+
let addSupplier = document.querySelector("#addSupplier");
|
|
7
|
+
let modal = document.querySelector(".modal");
|
|
8
|
+
let supplierName = document.querySelector("#supplierName");
|
|
9
|
+
let contactPerson = document.querySelector("#contactPerson");
|
|
10
|
+
let supplierMail = document.querySelector("#supplierMail");
|
|
11
|
+
let supplierPhone = document.querySelector("#supplierPhone");
|
|
12
|
+
let selectStatus = document.querySelector("#select");
|
|
13
|
+
let physicalAddress = document.querySelector("textarea");
|
|
14
|
+
let saveSupplier = document.querySelector("#saveSupplier");
|
|
15
|
+
let targetID = null;
|
|
16
|
+
const paginationContainer = document.querySelector("#pagination");
|
|
17
|
+
let allSuppliersData = [];
|
|
18
|
+
let filteredSuppliersData = [];
|
|
19
|
+
let sortSupplierName = document.querySelector("#sortSupplierName");
|
|
20
|
+
|
|
21
|
+
// initial render
|
|
22
|
+
renderNavbar("Suppliers");
|
|
23
|
+
loadAndRenderSuppliers();
|
|
24
|
+
setupInputValidation();
|
|
25
|
+
renderFooter();
|
|
26
|
+
|
|
27
|
+
// pagination state
|
|
28
|
+
const state = {
|
|
29
|
+
page: 1,
|
|
30
|
+
limit: 5,
|
|
31
|
+
totalCount: 0,
|
|
32
|
+
sortOrder: null // null | "asc" | "desc"
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// -----------------------------
|
|
36
|
+
// Render only current table page
|
|
37
|
+
// -----------------------------
|
|
38
|
+
function renderTable() {
|
|
39
|
+
const start = (state.page - 1) * state.limit;
|
|
40
|
+
const end = start + state.limit;
|
|
41
|
+
const paginatedData = filteredSuppliersData.slice(start, end);
|
|
42
|
+
|
|
43
|
+
showDatainTable.innerHTML = "";
|
|
44
|
+
|
|
45
|
+
if (paginatedData.length === 0) {
|
|
46
|
+
showDatainTable.innerHTML = `
|
|
47
|
+
<tr>
|
|
48
|
+
<td colspan="7" style="text-align:center;">No Data Matched!!</td>
|
|
49
|
+
</tr>
|
|
50
|
+
`;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
renderSuppliersRows(paginatedData);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// -----------------------------
|
|
58
|
+
// Render table + pagination
|
|
59
|
+
// -----------------------------
|
|
60
|
+
function renderSuppliersPage() {
|
|
61
|
+
renderTable();
|
|
62
|
+
renderPagination(paginationContainer, state, renderSuppliersPage);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// -----------------------------
|
|
66
|
+
// Helpers
|
|
67
|
+
// -----------------------------
|
|
68
|
+
function firstLatterOfSuppliers(suppliersName) {
|
|
69
|
+
let arrStr = suppliersName.trim().split(" ");
|
|
70
|
+
let firstLetter = [];
|
|
71
|
+
|
|
72
|
+
for (let index = 0; index < arrStr.length; index++) {
|
|
73
|
+
if (arrStr[index]) {
|
|
74
|
+
firstLetter.push(arrStr[index].charAt(0).toUpperCase());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return firstLetter.join("");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getNextSupplierId(data) {
|
|
82
|
+
if (!data.length) return 1;
|
|
83
|
+
|
|
84
|
+
let maxId = Math.max(...data.map((item) => Number(item.id)));
|
|
85
|
+
return maxId + 1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function clearValidation(input) {
|
|
89
|
+
input.setCustomValidity("");
|
|
90
|
+
input.style.border = "";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function resetFormFields() {
|
|
94
|
+
supplierName.value = "";
|
|
95
|
+
contactPerson.value = "";
|
|
96
|
+
supplierPhone.value = "";
|
|
97
|
+
supplierMail.value = "";
|
|
98
|
+
physicalAddress.value = "";
|
|
99
|
+
selectStatus.value = "";
|
|
100
|
+
|
|
101
|
+
clearValidation(supplierName);
|
|
102
|
+
clearValidation(contactPerson);
|
|
103
|
+
clearValidation(supplierPhone);
|
|
104
|
+
clearValidation(supplierMail);
|
|
105
|
+
clearValidation(physicalAddress);
|
|
106
|
+
clearValidation(selectStatus);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function escapeCSVValue(value) {
|
|
110
|
+
const stringValue = String(value ?? "");
|
|
111
|
+
if (
|
|
112
|
+
stringValue.includes(",") ||
|
|
113
|
+
stringValue.includes('"') ||
|
|
114
|
+
stringValue.includes("\n")
|
|
115
|
+
) {
|
|
116
|
+
return `"${stringValue.replace(/"/g, '""')}"`;
|
|
117
|
+
}
|
|
118
|
+
return stringValue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function downloadFile(content, fileName, mimeType) {
|
|
122
|
+
const blob = new Blob([content], { type: mimeType });
|
|
123
|
+
const url = URL.createObjectURL(blob);
|
|
124
|
+
|
|
125
|
+
const link = document.createElement("a");
|
|
126
|
+
link.href = url;
|
|
127
|
+
link.download = fileName;
|
|
128
|
+
document.body.appendChild(link);
|
|
129
|
+
link.click();
|
|
130
|
+
link.remove();
|
|
131
|
+
|
|
132
|
+
URL.revokeObjectURL(url);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// -----------------------------
|
|
136
|
+
// Search + Filter + Sort logic
|
|
137
|
+
// -----------------------------
|
|
138
|
+
function applyFilters() {
|
|
139
|
+
let result = [...allSuppliersData];
|
|
140
|
+
|
|
141
|
+
const searchValue = searchSuppliersByName.value.trim().toLowerCase();
|
|
142
|
+
const selectedStatus = formSelect.value.trim();
|
|
143
|
+
|
|
144
|
+
// search by supplier name
|
|
145
|
+
if (searchValue !== "") {
|
|
146
|
+
result = result.filter(function (supplier) {
|
|
147
|
+
return supplier.name.toLowerCase().includes(searchValue);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// filter by status
|
|
152
|
+
if (selectedStatus !== "" && selectedStatus.toLowerCase() !== "all") {
|
|
153
|
+
result = result.filter(function (supplier) {
|
|
154
|
+
return supplier.status.toLowerCase() === selectedStatus.toLowerCase();
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// sort by supplier name
|
|
159
|
+
if (state.sortOrder === "asc") {
|
|
160
|
+
result.sort(function (a, b) {
|
|
161
|
+
return a.name.localeCompare(b.name);
|
|
162
|
+
});
|
|
163
|
+
} else if (state.sortOrder === "desc") {
|
|
164
|
+
result.sort(function (a, b) {
|
|
165
|
+
return b.name.localeCompare(a.name);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
filteredSuppliersData = result;
|
|
170
|
+
state.totalCount = filteredSuppliersData.length;
|
|
171
|
+
renderSuppliersPage();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// -----------------------------
|
|
175
|
+
// Validation
|
|
176
|
+
// -----------------------------
|
|
177
|
+
function validateInputs(regexForValidInput, inputElement, messageShowForUser) {
|
|
178
|
+
const inputValue = inputElement.value.trim();
|
|
179
|
+
|
|
180
|
+
if (regexForValidInput.test(inputValue)) {
|
|
181
|
+
inputElement.setCustomValidity("");
|
|
182
|
+
inputElement.style.border = "2px solid rgb(0, 208, 59)";
|
|
183
|
+
} else {
|
|
184
|
+
inputElement.setCustomValidity(messageShowForUser);
|
|
185
|
+
inputElement.reportValidity();
|
|
186
|
+
inputElement.style.border = "2px solid rgba(255, 89, 89, 0.89)";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function validateSelect(selectValidate) {
|
|
191
|
+
if (selectValidate.value === "") {
|
|
192
|
+
selectValidate.setCustomValidity("Please select value");
|
|
193
|
+
selectValidate.reportValidity();
|
|
194
|
+
selectValidate.style.border = "2px solid rgba(255, 89, 89, 0.89)";
|
|
195
|
+
} else {
|
|
196
|
+
selectValidate.setCustomValidity("");
|
|
197
|
+
selectValidate.style.border = "2px solid rgb(0, 208, 59)";
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function validateSupplierForm() {
|
|
202
|
+
validateInputs(
|
|
203
|
+
/^[A-Za-z\s]{3,60}$/,
|
|
204
|
+
supplierName,
|
|
205
|
+
"Please Enter Valid Supplier Name"
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
validateInputs(
|
|
209
|
+
/^[A-Za-z\s]{3,60}$/,
|
|
210
|
+
contactPerson,
|
|
211
|
+
"Please Enter Valid Contact Person"
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
validateInputs(
|
|
215
|
+
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
|
216
|
+
supplierMail,
|
|
217
|
+
"Please enter a valid email"
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
validateInputs(
|
|
221
|
+
/^01[0125][0-9]{8}$/,
|
|
222
|
+
supplierPhone,
|
|
223
|
+
"Enter valid Egyptian phone number"
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
validateInputs(
|
|
227
|
+
/^.{3,100}$/,
|
|
228
|
+
physicalAddress,
|
|
229
|
+
"Enter valid Physical Address"
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
validateSelect(selectStatus);
|
|
233
|
+
|
|
234
|
+
return (
|
|
235
|
+
supplierName.checkValidity() &&
|
|
236
|
+
contactPerson.checkValidity() &&
|
|
237
|
+
supplierMail.checkValidity() &&
|
|
238
|
+
supplierPhone.checkValidity() &&
|
|
239
|
+
physicalAddress.checkValidity() &&
|
|
240
|
+
selectStatus.checkValidity()
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function setupInputValidation() {
|
|
245
|
+
supplierName.addEventListener("input", function () {
|
|
246
|
+
validateInputs(
|
|
247
|
+
/^[A-Za-z\s]{3,60}$/,
|
|
248
|
+
supplierName,
|
|
249
|
+
"Please Enter Valid Supplier Name"
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
contactPerson.addEventListener("input", function () {
|
|
254
|
+
validateInputs(
|
|
255
|
+
/^[A-Za-z\s]{3,60}$/,
|
|
256
|
+
contactPerson,
|
|
257
|
+
"Please Enter Valid Contact Person"
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
supplierMail.addEventListener("input", function () {
|
|
262
|
+
validateInputs(
|
|
263
|
+
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
|
264
|
+
supplierMail,
|
|
265
|
+
"Please enter a valid email"
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
supplierPhone.addEventListener("input", function () {
|
|
270
|
+
validateInputs(
|
|
271
|
+
/^01[0125][0-9]{8}$/,
|
|
272
|
+
supplierPhone,
|
|
273
|
+
"Enter valid Egyptian phone number"
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
selectStatus.addEventListener("change", function () {
|
|
278
|
+
validateSelect(selectStatus);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
physicalAddress.addEventListener("input", function () {
|
|
282
|
+
validateInputs(
|
|
283
|
+
/^.{3,100}$/,
|
|
284
|
+
physicalAddress,
|
|
285
|
+
"Enter valid Physical Address"
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// -----------------------------
|
|
291
|
+
// Load data
|
|
292
|
+
// -----------------------------
|
|
293
|
+
async function loadAndRenderSuppliers() {
|
|
294
|
+
try {
|
|
295
|
+
let suppliersData = (await getData("suppliers")).data;
|
|
296
|
+
|
|
297
|
+
allSuppliersData = suppliersData;
|
|
298
|
+
state.page = 1;
|
|
299
|
+
|
|
300
|
+
applyFilters();
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error("Failed to load suppliers:", error);
|
|
303
|
+
showDatainTable.innerHTML = `
|
|
304
|
+
<tr>
|
|
305
|
+
<td colspan="7" style="text-align:center;">Failed to load suppliers</td>
|
|
306
|
+
</tr>
|
|
307
|
+
`;
|
|
308
|
+
paginationContainer.innerHTML = "";
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// -----------------------------
|
|
313
|
+
// Search by supplier name
|
|
314
|
+
// -----------------------------
|
|
315
|
+
searchSuppliersByName.addEventListener("input", function () {
|
|
316
|
+
state.page = 1;
|
|
317
|
+
applyFilters();
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// -----------------------------
|
|
321
|
+
// Filter by status
|
|
322
|
+
// -----------------------------
|
|
323
|
+
formSelect.addEventListener("change", function () {
|
|
324
|
+
state.page = 1;
|
|
325
|
+
applyFilters();
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// -----------------------------
|
|
329
|
+
// Render rows
|
|
330
|
+
// -----------------------------
|
|
331
|
+
function renderSuppliersRows(dataAfterFilter) {
|
|
332
|
+
dataAfterFilter.forEach((dataFilter) => {
|
|
333
|
+
let statusClass =
|
|
334
|
+
dataFilter.status === "Active" ? "status_active" : "status_inactive";
|
|
335
|
+
|
|
336
|
+
showDatainTable.innerHTML += `
|
|
337
|
+
<tr id="${dataFilter.id}">
|
|
338
|
+
<td>
|
|
339
|
+
<p>
|
|
340
|
+
<span class="first-latter rounded-4 text-center p-2" id="firstLatter">
|
|
341
|
+
${firstLatterOfSuppliers(dataFilter.name)}
|
|
342
|
+
</span>
|
|
343
|
+
${dataFilter.name}
|
|
344
|
+
</p>
|
|
345
|
+
</td>
|
|
346
|
+
<td>${dataFilter.contactPerson}</td>
|
|
347
|
+
<td>${dataFilter.phone}</td>
|
|
348
|
+
<td><a href="mailto:${dataFilter.email}">${dataFilter.email}</a></td>
|
|
349
|
+
<td>${dataFilter.address}</td>
|
|
350
|
+
<td>
|
|
351
|
+
<div class="${statusClass}">
|
|
352
|
+
${dataFilter.status}
|
|
353
|
+
</div>
|
|
354
|
+
</td>
|
|
355
|
+
<td>
|
|
356
|
+
<button class="btn edit_supplier_btn" data-bs-toggle="modal" data-bs-target="#exampleModal" id="editSupplierBtn">
|
|
357
|
+
<i class="fa-solid fa-pen-to-square"></i>
|
|
358
|
+
</button>
|
|
359
|
+
<button class="btn delete_supplier_btn" id="deleteSupplierBtn">
|
|
360
|
+
<i class="fa-solid fa-trash-can"></i>
|
|
361
|
+
</button>
|
|
362
|
+
</td>
|
|
363
|
+
</tr>
|
|
364
|
+
`;
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// -----------------------------
|
|
369
|
+
// Sort by supplier name
|
|
370
|
+
// click 1 => asc
|
|
371
|
+
// click 2 => desc
|
|
372
|
+
// click 3 => reset
|
|
373
|
+
// -----------------------------
|
|
374
|
+
sortSupplierName.addEventListener("click", function () {
|
|
375
|
+
if (state.sortOrder === null) {
|
|
376
|
+
state.sortOrder = "asc";
|
|
377
|
+
} else if (state.sortOrder === "asc") {
|
|
378
|
+
state.sortOrder = "desc";
|
|
379
|
+
} else {
|
|
380
|
+
state.sortOrder = null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
state.page = 1;
|
|
384
|
+
applyFilters();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// -----------------------------
|
|
388
|
+
// Export suppliers data
|
|
389
|
+
// Exports current filtered/sorted data
|
|
390
|
+
// -----------------------------
|
|
391
|
+
exportData.addEventListener("click", function () {
|
|
392
|
+
if (!filteredSuppliersData.length) {
|
|
393
|
+
alert("No supplier data available to export.");
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const headers = [
|
|
398
|
+
"ID",
|
|
399
|
+
"Supplier Name",
|
|
400
|
+
"Contact Person",
|
|
401
|
+
"Phone",
|
|
402
|
+
"Email",
|
|
403
|
+
"Address",
|
|
404
|
+
"Status"
|
|
405
|
+
];
|
|
406
|
+
|
|
407
|
+
const rows = filteredSuppliersData.map(function (supplier) {
|
|
408
|
+
return [
|
|
409
|
+
escapeCSVValue(supplier.id),
|
|
410
|
+
escapeCSVValue(supplier.name),
|
|
411
|
+
escapeCSVValue(supplier.contactPerson),
|
|
412
|
+
escapeCSVValue(supplier.phone),
|
|
413
|
+
escapeCSVValue(supplier.email),
|
|
414
|
+
escapeCSVValue(supplier.address),
|
|
415
|
+
escapeCSVValue(supplier.status)
|
|
416
|
+
].join(",");
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const csvContent = [headers.join(","), ...rows].join("\n");
|
|
420
|
+
const fileName = `suppliers-${new Date().toISOString().slice(0, 10)}.csv`;
|
|
421
|
+
|
|
422
|
+
downloadFile(csvContent, fileName, "text/csv;charset=utf-8;");
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// -----------------------------
|
|
426
|
+
// Add supplier
|
|
427
|
+
// -----------------------------
|
|
428
|
+
addSupplier.addEventListener("click", function () {
|
|
429
|
+
targetID = null;
|
|
430
|
+
resetFormFields();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// -----------------------------
|
|
434
|
+
// Save supplier
|
|
435
|
+
// -----------------------------
|
|
436
|
+
saveSupplier.addEventListener("click", async function (e) {
|
|
437
|
+
e.preventDefault();
|
|
438
|
+
|
|
439
|
+
if (!validateSupplierForm()) return;
|
|
440
|
+
|
|
441
|
+
try {
|
|
442
|
+
const supplierObject = {
|
|
443
|
+
name: supplierName.value.trim(),
|
|
444
|
+
contactPerson: contactPerson.value.trim(),
|
|
445
|
+
phone: supplierPhone.value.trim(),
|
|
446
|
+
email: supplierMail.value.trim(),
|
|
447
|
+
address: physicalAddress.value.trim(),
|
|
448
|
+
status: selectStatus.value
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
if (targetID) {
|
|
452
|
+
await putData("suppliers", targetID, {
|
|
453
|
+
id: String(targetID),
|
|
454
|
+
...supplierObject
|
|
455
|
+
});
|
|
456
|
+
} else {
|
|
457
|
+
const freshSuppliers = (await getData("suppliers")).data;
|
|
458
|
+
const newSupplierId = getNextSupplierId(freshSuppliers);
|
|
459
|
+
|
|
460
|
+
await postData("suppliers", {
|
|
461
|
+
id: String(newSupplierId),
|
|
462
|
+
...supplierObject
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
resetFormFields();
|
|
467
|
+
targetID = null;
|
|
468
|
+
await loadAndRenderSuppliers();
|
|
469
|
+
} catch (error) {
|
|
470
|
+
console.error("Save failed:", error);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// -----------------------------
|
|
475
|
+
// Edit / Delete
|
|
476
|
+
// -----------------------------
|
|
477
|
+
showDatainTable.addEventListener("click", async function (e) {
|
|
478
|
+
let editBtn = e.target.closest(".edit_supplier_btn");
|
|
479
|
+
let deleteBtn = e.target.closest(".delete_supplier_btn");
|
|
480
|
+
|
|
481
|
+
// Edit
|
|
482
|
+
if (editBtn) {
|
|
483
|
+
let rowDataSupp_ofTarget = editBtn.closest("tr");
|
|
484
|
+
targetID = rowDataSupp_ofTarget.id;
|
|
485
|
+
|
|
486
|
+
try {
|
|
487
|
+
const supplier = allSuppliersData.find(
|
|
488
|
+
(item) => String(item.id) === String(targetID)
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
if (!supplier) return;
|
|
492
|
+
|
|
493
|
+
supplierName.value = supplier.name;
|
|
494
|
+
contactPerson.value = supplier.contactPerson;
|
|
495
|
+
supplierPhone.value = supplier.phone;
|
|
496
|
+
supplierMail.value = supplier.email;
|
|
497
|
+
physicalAddress.value = supplier.address;
|
|
498
|
+
selectStatus.value = supplier.status;
|
|
499
|
+
|
|
500
|
+
clearValidation(supplierName);
|
|
501
|
+
clearValidation(contactPerson);
|
|
502
|
+
clearValidation(supplierPhone);
|
|
503
|
+
clearValidation(supplierMail);
|
|
504
|
+
clearValidation(physicalAddress);
|
|
505
|
+
clearValidation(selectStatus);
|
|
506
|
+
} catch (error) {
|
|
507
|
+
console.error("Edit failed:", error);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Delete
|
|
512
|
+
if (deleteBtn) {
|
|
513
|
+
let row = deleteBtn.closest("tr");
|
|
514
|
+
let currentId = row.id;
|
|
515
|
+
let supplierRowName = row.children[0].innerText.trim();
|
|
516
|
+
|
|
517
|
+
if (confirm(`Are You Sure to Delete ${supplierRowName} ?`)) {
|
|
518
|
+
try {
|
|
519
|
+
await deleteData("suppliers", currentId);
|
|
520
|
+
await loadAndRenderSuppliers();
|
|
521
|
+
|
|
522
|
+
const maxPage = Math.ceil(state.totalCount / state.limit) || 1;
|
|
523
|
+
if (state.page > maxPage) {
|
|
524
|
+
state.page = maxPage;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
applyFilters();
|
|
528
|
+
} catch (error) {
|
|
529
|
+
console.error("Delete failed:", error);
|
|
530
|
+
}
|
|
531
|
+
} else {
|
|
532
|
+
alert("Supplier wasn't deleted");
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
});
|