diff --git a/src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java b/src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java index 03ab898..7bec3be 100644 --- a/src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java +++ b/src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java @@ -871,4 +871,183 @@ public class TonerController { } return tonerService.getWorkorderModelRequestHistory(); } + + // Save new department for workorder + @PostMapping("/api/savedepartment") + @ResponseBody + public boolean saveNewDepartment( + @RequestParam("department") String department, + HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + + return tonerService.savenewdepartment(department); + } + + @PostMapping("/api/savetonermodel") + @ResponseBody + public boolean saveNewTonerModel( + @RequestParam("tonermodel") String tonermodel, + @RequestParam("pid") int pid, + @RequestParam(required = false) String partCode, + HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + + return tonerService.savetonermodel(pid, tonermodel, partCode); + } + + @PostMapping("/api/saveprintermodel") + @ResponseBody + public boolean saveNewPrinterModel( + @RequestParam("printermodel") String printermodel, + HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + return tonerService.saveprintermodel(printermodel); + } + + // ================= MASTER UPDATE ENDPOINTS ================= + + @PostMapping("/api/updateprintermodel") + @ResponseBody + public boolean updatePrinterModel( + @RequestParam("id") int id, + @RequestParam("printermodel") String printermodel, + HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + return tonerService.updatePrinterModel(id, printermodel); + } + + @PostMapping("/api/updatetonermodel") + @ResponseBody + public boolean updateTonerModel( + @RequestParam("id") int id, + @RequestParam("pid") int pid, + @RequestParam("tonermodel") String tonermodel, + @RequestParam(value = "partCode", required = false) String partCode, + HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + return tonerService.updateTonerModel(id, pid, tonermodel, partCode); + } + + @PostMapping("/api/updatedepartment") + @ResponseBody + public boolean updateDepartment( + @RequestParam("id") int id, + @RequestParam("department") String department, + HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + return tonerService.updateDepartment(id, department); + } + + // ================= MASTER DELETE & RESTORE ENDPOINTS ================= + + @PostMapping("/api/deleteprintermodel") + @ResponseBody + public boolean deletePrinterModel(@RequestParam("id") int id, HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + return tonerService.deletePrinterModel(id); + } + + @PostMapping("/api/restoreprintermodel") + @ResponseBody + public boolean restorePrinterModel(@RequestParam("id") int id, HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + return tonerService.restorePrinterModel(id); + } + + @PostMapping("/api/deletetonermodel") + @ResponseBody + public boolean deleteTonerModel(@RequestParam("id") int id, HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + return tonerService.deleteTonerModel(id); + } + + @PostMapping("/api/restoretonermodel") + @ResponseBody + public boolean restoreTonerModel(@RequestParam("id") int id, HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + return tonerService.restoreTonerModel(id); + } + + @PostMapping("/api/deletedepartment") + @ResponseBody + public boolean deleteDepartment(@RequestParam("id") int id, HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + return tonerService.deleteDepartment(id); + } + + @PostMapping("/api/restoredepartment") + @ResponseBody + public boolean restoreDepartment(@RequestParam("id") int id, HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return false; + } + return tonerService.restoreDepartment(id); + } + + // ================= MASTER LISTING ENDPOINTS ================= + + @GetMapping("/api/master/printers") + @ResponseBody + public List> getMasterPrinters( + @RequestParam(value = "status", defaultValue = "active") String status, + HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return java.util.Collections.emptyList(); + } + return tonerService.getMasterPrinters(status); + } + + @GetMapping("/api/master/toners") + @ResponseBody + public List> getMasterToners( + @RequestParam(value = "status", defaultValue = "active") String status, + HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return java.util.Collections.emptyList(); + } + return tonerService.getMasterToners(status); + } + + @GetMapping("/api/master/departments") + @ResponseBody + public List> getMasterDepartments( + @RequestParam(value = "status", defaultValue = "active") String status, + HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return java.util.Collections.emptyList(); + } + return tonerService.getMasterDepartments(status); + } + + @GetMapping("/api/toners/all") + @ResponseBody + public List> getAllToners(HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return java.util.Collections.emptyList(); + } + return tonerService.getAllActiveToners(); + } + } diff --git a/src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java b/src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java index 286a4d1..3cda3c3 100644 --- a/src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java +++ b/src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java @@ -222,6 +222,253 @@ public class TonerService { return map; } + // ================ Adding department & toners & printers ================ + + public boolean savenewdepartment(String department) { + if (department == null || department.trim().isEmpty()) return false; + String cleanDept = department.trim(); + try { + String sql = "INSERT INTO toner_department_list (dept_name, is_active, is_delete) VALUES (?, 1, 0)"; + jdbcTemplate.update(sql, cleanDept); + return true; + } catch (Exception e) { + try { + String sql2 = "INSERT INTO toner_department_list (department_name, is_active, is_delete) VALUES (?, 1, 0)"; + jdbcTemplate.update(sql2, cleanDept); + return true; + } catch (Exception ex) { + try { + String sql3 = "INSERT INTO toner_department_list (dept_name) VALUES (?)"; + jdbcTemplate.update(sql3, cleanDept); + return true; + } catch (Exception ex2) { + ex2.printStackTrace(); + return false; + } + } + } + } + + public boolean savetonermodel(int pid, String modelname, String partcode) { + if (pid <= 0 || modelname == null || modelname.trim().isEmpty()) return false; + String cleanModel = modelname.trim(); + String cleanPart = (partcode != null && !partcode.trim().isEmpty()) ? partcode.trim() : null; + try { + String sql = "INSERT INTO toner_model_list (pid, model_name, part_code, is_active, is_delete) VALUES (?, ?, ?, 1, 0)"; + jdbcTemplate.update(sql, pid, cleanModel, cleanPart); + return true; + } catch (Exception e) { + try { + String sql2 = "INSERT INTO toner_model_list (pid, model_name, part_code) VALUES (?, ?, ?)"; + jdbcTemplate.update(sql2, pid, cleanModel, cleanPart); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + } + } + + public boolean saveprintermodel(String printermodel) { + if (printermodel == null || printermodel.trim().isEmpty()) return false; + String cleanPrinter = printermodel.trim(); + try { + String sql = "INSERT INTO toner_printer_model_list (printer_name, is_active, is_delete) VALUES (?, 1, 0)"; + jdbcTemplate.update(sql, cleanPrinter); + return true; + } catch (Exception e) { + try { + String sql2 = "INSERT INTO toner_printer_model_list (printer_name) VALUES (?)"; + jdbcTemplate.update(sql2, cleanPrinter); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + } + } + + public boolean updatePrinterModel(int id, String printermodel) { + if (id <= 0 || printermodel == null || printermodel.trim().isEmpty()) return false; + String cleanPrinter = printermodel.trim(); + try { + String sql = "UPDATE toner_printer_model_list SET printer_name = ? WHERE id = ?"; + return jdbcTemplate.update(sql, cleanPrinter, id) > 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean updateTonerModel(int id, int pid, String modelname, String partcode) { + if (id <= 0 || pid <= 0 || modelname == null || modelname.trim().isEmpty()) return false; + String cleanModel = modelname.trim(); + String cleanPart = (partcode != null && !partcode.trim().isEmpty()) ? partcode.trim() : null; + try { + String sql = "UPDATE toner_model_list SET pid = ?, model_name = ?, part_code = ? WHERE id = ?"; + return jdbcTemplate.update(sql, pid, cleanModel, cleanPart, id) > 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean updateDepartment(int id, String department) { + if (id <= 0 || department == null || department.trim().isEmpty()) return false; + String cleanDept = department.trim(); + try { + String sql = "UPDATE toner_department_list SET dept_name = ? WHERE id = ?"; + return jdbcTemplate.update(sql, cleanDept, id) > 0; + } catch (Exception e) { + try { + String sql2 = "UPDATE toner_department_list SET department_name = ? WHERE id = ?"; + return jdbcTemplate.update(sql2, cleanDept, id) > 0; + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + } + } + + public boolean deletePrinterModel(int id) { + if (id <= 0) return false; + try { + String sql = "UPDATE toner_printer_model_list SET is_active = 0, is_delete = 1 WHERE id = ?"; + return jdbcTemplate.update(sql, id) > 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean restorePrinterModel(int id) { + if (id <= 0) return false; + try { + String sql = "UPDATE toner_printer_model_list SET is_active = 1, is_delete = 0 WHERE id = ?"; + return jdbcTemplate.update(sql, id) > 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean deleteTonerModel(int id) { + if (id <= 0) return false; + try { + String sql = "UPDATE toner_model_list SET is_active = 0, is_delete = 1 WHERE id = ?"; + return jdbcTemplate.update(sql, id) > 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean restoreTonerModel(int id) { + if (id <= 0) return false; + try { + String sql = "UPDATE toner_model_list SET is_active = 1, is_delete = 0 WHERE id = ?"; + return jdbcTemplate.update(sql, id) > 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean deleteDepartment(int id) { + if (id <= 0) return false; + try { + String sql = "UPDATE toner_department_list SET is_active = 0, is_delete = 1 WHERE id = ?"; + return jdbcTemplate.update(sql, id) > 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean restoreDepartment(int id) { + if (id <= 0) return false; + try { + String sql = "UPDATE toner_department_list SET is_active = 1, is_delete = 0 WHERE id = ?"; + return jdbcTemplate.update(sql, id) > 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public List> getMasterPrinters(String status) { + try { + String sql; + if ("deleted".equalsIgnoreCase(status)) { + sql = "SELECT id, printer_name, is_active, is_delete FROM toner_printer_model_list WHERE (is_delete = 1 OR is_active = 0) ORDER BY id DESC"; + } else if ("all".equalsIgnoreCase(status)) { + sql = "SELECT id, printer_name, is_active, is_delete FROM toner_printer_model_list ORDER BY printer_name ASC"; + } else { + sql = "SELECT id, printer_name, is_active, is_delete FROM toner_printer_model_list WHERE (is_active IS NULL OR is_active = 1) AND (is_delete IS NULL OR is_delete = 0) ORDER BY printer_name ASC"; + } + return jdbcTemplate.queryForList(sql); + } catch (Exception e) { + e.printStackTrace(); + return new ArrayList<>(); + } + } + + public List> getMasterToners(String status) { + try { + String sql; + if ("deleted".equalsIgnoreCase(status)) { + sql = "SELECT t.id, t.pid, t.model_name, t.part_code, p.printer_name, t.is_active, t.is_delete FROM toner_model_list t " + + "LEFT JOIN toner_printer_model_list p ON t.pid = p.id " + + "WHERE (t.is_delete = 1 OR t.is_active = 0) ORDER BY t.id DESC"; + } else if ("all".equalsIgnoreCase(status)) { + sql = "SELECT t.id, t.pid, t.model_name, t.part_code, p.printer_name, t.is_active, t.is_delete FROM toner_model_list t " + + "LEFT JOIN toner_printer_model_list p ON t.pid = p.id ORDER BY p.printer_name ASC, t.model_name ASC"; + } else { + sql = "SELECT t.id, t.pid, t.model_name, t.part_code, p.printer_name, t.is_active, t.is_delete FROM toner_model_list t " + + "LEFT JOIN toner_printer_model_list p ON t.pid = p.id " + + "WHERE (t.is_active IS NULL OR t.is_active = 1) AND (t.is_delete IS NULL OR t.is_delete = 0) " + + "ORDER BY p.printer_name ASC, t.model_name ASC"; + } + return jdbcTemplate.queryForList(sql); + } catch (Exception e) { + e.printStackTrace(); + return new ArrayList<>(); + } + } + + public List> getMasterDepartments(String status) { + try { + String sql; + if ("deleted".equalsIgnoreCase(status)) { + sql = "SELECT id, dept_name, is_active, is_delete FROM toner_department_list WHERE (is_delete = 1 OR is_active = 0) ORDER BY id DESC"; + } else if ("all".equalsIgnoreCase(status)) { + sql = "SELECT id, dept_name, is_active, is_delete FROM toner_department_list ORDER BY dept_name ASC"; + } else { + sql = "SELECT id, dept_name, is_active, is_delete FROM toner_department_list WHERE (is_active IS NULL OR is_active = 1) AND (is_delete IS NULL OR is_delete = 0) ORDER BY dept_name ASC"; + } + return jdbcTemplate.queryForList(sql); + } catch (Exception e) { + try { + String fallbackSql; + if ("deleted".equalsIgnoreCase(status)) { + fallbackSql = "SELECT id, department_name AS dept_name, is_active, is_delete FROM toner_department_list WHERE (is_delete = 1 OR is_active = 0) ORDER BY id DESC"; + } else if ("all".equalsIgnoreCase(status)) { + fallbackSql = "SELECT id, department_name AS dept_name, is_active, is_delete FROM toner_department_list ORDER BY department_name ASC"; + } else { + fallbackSql = "SELECT id, department_name AS dept_name, is_active, is_delete FROM toner_department_list WHERE (is_active IS NULL OR is_active = 1) AND (is_delete IS NULL OR is_delete = 0) ORDER BY department_name ASC"; + } + return jdbcTemplate.queryForList(fallbackSql); + } catch (Exception ex) { + ex.printStackTrace(); + return new ArrayList<>(); + } + } + } + + public List> getAllActiveToners() { + return getMasterToners("active"); + } + // ================= PRINTERS & TONERS ================= public List> getActivePrinters() { String sql = "SELECT id, printer_name FROM toner_printer_model_list WHERE is_active = 1 AND is_delete = 0"; @@ -441,7 +688,9 @@ public class TonerService { int pid, int tid, int qtyRequested, String newUploadPath, String username) { Timestamp now = new Timestamp(System.currentTimeMillis()); try { - String safeZoneId = (zoneId != null && !zoneId.trim().isEmpty() && !"null".equalsIgnoreCase(zoneId)) ? zoneId.trim() : null; + String safeZoneId = (zoneId != null && !zoneId.trim().isEmpty() && !"null".equalsIgnoreCase(zoneId)) + ? zoneId.trim() + : null; if (newUploadPath != null && !newUploadPath.trim().isEmpty()) { String sql = "UPDATE toner_department_requested_details SET " + "itcell_ref_no = ?, dept_id = ?, zone_id = ?, pid = ?, tid = ?, " + diff --git a/src/main/resources/templates/gccservicepaymenttracker/dashboard.html b/src/main/resources/templates/gccservicepaymenttracker/dashboard.html index d9c6f5e..e9c4dca 100644 --- a/src/main/resources/templates/gccservicepaymenttracker/dashboard.html +++ b/src/main/resources/templates/gccservicepaymenttracker/dashboard.html @@ -305,6 +305,16 @@ color: #991b1b; } + .pending-split-box { + transition: all 0.2s ease-in-out; + } + + .pending-split-box:hover { + transform: translateY(-2px); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08); + filter: brightness(0.97); + } + .category-card:hover { transform: translateY(-4px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.06); @@ -1083,11 +1093,37 @@
-
-

Pending Bills

-

0

+
+
+
+

Pending Bills

+

0

+
+
+ +
+
+ IT + Cell + 0 +
+
+ Others + 0 +
+
- +
@@ -3258,6 +3294,8 @@ const vendorMap = {}; let allCompletedCount = 0; + let totalItCell = 0; + let totalOthers = 0; allRecords.forEach(row => { const statusName = String(row.resolved_status || getStatusName(row.status) || '').trim(); @@ -3333,8 +3371,10 @@ } else { const group = getStatusGroup(statusName); if (group === 'it_cell') { + totalItCell++; vData.itCellCount++; } else { + totalOthers++; vData.othersCount++; } @@ -3358,6 +3398,10 @@ if (cardCompletedEl) cardCompletedEl.textContent = completedTotal; const cardPendingEl = document.getElementById('count-pending'); if (cardPendingEl) cardPendingEl.textContent = pendingTotal; + const cardPendingItCellEl = document.getElementById('count-pending-itcell'); + if (cardPendingItCellEl) cardPendingItCellEl.textContent = totalItCell; + const cardPendingOthersEl = document.getElementById('count-pending-others'); + if (cardPendingOthersEl) cardPendingOthersEl.textContent = totalOthers; let summaryBodyHtml = ` ${total} diff --git a/src/main/resources/templates/gcctonermanagement/upload.html b/src/main/resources/templates/gcctonermanagement/upload.html index d5ab1ec..b6e7d53 100644 --- a/src/main/resources/templates/gcctonermanagement/upload.html +++ b/src/main/resources/templates/gcctonermanagement/upload.html @@ -861,6 +861,10 @@ Outward Register + + + Master Settings + @@ -1094,10 +1098,23 @@
Category & Workorder Wise Stock Breakdown
+
+ + + entries +
0 records +
@@ -1146,6 +1163,17 @@
+ +
+
+ Showing 0 to 0 of 0 entries +
+
    + +
+
@@ -2024,9 +2052,9 @@
-
@@ -2493,10 +2521,430 @@ + + +
+
+

+ + Master Configuration & Management +

+
+ + + + + +
+
+ +
+
+
+ Add New Printer Model +
+
+
+ + +
+ +
+
+
+ + +
+
+
+
+
+ Printer Models +
+
+ + +
+
+
+ + 0 + Records +
+
+
+ + + + + + + + + + + + + + +
S.NoPrinter Model NameStatusAction
Loading printer + models...
+
+
+
+
+
+ + + + + + + +
+ + + + + + + + + + @@ -2639,6 +3087,8 @@ }); lastSavedOutward = null; disableUploadArea(); + } else if (panelName === 'master') { + loadMasterPage(); } } @@ -2915,6 +3365,8 @@ // ================= DASHBOARD VIEW LOGIC ================= let dashboardChartInstance = null; let dashboardGroupsCached = []; + let dashboardBreakdownCurrentPage = 1; + let dashboardBreakdownPageSize = 20; let activeDashboardDetailsType = ''; let dashboardDetailsCurrentPage = 1; let dashboardDetailsPageSize = 10; @@ -3276,7 +3728,7 @@ renderDashboardChart(totalAvailable, totalOutward); // Render table - renderDashboardTable(list); + renderDashboardTable(list, 1); // If the details card is active, reload its details dynamically if (activeDashboardDetailsType) { @@ -3934,18 +4386,30 @@ applyDashboardDetailsFilters(1); } + function onDashboardBreakdownPageSizeChange(val) { + dashboardBreakdownPageSize = parseInt(val) || 20; + dashboardBreakdownCurrentPage = 1; + renderDashboardTable(dashboardGroupsCached, 1); + } + function onDashboardBreakdownViewChange(val) { - renderDashboardTable(dashboardGroupsCached); + dashboardBreakdownCurrentPage = 1; + renderDashboardTable(dashboardGroupsCached, 1); } function onDashboardStockBreakdownSearchInput(val) { - renderDashboardTable(dashboardGroupsCached); + dashboardBreakdownCurrentPage = 1; + renderDashboardTable(dashboardGroupsCached, 1); } - function renderDashboardTable(data) { + function renderDashboardTable(data, targetPage) { const tbody = document.getElementById('dashboard-tbody'); if (!tbody) return; + if (typeof targetPage === 'number') { + dashboardBreakdownCurrentPage = targetPage; + } + tbody.innerHTML = ''; const viewFilter = document.getElementById('dashboard-breakdown-view-filter')?.value || 'user_request'; @@ -3985,11 +4449,15 @@ countBadge.textContent = `${filtered.length} records`; } + const totalPages = Math.ceil(filtered.length / dashboardBreakdownPageSize) || 1; + dashboardBreakdownCurrentPage = Math.min(Math.max(1, dashboardBreakdownCurrentPage), totalPages); + if (filtered.length === 0) { const emptyMsg = viewFilter === 'user_request' ? 'No user request breakdown records found.' : (viewFilter === 'workorder' ? 'No work order breakdown records found.' : 'No matching records found.'); tbody.innerHTML = `${emptyMsg}`; + renderTablePaginationNav('dashboard-breakdown-pagination-info', 'dashboard-breakdown-pagination-nav', dashboardBreakdownCurrentPage, dashboardBreakdownPageSize, 1, 0, (p) => renderDashboardTable(dashboardGroupsCached, p)); return; } @@ -4001,8 +4469,11 @@ return (a.workOrderRef || '').localeCompare(b.workOrderRef || ''); }); - filtered.forEach((item, index) => { + const pagedData = filtered.slice((dashboardBreakdownCurrentPage - 1) * dashboardBreakdownPageSize, dashboardBreakdownCurrentPage * dashboardBreakdownPageSize); + + pagedData.forEach((item, index) => { const tr = document.createElement('tr'); + const globalIdx = (dashboardBreakdownCurrentPage - 1) * dashboardBreakdownPageSize + index + 1; let availableBadge = ''; if (item.workorderQty > 0 || item.inwardQty > 0) { @@ -4056,7 +4527,7 @@ : `0`; let cellsHtml = ` - ${index + 1} + ${globalIdx} ${escapeHtml(item.printerName)} `; if (showWoRef) { @@ -4076,6 +4547,8 @@ tr.innerHTML = cellsHtml; tbody.appendChild(tr); }); + + renderTablePaginationNav('dashboard-breakdown-pagination-info', 'dashboard-breakdown-pagination-nav', dashboardBreakdownCurrentPage, dashboardBreakdownPageSize, totalPages, filtered.length, (p) => renderDashboardTable(dashboardGroupsCached, p)); } let activeDbBreakdownKey = ''; @@ -5817,6 +6290,8 @@ if (cardPending) cardPending.style.outline = (statusName === 'Pending') ? '3px solid #f59e0b' : 'none'; renderUserRequestsTable('user-requests-table-body', userRequestsCached, true, 1); + //renderUserRequestsTable('cat-work-page-size-select', userRequestsCached, true, 1); + } function onUserReqPageSizeChange(val) { @@ -6078,7 +6553,7 @@ if (!groups[key]) { groups[key] = { itcell_ref_no: cleanRef, - dept_name: item.dept_name, + dept_name: item.dept_name || 'N/A', zone_name: item.zone_name || 'N/A', pid: item.pid, printer_name: item.printer_name || 'N/A', @@ -6103,7 +6578,7 @@ groups[key].cdate = item.cdate; } if (item.user_request_upload) { - const files = item.user_request_upload.split(',').filter(Boolean); + const files = item.user_request_upload.split(',').map(f => (f || '').trim()).filter(Boolean); files.forEach(f => { if (!groups[key].user_request_uploads.includes(f)) { groups[key].user_request_uploads.push(f); @@ -6270,18 +6745,28 @@ let docBtn = '-'; if (showDoc && item.user_request_uploads && item.user_request_uploads.length > 0) { - docBtn = item.user_request_uploads.map((file, i) => { - const label = item.user_request_uploads.length > 1 ? `View Letter ${i + 1}` : `View Letter`; - return ` ${label}`; - }).join(' '); + const validFiles = [...new Set(item.user_request_uploads.map(f => (f || '').trim()).filter(Boolean))]; + if (validFiles.length > 0) { + docBtn = ` View Letter`; + } } const refLink = `${escapeHtml(item.itcell_ref_no || '')}`; - const reqNoDisplay = (item.req_nos && item.req_nos.length > 0) ? item.req_nos.join(', ') : (item.req_no || '-'); + + let reqNoHtml = '-'; + if (item.req_nos && item.req_nos.length > 4) { + const shown = item.req_nos.slice(0, 3).map(escapeHtml).join(', '); + const remaining = item.req_nos.length - 3; + reqNoHtml = `${shown}, ... (+${remaining} more)`; + } else if (item.req_nos && item.req_nos.length > 0) { + reqNoHtml = item.req_nos.map(escapeHtml).join(', '); + } else if (item.req_no) { + reqNoHtml = escapeHtml(item.req_no); + } let rowHtml = ` ${globalIdx} - ${escapeHtml(reqNoDisplay)} + ${reqNoHtml} ${refLink} ${escapeHtml(item.dept_name)} ${escapeHtml(item.zone_name)} @@ -6845,7 +7330,7 @@ const zoneId = zoneEl ? zoneEl.value.trim() : ''; const urInput = document.getElementById('request-user-file'); - if (!itcellRefNo || !deptId) { + if (!itcellRefNo) { showNotification('warning', 'Missing Fields', 'Please fill in all required fields.'); return; } @@ -8859,10 +9344,6 @@ statusBadge = 'Partial'; } - const pdfBtn = item.user_request_upload - ? ` View` - : ` N/A`; - const editBtn = ` ` + : `
+ + +
`; + + tr.innerHTML = ` + ${index + 1} + ${escapeHtml(item.printer_name)} + ${statusBadge} + ${actionBtn} + `; + tbody.appendChild(tr); + }); + } + + function openEditMasterPrinterModal(id) { + const printer = masterPrintersCached.find(p => String(p.id) === String(id)); + if (!printer) return; + + document.getElementById('edit-master-printer-id').value = printer.id; + document.getElementById('edit-master-printer-name').value = printer.printer_name || ''; + + const modal = new bootstrap.Modal(document.getElementById('editMasterPrinterModal')); + modal.show(); + } + + function submitEditMasterPrinter() { + const id = document.getElementById('edit-master-printer-id')?.value; + const nameInput = document.getElementById('edit-master-printer-name'); + const saveBtn = document.getElementById('edit-master-printer-save-btn'); + if (!id || !nameInput) return; + + const name = nameInput.value.trim(); + if (!name) { + showNotification('warning', 'Field Required', 'Please enter a printer model name.'); + return; + } + + if (saveBtn) { + saveBtn.disabled = true; + saveBtn.innerHTML = ' Saving...'; + } + + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('id', id); + formData.append('printermodel', name); + + fetch(contextPath + 'gcctonermanagement/api/updateprintermodel', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (saveBtn) { + saveBtn.disabled = false; + saveBtn.innerHTML = ' Save Changes'; + } + if (success === true || success === 'true') { + const modalEl = document.getElementById('editMasterPrinterModal'); + const modalInstance = bootstrap.Modal.getInstance(modalEl); + if (modalInstance) modalInstance.hide(); + + Swal.fire({ + icon: 'success', + title: 'Printer Model Updated!', + text: `"${name}" was updated successfully.`, + confirmButtonColor: '#0f5c78' + }); + fetchMasterPrinters(); + fetchPrinters(); + } else { + showNotification('error', 'Error', 'Failed to update printer model. Please try again.'); + } + }) + .catch(err => { + console.error("Error updating printer model:", err); + if (saveBtn) { + saveBtn.disabled = false; + saveBtn.innerHTML = ' Save Changes'; + } + showNotification('error', 'Error', 'Connection error while updating printer model.'); + }); + } + + function submitMasterPrinter() { + const nameInput = document.getElementById('master-printer-name'); + const submitBtn = document.getElementById('master-printer-submit-btn'); + if (!nameInput) return; + + const name = nameInput.value.trim(); + if (!name) { + showNotification('warning', 'Field Required', 'Please enter a printer model name.'); + return; + } + + if (submitBtn) { + submitBtn.disabled = true; + submitBtn.innerHTML = ' Saving...'; + } + + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('printermodel', name); + + fetch(contextPath + 'gcctonermanagement/api/saveprintermodel', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (submitBtn) { + submitBtn.disabled = false; + submitBtn.innerHTML = ' Save Printer Model'; + } + if (success === true || success === 'true') { + Swal.fire({ + icon: 'success', + title: 'Printer Model Added!', + text: `"${name}" has been added to active printer models.`, + confirmButtonColor: '#0f5c78' + }); + nameInput.value = ''; + fetchMasterPrinters(); + fetchPrinters(); + } else { + showNotification('error', 'Error', 'Failed to save printer model. Please try again.'); + } + }) + .catch(err => { + console.error("Error saving printer model:", err); + if (submitBtn) { + submitBtn.disabled = false; + submitBtn.innerHTML = ' Save Printer Model'; + } + showNotification('error', 'Error', 'Connection error while saving printer model.'); + }); + } + + function deleteMasterPrinter(id, name) { + if (!id) return; + Swal.fire({ + title: 'Delete Printer Model?', + text: `Are you sure you want to delete "${name}"? It will be deactivated and hidden from all dropdowns.`, + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#d33', + cancelButtonColor: '#64748b', + confirmButtonText: ' Yes, Delete', + cancelButtonText: 'Cancel' + }).then(result => { + if (result.isConfirmed) { + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('id', id); + + fetch(contextPath + 'gcctonermanagement/api/deleteprintermodel', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (success === true || success === 'true') { + showNotification('success', 'Deleted', `"${name}" was deleted successfully.`); + fetchMasterPrinters(); + fetchPrinters(); + } else { + showNotification('error', 'Error', 'Failed to delete printer model.'); + } + }) + .catch(err => { + console.error("Error deleting printer model:", err); + showNotification('error', 'Error', 'Connection error while deleting printer model.'); + }); + } + }); + } + + function restoreMasterPrinter(id, name) { + if (!id) return; + Swal.fire({ + title: 'Restore Printer Model?', + text: `Do you want to restore "${name}"? It will become active and available in all dropdowns again.`, + icon: 'question', + showCancelButton: true, + confirmButtonColor: '#10b981', + cancelButtonColor: '#64748b', + confirmButtonText: ' Yes, Restore', + cancelButtonText: 'Cancel' + }).then(result => { + if (result.isConfirmed) { + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('id', id); + + fetch(contextPath + 'gcctonermanagement/api/restoreprintermodel', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (success === true || success === 'true') { + showNotification('success', 'Restored', `"${name}" was restored successfully.`); + fetchMasterPrinters(); + fetchPrinters(); + } else { + showNotification('error', 'Error', 'Failed to restore printer model.'); + } + }) + .catch(err => { + console.error("Error restoring printer model:", err); + showNotification('error', 'Error', 'Connection error while restoring printer model.'); + }); + } + }); + } + + // --- 2. TONER MODELS --- + function setMasterTonerStatusFilter(status) { + masterTonerStatusFilter = status; + const btnActive = document.getElementById('btn-master-toner-status-active'); + const btnDeleted = document.getElementById('btn-master-toner-status-deleted'); + if (btnActive && btnDeleted) { + if (status === 'active') { + btnActive.classList.add('active'); + btnDeleted.classList.remove('active'); + } else { + btnDeleted.classList.add('active'); + btnActive.classList.remove('active'); + } + } + fetchMasterToners(); + } + + function populateMasterTonerPrinterDropdowns() { + const selectForm = document.getElementById('master-toner-printer-select'); + const selectFilter = document.getElementById('master-toner-printer-filter'); + + if (selectForm) { + const currentVal = selectForm.value; + selectForm.innerHTML = ''; + masterPrintersCached.filter(p => !p.is_delete && (p.is_active === undefined || p.is_active == 1)).forEach(p => { + const opt = document.createElement('option'); + opt.value = p.id; + opt.textContent = p.printer_name; + if (String(p.id) === String(currentVal)) opt.selected = true; + selectForm.appendChild(opt); + }); + } + + if (selectFilter) { + const currentFilter = selectFilter.value; + selectFilter.innerHTML = ''; + masterPrintersCached.forEach(p => { + const opt = document.createElement('option'); + opt.value = p.id; + opt.textContent = p.printer_name; + if (String(p.id) === String(currentFilter)) opt.selected = true; + selectFilter.appendChild(opt); + }); + } + } + + function fetchMasterToners() { + const tbody = document.getElementById('master-toner-tbody'); + if (tbody) { + tbody.innerHTML = 'Loading toner models...'; + } + + fetch(contextPath + 'gcctonermanagement/api/master/toners?status=' + encodeURIComponent(masterTonerStatusFilter)) + .then(res => res.json()) + .then(data => { + masterTonersCached = data || []; + filterMasterToners(); + }) + .catch(err => { + console.error("Error fetching master toners:", err); + if (tbody) tbody.innerHTML = 'Failed to load toner models.'; + }); + } + + function filterMasterToners() { + const pidFilter = document.getElementById('master-toner-printer-filter')?.value || ''; + const searchQ = (document.getElementById('master-toner-search')?.value || '').toLowerCase().trim(); + + let filtered = masterTonersCached.filter(t => { + if (pidFilter && String(t.pid) !== String(pidFilter)) return false; + if (searchQ) { + const modelName = (t.model_name || '').toLowerCase(); + const partCode = (t.part_code || '').toLowerCase(); + const printerName = (t.printer_name || '').toLowerCase(); + if (!modelName.includes(searchQ) && !partCode.includes(searchQ) && !printerName.includes(searchQ)) { + return false; + } + } + return true; + }); + + renderMasterTonersTable(filtered); + } + + function renderMasterTonersTable(data) { + const tbody = document.getElementById('master-toner-tbody'); + const countBadge = document.getElementById('master-toner-count-badge'); + if (countBadge) countBadge.textContent = `${data.length} Records`; + if (!tbody) return; + + tbody.innerHTML = ''; + if (data.length === 0) { + tbody.innerHTML = `No ${masterTonerStatusFilter === 'deleted' ? 'deleted' : 'configured'} toner models found.`; + return; + } + + data.forEach((item, index) => { + const tr = document.createElement('tr'); + const isDeleted = (item.is_delete == 1 || item.is_active == 0); + const statusBadge = isDeleted + ? 'Deleted' + : 'Active'; + + const actionBtn = isDeleted + ? `` + : `
+ + +
`; + + tr.innerHTML = ` + ${index + 1} + ${escapeHtml(item.printer_name || 'N/A')} + ${escapeHtml(item.model_name)} + ${escapeHtml(item.part_code || '-')} + ${statusBadge} + ${actionBtn} + `; + tbody.appendChild(tr); + }); + } + + function openEditMasterTonerModal(id) { + const toner = masterTonersCached.find(t => String(t.id) === String(id)); + if (!toner) return; + + document.getElementById('edit-master-toner-id').value = toner.id; + document.getElementById('edit-master-toner-model-name').value = toner.model_name || ''; + document.getElementById('edit-master-toner-part-code').value = toner.part_code || ''; + + const select = document.getElementById('edit-master-toner-printer-select'); + if (select) { + select.innerHTML = ''; + masterPrintersCached.filter(p => !p.is_delete && (p.is_active === undefined || p.is_active == 1)).forEach(p => { + const opt = document.createElement('option'); + opt.value = p.id; + opt.textContent = p.printer_name; + if (String(p.id) === String(toner.pid)) opt.selected = true; + select.appendChild(opt); + }); + } + + const modal = new bootstrap.Modal(document.getElementById('editMasterTonerModal')); + modal.show(); + } + + function submitEditMasterToner() { + const id = document.getElementById('edit-master-toner-id')?.value; + const pid = document.getElementById('edit-master-toner-printer-select')?.value; + const modelInput = document.getElementById('edit-master-toner-model-name'); + const partInput = document.getElementById('edit-master-toner-part-code'); + const saveBtn = document.getElementById('edit-master-toner-save-btn'); + + if (!id || !pid || !modelInput) return; + + const modelName = modelInput.value.trim(); + const partCode = partInput ? partInput.value.trim() : ''; + + if (!modelName) { + showNotification('warning', 'Field Required', 'Please enter a toner model name.'); + return; + } + + if (saveBtn) { + saveBtn.disabled = true; + saveBtn.innerHTML = ' Saving...'; + } + + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('id', id); + formData.append('pid', pid); + formData.append('tonermodel', modelName); + if (partCode) { + formData.append('partCode', partCode); + } + + fetch(contextPath + 'gcctonermanagement/api/updatetonermodel', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (saveBtn) { + saveBtn.disabled = false; + saveBtn.innerHTML = ' Save Changes'; + } + if (success === true || success === 'true') { + const modalEl = document.getElementById('editMasterTonerModal'); + const modalInstance = bootstrap.Modal.getInstance(modalEl); + if (modalInstance) modalInstance.hide(); + + Swal.fire({ + icon: 'success', + title: 'Toner Model Updated!', + text: `"${modelName}" was updated successfully.`, + confirmButtonColor: '#0f5c78' + }); + fetchMasterToners(); + } else { + showNotification('error', 'Error', 'Failed to update toner model. Please try again.'); + } + }) + .catch(err => { + console.error("Error updating toner model:", err); + if (saveBtn) { + saveBtn.disabled = false; + saveBtn.innerHTML = ' Save Changes'; + } + showNotification('error', 'Error', 'Connection error while updating toner model.'); + }); + } + + function submitMasterToner() { + const printerSelect = document.getElementById('master-toner-printer-select'); + const modelInput = document.getElementById('master-toner-model-name'); + const partInput = document.getElementById('master-toner-part-code'); + const submitBtn = document.getElementById('master-toner-submit-btn'); + + if (!printerSelect || !modelInput) return; + + const pid = printerSelect.value; + const modelName = modelInput.value.trim(); + const partCode = partInput ? partInput.value.trim() : ''; + + if (!pid) { + showNotification('warning', 'Missing Selection', 'Please select a printer model.'); + return; + } + if (!modelName) { + showNotification('warning', 'Field Required', 'Please enter a toner model name.'); + return; + } + + if (submitBtn) { + submitBtn.disabled = true; + submitBtn.innerHTML = ' Saving...'; + } + + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('pid', pid); + formData.append('tonermodel', modelName); + if (partCode) { + formData.append('partCode', partCode); + } + + fetch(contextPath + 'gcctonermanagement/api/savetonermodel', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (submitBtn) { + submitBtn.disabled = false; + submitBtn.innerHTML = ' Save Toner Model'; + } + if (success === true || success === 'true') { + Swal.fire({ + icon: 'success', + title: 'Toner Model Added!', + text: `"${modelName}" has been added successfully.`, + confirmButtonColor: '#0f5c78' + }); + modelInput.value = ''; + if (partInput) partInput.value = ''; + fetchMasterToners(); + } else { + showNotification('error', 'Error', 'Failed to save toner model. Please try again.'); + } + }) + .catch(err => { + console.error("Error saving toner model:", err); + if (submitBtn) { + submitBtn.disabled = false; + submitBtn.innerHTML = ' Save Toner Model'; + } + showNotification('error', 'Error', 'Connection error while saving toner model.'); + }); + } + + function deleteMasterToner(id, name) { + if (!id) return; + Swal.fire({ + title: 'Delete Toner Model?', + text: `Are you sure you want to delete "${name}"? It will be deactivated and hidden from all dropdowns.`, + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#d33', + cancelButtonColor: '#64748b', + confirmButtonText: ' Yes, Delete', + cancelButtonText: 'Cancel' + }).then(result => { + if (result.isConfirmed) { + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('id', id); + + fetch(contextPath + 'gcctonermanagement/api/deletetonermodel', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (success === true || success === 'true') { + showNotification('success', 'Deleted', `"${name}" was deleted successfully.`); + fetchMasterToners(); + } else { + showNotification('error', 'Error', 'Failed to delete toner model.'); + } + }) + .catch(err => { + console.error("Error deleting toner model:", err); + showNotification('error', 'Error', 'Connection error while deleting toner model.'); + }); + } + }); + } + + function restoreMasterToner(id, name) { + if (!id) return; + Swal.fire({ + title: 'Restore Toner Model?', + text: `Do you want to restore "${name}"? It will become active and available in all dropdowns again.`, + icon: 'question', + showCancelButton: true, + confirmButtonColor: '#10b981', + cancelButtonColor: '#64748b', + confirmButtonText: ' Yes, Restore', + cancelButtonText: 'Cancel' + }).then(result => { + if (result.isConfirmed) { + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('id', id); + + fetch(contextPath + 'gcctonermanagement/api/restoretonermodel', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (success === true || success === 'true') { + showNotification('success', 'Restored', `"${name}" was restored successfully.`); + fetchMasterToners(); + } else { + showNotification('error', 'Error', 'Failed to restore toner model.'); + } + }) + .catch(err => { + console.error("Error restoring toner model:", err); + showNotification('error', 'Error', 'Connection error while restoring toner model.'); + }); + } + }); + } + + // --- 3. DEPARTMENTS --- + function setMasterDeptStatusFilter(status) { + masterDeptStatusFilter = status; + const btnActive = document.getElementById('btn-master-dept-status-active'); + const btnDeleted = document.getElementById('btn-master-dept-status-deleted'); + if (btnActive && btnDeleted) { + if (status === 'active') { + btnActive.classList.add('active'); + btnDeleted.classList.remove('active'); + } else { + btnDeleted.classList.add('active'); + btnActive.classList.remove('active'); + } + } + fetchMasterDepartments(); + } + + function fetchMasterDepartments() { + const tbody = document.getElementById('master-dept-tbody'); + if (tbody) { + tbody.innerHTML = 'Loading departments...'; + } + + fetch(contextPath + 'gcctonermanagement/api/master/departments?status=' + encodeURIComponent(masterDeptStatusFilter)) + .then(res => res.json()) + .then(data => { + masterDepartmentsCached = data || []; + if (masterDeptStatusFilter === 'active') { + departmentsCached = data || []; + fetchDepartmentsList(); + } + filterMasterDepartments(document.getElementById('master-dept-search')?.value || ''); + }) + .catch(err => { + console.error("Error fetching master departments:", err); + if (tbody) tbody.innerHTML = 'Failed to load departments.'; + }); + } + + function filterMasterDepartments(query) { + const q = (query || '').toLowerCase().trim(); + const filtered = masterDepartmentsCached.filter(d => (d.dept_name || '').toLowerCase().includes(q)); + renderMasterDepartmentsTable(filtered); + } + + function renderMasterDepartmentsTable(data) { + const tbody = document.getElementById('master-dept-tbody'); + const countBadge = document.getElementById('master-dept-count-badge'); + if (countBadge) countBadge.textContent = `${data.length} Records`; + if (!tbody) return; + + tbody.innerHTML = ''; + if (data.length === 0) { + tbody.innerHTML = `No ${masterDeptStatusFilter === 'deleted' ? 'deleted' : 'active'} departments found.`; + return; + } + + data.forEach((item, index) => { + const tr = document.createElement('tr'); + const isDeleted = (item.is_delete == 1 || item.is_active == 0); + const statusBadge = isDeleted + ? 'Deleted' + : 'Active'; + + const actionBtn = isDeleted + ? `` + : `
+ + +
`; + + tr.innerHTML = ` + ${index + 1} + ${escapeHtml(item.dept_name)} + ${statusBadge} + ${actionBtn} + `; + tbody.appendChild(tr); + }); + } + + function openEditMasterDepartmentModal(id) { + const dept = masterDepartmentsCached.find(d => String(d.id) === String(id)); + if (!dept) return; + + document.getElementById('edit-master-dept-id').value = dept.id; + document.getElementById('edit-master-dept-name').value = dept.dept_name || ''; + + const modal = new bootstrap.Modal(document.getElementById('editMasterDeptModal')); + modal.show(); + } + + function submitEditMasterDepartment() { + const id = document.getElementById('edit-master-dept-id')?.value; + const deptInput = document.getElementById('edit-master-dept-name'); + const saveBtn = document.getElementById('edit-master-dept-save-btn'); + if (!id || !deptInput) return; + + const deptName = deptInput.value.trim(); + if (!deptName) { + showNotification('warning', 'Field Required', 'Please enter a department name.'); + return; + } + + if (saveBtn) { + saveBtn.disabled = true; + saveBtn.innerHTML = ' Saving...'; + } + + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('id', id); + formData.append('department', deptName); + + fetch(contextPath + 'gcctonermanagement/api/updatedepartment', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (saveBtn) { + saveBtn.disabled = false; + saveBtn.innerHTML = ' Save Changes'; + } + if (success === true || success === 'true') { + const modalEl = document.getElementById('editMasterDeptModal'); + const modalInstance = bootstrap.Modal.getInstance(modalEl); + if (modalInstance) modalInstance.hide(); + + Swal.fire({ + icon: 'success', + title: 'Department Updated!', + text: `"${deptName}" was updated successfully.`, + confirmButtonColor: '#0f5c78' + }); + fetchMasterDepartments(); + fetchDepartmentsList(); + } else { + showNotification('error', 'Error', 'Failed to update department. Please try again.'); + } + }) + .catch(err => { + console.error("Error updating department:", err); + if (saveBtn) { + saveBtn.disabled = false; + saveBtn.innerHTML = ' Save Changes'; + } + showNotification('error', 'Error', 'Connection error while updating department.'); + }); + } + + function submitMasterDepartment() { + const deptInput = document.getElementById('master-dept-name'); + const submitBtn = document.getElementById('master-dept-submit-btn'); + if (!deptInput) return; + + const deptName = deptInput.value.trim(); + if (!deptName) { + showNotification('warning', 'Field Required', 'Please enter a department name.'); + return; + } + + if (submitBtn) { + submitBtn.disabled = true; + submitBtn.innerHTML = ' Saving...'; + } + + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('department', deptName); + + fetch(contextPath + 'gcctonermanagement/api/savedepartment', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (submitBtn) { + submitBtn.disabled = false; + submitBtn.innerHTML = ' Save Department'; + } + if (success === true || success === 'true') { + Swal.fire({ + icon: 'success', + title: 'Department Added!', + text: `"${deptName}" has been added to active departments.`, + confirmButtonColor: '#0f5c78' + }); + deptInput.value = ''; + fetchMasterDepartments(); + } else { + showNotification('error', 'Error', 'Failed to save department. Please try again.'); + } + }) + .catch(err => { + console.error("Error saving department:", err); + if (submitBtn) { + submitBtn.disabled = false; + submitBtn.innerHTML = ' Save Department'; + } + showNotification('error', 'Error', 'Connection error while saving department.'); + }); + } + + function deleteMasterDepartment(id, name) { + if (!id) return; + Swal.fire({ + title: 'Delete Department?', + text: `Are you sure you want to delete "${name}"? It will be deactivated and hidden from all dropdowns.`, + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#d33', + cancelButtonColor: '#64748b', + confirmButtonText: ' Yes, Delete', + cancelButtonText: 'Cancel' + }).then(result => { + if (result.isConfirmed) { + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('id', id); + + fetch(contextPath + 'gcctonermanagement/api/deletedepartment', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (success === true || success === 'true') { + showNotification('success', 'Deleted', `"${name}" was deleted successfully.`); + fetchMasterDepartments(); + fetchDepartmentsList(); + } else { + showNotification('error', 'Error', 'Failed to delete department.'); + } + }) + .catch(err => { + console.error("Error deleting department:", err); + showNotification('error', 'Error', 'Connection error while deleting department.'); + }); + } + }); + } + + function restoreMasterDepartment(id, name) { + if (!id) return; + Swal.fire({ + title: 'Restore Department?', + text: `Do you want to restore "${name}"? It will become active and available in all dropdowns again.`, + icon: 'question', + showCancelButton: true, + confirmButtonColor: '#10b981', + cancelButtonColor: '#64748b', + confirmButtonText: ' Yes, Restore', + cancelButtonText: 'Cancel' + }).then(result => { + if (result.isConfirmed) { + const csrf = getCsrfMeta(); + const formData = new FormData(); + formData.append('id', id); + + fetch(contextPath + 'gcctonermanagement/api/restoredepartment', { + method: 'POST', + headers: (csrf.header && csrf.token) ? { [csrf.header]: csrf.token } : {}, + body: formData + }) + .then(res => res.json()) + .then(success => { + if (success === true || success === 'true') { + showNotification('success', 'Restored', `"${name}" was restored successfully.`); + fetchMasterDepartments(); + fetchDepartmentsList(); + } else { + showNotification('error', 'Error', 'Failed to restore department.'); + } + }) + .catch(err => { + console.error("Error restoring department:", err); + showNotification('error', 'Error', 'Connection error while restoring department.'); + }); + } + }); + } + // --- HELPERS --- function escapeHtml(text) { if (!text) return ''; @@ -9022,7 +10494,7 @@