From 31c2c6906902e1b18b297d19ac6a1098d3efd167 Mon Sep 17 00:00:00 2001 From: yoga-agoy Date: Mon, 29 Jun 2026 15:55:12 +0530 Subject: [PATCH] added login page for service documents and department filter --- .../GccServiceDocumentsController.java | 129 ++++++++--- .../service/GccServiceDocumentsService.java | 114 ++++++++-- src/main/resources/application.properties | 4 +- .../templates/gccservicedocuments/login.html | 115 ++++++++++ .../templates/gccservicedocuments/upload.html | 203 +++++++++++++----- .../templates/gccservicedocuments/view.html | 115 +++++++--- 6 files changed, 548 insertions(+), 132 deletions(-) create mode 100644 src/main/resources/templates/gccservicedocuments/login.html diff --git a/src/main/java/com/gcc_manpower/gccservicedocuments/controller/GccServiceDocumentsController.java b/src/main/java/com/gcc_manpower/gccservicedocuments/controller/GccServiceDocumentsController.java index 92cdd38..63c666b 100644 --- a/src/main/java/com/gcc_manpower/gccservicedocuments/controller/GccServiceDocumentsController.java +++ b/src/main/java/com/gcc_manpower/gccservicedocuments/controller/GccServiceDocumentsController.java @@ -2,6 +2,7 @@ package com.gcc_manpower.gccservicedocuments.controller; import com.gcc_manpower.gccservicedocuments.service.GccServiceDocumentsService; import com.gcc_manpower.gccservicedocuments.service.VideoCompressionService; +import com.gcc_manpower.service.AppUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; @@ -24,20 +25,24 @@ public class GccServiceDocumentsController { @Autowired private VideoCompressionService videoCompressionService; + @Autowired + private AppUserService appUserService; + @Value("${document.upload.path:gccservicedocuments}") private String documentUploadPathConfig; @GetMapping({ "", "/" }) public String viewPortal(Model model, HttpSession session) { - // String usrName = (String) session.getAttribute("usrName"); - // String usrRole = (String) session.getAttribute("usrRole"); - - // boolean isLoggedIn = (usrName != null); - // boolean isAdmin = "ADMIN".equalsIgnoreCase(usrRole); + String usrName = (String) session.getAttribute("usrName"); + if (usrName == null) { + return "redirect:/gccservicedocuments/login"; + } + String usrRole = (String) session.getAttribute("usrRole"); + boolean isAdmin = "ADMIN".equalsIgnoreCase(usrRole); model.addAttribute("isLoggedIn", true); - model.addAttribute("isAdmin", true); - model.addAttribute("usrName", "Guest"); + model.addAttribute("isAdmin", isAdmin); + model.addAttribute("usrName", usrName); model.addAttribute("curPage", "gccservicedocuments"); return "gccservicedocuments/upload"; @@ -45,28 +50,69 @@ public class GccServiceDocumentsController { @GetMapping("/view") public String viewReportPage(Model model, HttpSession session) { - // String usrName = (String) session.getAttribute("usrName"); - // if (usrName == null) { - // return "redirect:/login"; - // } - // String usrRole = (String) session.getAttribute("usrRole"); - // boolean isAdmin = "ADMIN".equalsIgnoreCase(usrRole); + String usrName = (String) session.getAttribute("usrName"); + if (usrName == null) { + return "redirect:/gccservicedocuments/login"; + } + String usrRole = (String) session.getAttribute("usrRole"); + boolean isAdmin = "ADMIN".equalsIgnoreCase(usrRole); model.addAttribute("isLoggedIn", true); - model.addAttribute("isAdmin", true); - model.addAttribute("usrName", "Guest"); + model.addAttribute("isAdmin", isAdmin); + model.addAttribute("usrName", usrName); model.addAttribute("curPage", "gccservicedocuments_upload"); return "gccservicedocuments/view"; } + @GetMapping("/login") + public String login(HttpSession session) { + if (session.getAttribute("usrName") != null) { + return "redirect:/gccservicedocuments/view"; + } + return "gccservicedocuments/login"; + } + + @PostMapping("/api/login") + @ResponseBody + public Map validateLogin( + @RequestParam("usrName") String usrName, + @RequestParam("usrPass") String usrPass, + HttpSession session) { + + Map authResult = appUserService.authenticate(usrName, usrPass); + Map response = new HashMap<>(); + + if (Boolean.TRUE.equals(authResult.get("success"))) { + session.setAttribute("usrName", authResult.get("usrName")); + session.setAttribute("usrRole", authResult.get("usrRole")); + session.setMaxInactiveInterval(1800); // 30 minutes session timeout + response.put("result", true); + response.put("usrName", authResult.get("usrName")); + } else { + response.put("result", false); + response.put("error", authResult.get("error")); + } + + return response; + } + + @GetMapping("/logout") + public String logout(HttpSession session) { + session.removeAttribute("usrName"); + session.removeAttribute("usrRole"); + session.invalidate(); + return "redirect:/gccservicedocuments/login"; + } + @GetMapping("/api/list") @ResponseBody public List> listDocuments( - @RequestParam(value = "category", required = false) String category, - @RequestParam(value = "fromDate", required = false) String fromDate, - @RequestParam(value = "toDate", required = false) String toDate) { - return documentService.searchDocuments(category, fromDate, toDate); + @RequestParam(value = "mainCategory", required = false) String mainCategory, + @RequestParam(value = "subCategory", required = false) String subCategory, + @RequestParam(value = "fromDate", required = false) String fromDate, + @RequestParam(value = "toDate", required = false) String toDate) { + return documentService.searchDocuments(mainCategory, subCategory, fromDate, toDate); } @GetMapping("/api/categories") @@ -75,6 +121,18 @@ public class GccServiceDocumentsController { return documentService.getAllCategories(); } + @GetMapping("/api/main-categories") + @ResponseBody + public List> getMainCategories() { + return documentService.getAllMainCategories(); + } + + @GetMapping("/api/sub-categories") + @ResponseBody + public List> getSubCategories() { + return documentService.getAllSubCategories(); + } + @GetMapping("/api/file-types") @ResponseBody public List> getFileTypes() { @@ -85,18 +143,19 @@ public class GccServiceDocumentsController { @ResponseBody public Map uploadFile( @RequestParam("title") String title, - @RequestParam("category") String category, + @RequestParam("mainCategory") String mainCategory, + @RequestParam("subCategory") String subCategory, @RequestParam("fileType") String fileType, @RequestParam("file") MultipartFile file, HttpSession session) { Map response = new HashMap<>(); - // String usrName = (String) session.getAttribute("usrName"); - // if (usrName == null) { - // response.put("success", false); - // response.put("error", "Unauthorized access! Please log in."); - // return response; - // } + String usrName = (String) session.getAttribute("usrName"); + if (usrName == null) { + response.put("success", false); + response.put("error", "Unauthorized access! Please log in."); + return response; + } if (file.isEmpty()) { response.put("success", false); @@ -107,7 +166,7 @@ public class GccServiceDocumentsController { try { // Clean category name for folder names (letters, numbers, underscores, dashes, // spaces) - String cleanCategory = category.trim().replaceAll("[^a-zA-Z0-9-_ ]", "").trim(); + String cleanCategory = subCategory.trim().replaceAll("[^a-zA-Z0-9-_ ]", "").trim(); if (cleanCategory.isEmpty()) { cleanCategory = "General"; } @@ -142,7 +201,8 @@ public class GccServiceDocumentsController { boolean saved = documentService.saveDocument( title.trim(), - cleanCategory, + mainCategory, + subCategory, fileType, fileWebUrl, originalFilename); @@ -172,12 +232,13 @@ public class GccServiceDocumentsController { @ResponseBody public Map deleteFile(@RequestParam("id") Long id, HttpSession session) { Map response = new HashMap<>(); - // String usrName = (String) session.getAttribute("usrName"); - // if (usrName == null) { - // response.put("success", false); - // response.put("error", "Unauthorized access! Please log in."); - // return response; - // } + String usrName = (String) session.getAttribute("usrName"); + String usrRole = (String) session.getAttribute("usrRole"); + if (usrName == null || !"ADMIN".equalsIgnoreCase(usrRole)) { + response.put("success", false); + response.put("error", "Unauthorized access! Admin role required."); + return response; + } boolean deleted = documentService.deleteDocument(id); if (deleted) { diff --git a/src/main/java/com/gcc_manpower/gccservicedocuments/service/GccServiceDocumentsService.java b/src/main/java/com/gcc_manpower/gccservicedocuments/service/GccServiceDocumentsService.java index e076bf7..4863f1f 100644 --- a/src/main/java/com/gcc_manpower/gccservicedocuments/service/GccServiceDocumentsService.java +++ b/src/main/java/com/gcc_manpower/gccservicedocuments/service/GccServiceDocumentsService.java @@ -20,11 +20,11 @@ public class GccServiceDocumentsService { this.jdbcTemplate = new JdbcTemplate(dataSource); } - private int getOrCreateCategory(String categoryName) { + private int getOrCreateMainCategory(String mainCategoryName) { // Try parsing as ID first try { - int id = Integer.parseInt(categoryName.trim()); - String checkIdSql = "SELECT id FROM category_list WHERE id = ? AND is_delete = 0"; + int id = Integer.parseInt(mainCategoryName.trim()); + String checkIdSql = "SELECT id FROM main_category_list WHERE id = ? AND is_delete = 0"; List ids = jdbcTemplate.queryForList(checkIdSql, Integer.class, id); if (!ids.isEmpty()) { return ids.get(0); @@ -33,27 +33,65 @@ public class GccServiceDocumentsService { // Not a number, fallback to name matching } - String selectSql = "SELECT id FROM category_list WHERE category_name = ? AND is_delete = 0"; + String selectSql = "SELECT id FROM main_category_list WHERE main_category_name = ? AND is_delete = 0"; try { - List ids = jdbcTemplate.queryForList(selectSql, Integer.class, categoryName.trim()); + List ids = jdbcTemplate.queryForList(selectSql, Integer.class, mainCategoryName.trim()); if (!ids.isEmpty()) { return ids.get(0); } } catch (Exception e) { - System.err.println("Error checking category: " + e.getMessage()); + System.err.println("Error checking main category: " + e.getMessage()); } - // Insert new category - String insertSql = "INSERT INTO category_list (category_name, is_active, is_delete) VALUES (?, 1, 0)"; + // Insert new main category + String insertSql = "INSERT INTO main_category_list (main_category_name, is_active, is_delete) VALUES (?, 1, 0)"; try { - jdbcTemplate.update(insertSql, categoryName.trim()); + jdbcTemplate.update(insertSql, mainCategoryName.trim()); // Fetch the generated ID - List ids = jdbcTemplate.queryForList(selectSql, Integer.class, categoryName.trim()); + List ids = jdbcTemplate.queryForList(selectSql, Integer.class, mainCategoryName.trim()); if (!ids.isEmpty()) { return ids.get(0); } } catch (Exception e) { - System.err.println("Error creating category: " + e.getMessage()); + System.err.println("Error creating main category: " + e.getMessage()); + } + return 0; + } + + private int getOrCreateSubCategory(String subCategoryName, int mainCategoryId) { + // Try parsing as ID first + try { + int id = Integer.parseInt(subCategoryName.trim()); + String checkIdSql = "SELECT id FROM sub_category_list WHERE id = ? AND mid = ? AND is_delete = 0"; + List ids = jdbcTemplate.queryForList(checkIdSql, Integer.class, id, String.valueOf(mainCategoryId)); + if (!ids.isEmpty()) { + return ids.get(0); + } + } catch (NumberFormatException e) { + // Not a number, fallback to name matching + } + + String selectSql = "SELECT id FROM sub_category_list WHERE category_name = ? AND mid = ? AND is_delete = 0"; + try { + List ids = jdbcTemplate.queryForList(selectSql, Integer.class, subCategoryName.trim(), String.valueOf(mainCategoryId)); + if (!ids.isEmpty()) { + return ids.get(0); + } + } catch (Exception e) { + System.err.println("Error checking sub category: " + e.getMessage()); + } + + // Insert new sub category + String insertSql = "INSERT INTO sub_category_list (category_name, mid, is_active, is_delete) VALUES (?, ?, 1, 0)"; + try { + jdbcTemplate.update(insertSql, subCategoryName.trim(), String.valueOf(mainCategoryId)); + // Fetch the generated ID + List ids = jdbcTemplate.queryForList(selectSql, Integer.class, subCategoryName.trim(), String.valueOf(mainCategoryId)); + if (!ids.isEmpty()) { + return ids.get(0); + } + } catch (Exception e) { + System.err.println("Error creating sub category: " + e.getMessage()); } return 0; } @@ -96,16 +134,23 @@ public class GccServiceDocumentsService { return 0; } - public boolean saveDocument(String title, String category, String fileType, String filePath, + public boolean saveDocument(String title, String mainCategory, String subCategory, String fileType, String filePath, String originalFilename) { - int categoryId = getOrCreateCategory(category); + int mainCategoryId = getOrCreateMainCategory(mainCategory); + if (mainCategoryId == 0) { + return false; + } + int subCategoryId = getOrCreateSubCategory(subCategory, mainCategoryId); + if (subCategoryId == 0) { + return false; + } int fileTypeId = getOrCreateFileType(fileType); String sql = "INSERT INTO documents_uploads (title, category, file_type, file_path, original_filename, is_active, is_delete) " + "VALUES (?, ?, ?, ?, ?, 1, 0)"; try { - int rows = jdbcTemplate.update(sql, title, String.valueOf(categoryId), String.valueOf(fileTypeId), filePath, + int rows = jdbcTemplate.update(sql, title, String.valueOf(subCategoryId), String.valueOf(fileTypeId), filePath, originalFilename); return rows > 0; } catch (Exception e) { @@ -114,19 +159,24 @@ public class GccServiceDocumentsService { } } - public List> searchDocuments(String category, String fromDate, String toDate) { + public List> searchDocuments(String mainCategory, String subCategory, String fromDate, String toDate) { StringBuilder sql = new StringBuilder( - "SELECT d.id, d.title, c.category_name AS category, f.file_type_name AS file_type, d.file_path, d.original_filename, d.upload_date " + "SELECT d.id, d.title, s.category_name AS category, m.main_category_name AS main_category, f.file_type_name AS file_type, d.file_path, d.original_filename, d.upload_date " + "FROM documents_uploads d " + - "LEFT JOIN category_list c ON d.category = c.id " + + "LEFT JOIN sub_category_list s ON d.category = s.id " + + "LEFT JOIN main_category_list m ON s.mid = m.id " + "LEFT JOIN upload_file_types f ON d.file_type = f.id " + "WHERE d.is_active = 1 AND d.is_delete = 0"); List params = new ArrayList<>(); - if (category != null && !category.trim().isEmpty()) { - sql.append(" AND c.category_name = ?"); - params.add(category.trim()); + if (mainCategory != null && !mainCategory.trim().isEmpty()) { + sql.append(" AND m.main_category_name = ?"); + params.add(mainCategory.trim()); + } + if (subCategory != null && !subCategory.trim().isEmpty()) { + sql.append(" AND s.category_name = ?"); + params.add(subCategory.trim()); } if (fromDate != null && !fromDate.trim().isEmpty()) { sql.append(" AND DATE(d.upload_date) >= ?"); @@ -137,7 +187,7 @@ public class GccServiceDocumentsService { params.add(toDate.trim()); } - sql.append(" ORDER BY d.category "); + sql.append(" ORDER BY m.main_category_name, s.category_name "); try { return jdbcTemplate.queryForList(sql.toString(), params.toArray()); @@ -159,7 +209,7 @@ public class GccServiceDocumentsService { } public List getAllCategories() { - String sql = "SELECT category_name FROM category_list WHERE is_active = 1 AND is_delete = 0 ORDER BY category_name"; + String sql = "SELECT category_name FROM sub_category_list WHERE is_active = 1 AND is_delete = 0 ORDER BY category_name"; try { return jdbcTemplate.queryForList(sql, String.class); } catch (Exception e) { @@ -168,6 +218,26 @@ public class GccServiceDocumentsService { } } + public List> getAllMainCategories() { + String sql = "SELECT id, main_category_name FROM main_category_list WHERE is_active = 1 AND is_delete = 0 ORDER BY main_category_name"; + try { + return jdbcTemplate.queryForList(sql); + } catch (Exception e) { + System.err.println("Error fetching main categories: " + e.getMessage()); + return new ArrayList<>(); + } + } + + public List> getAllSubCategories() { + String sql = "SELECT id, category_name, mid FROM sub_category_list WHERE is_active = 1 AND is_delete = 0 ORDER BY category_name"; + try { + return jdbcTemplate.queryForList(sql); + } catch (Exception e) { + System.err.println("Error fetching sub categories: " + e.getMessage()); + return new ArrayList<>(); + } + } + public List> getAllFileTypes() { String sql = "SELECT id, file_type_name FROM upload_file_types WHERE is_active = 1 AND is_delete = 0 ORDER BY file_type_name"; try { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7197b79..4687540 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,5 +1,5 @@ -spring.profiles.active=local -#spring.profiles.active=prod +#spring.profiles.active=local +spring.profiles.active=prod spring.application.name=gcc-manpower server.servlet.session.timeout=10m \ No newline at end of file diff --git a/src/main/resources/templates/gccservicedocuments/login.html b/src/main/resources/templates/gccservicedocuments/login.html new file mode 100644 index 0000000..6e503ac --- /dev/null +++ b/src/main/resources/templates/gccservicedocuments/login.html @@ -0,0 +1,115 @@ + + + + + + + GCC Service Documents - Login + + + + + + + + + + + + + +
+ +

GREATER CHENNAI CORPORATION

+

பெருநகர சென்னை மாநகராட்சி

+
+

GCC Training Services Documents

+
+

Login

+ + + +
+
+ +
+
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/gccservicedocuments/upload.html b/src/main/resources/templates/gccservicedocuments/upload.html index 0895f39..291e35e 100644 --- a/src/main/resources/templates/gccservicedocuments/upload.html +++ b/src/main/resources/templates/gccservicedocuments/upload.html @@ -271,10 +271,24 @@ Upload Documents - - - Back to View Report - + @@ -288,26 +302,35 @@ placeholder="Enter a descriptive title"> - +
-
+
-
- - + + + +
- - +
+ + +
@@ -358,26 +381,109 @@ const contextPath = /*[[@{/}]]*/ ''; let selectedFile = null; + let allSubCategories = []; + document.addEventListener('DOMContentLoaded', () => { - loadExistingCategories(); + loadCategoriesAndSubcategories(); loadFileTypes(); setupDragAndDrop(); }); - function loadExistingCategories() { - fetch(contextPath + 'gccservicedocuments/api/categories') + function loadCategoriesAndSubcategories() { + // Load main categories + fetch(contextPath + 'gccservicedocuments/api/main-categories') .then(res => res.json()) - .then(categories => { - const select = document.getElementById('category-select'); - // Add items before the last child option (+ Add Custom Category...) - categories.forEach(cat => { + .then(mainCats => { + const select = document.getElementById('main-category-select'); + // Add items before the last child option (+ Add Custom Main Category...) + mainCats.forEach(cat => { const opt = document.createElement('option'); - opt.value = cat; - opt.textContent = cat; + opt.value = cat.id; + opt.textContent = cat.main_category_name; select.insertBefore(opt, select.lastElementChild); }); }) - .catch(err => console.error("Error loading categories:", err)); + .catch(err => console.error("Error loading main categories:", err)); + + // Load subcategories + fetch(contextPath + 'gccservicedocuments/api/sub-categories') + .then(res => res.json()) + .then(subCats => { + allSubCategories = subCats; + onMainCategoryChange(); + }) + .catch(err => console.error("Error loading subcategories:", err)); + } + + function onMainCategoryChange() { + const mainSelect = document.getElementById('main-category-select'); + const subSelect = document.getElementById('subcategory-select'); + const customMainInput = document.getElementById('custom-main-category-input'); + const customSubInput = document.getElementById('custom-subcategory-input'); + + subSelect.innerHTML = ''; + subSelect.disabled = true; + + if (mainSelect.value === '__NEW__') { + customMainInput.style.display = 'block'; + customMainInput.required = true; + customMainInput.focus(); + + const optNew = document.createElement('option'); + optNew.value = '__NEW__'; + optNew.textContent = '+ Add Custom Subcategory...'; + subSelect.appendChild(optNew); + subSelect.value = '__NEW__'; + subSelect.disabled = false; + + customSubInput.style.display = 'block'; + customSubInput.required = true; + } else if (mainSelect.value === '') { + customMainInput.style.display = 'none'; + customMainInput.required = false; + customMainInput.value = ''; + + customSubInput.style.display = 'none'; + customSubInput.required = false; + customSubInput.value = ''; + } else { + customMainInput.style.display = 'none'; + customMainInput.required = false; + customMainInput.value = ''; + + const selectedMid = mainSelect.value; + const filtered = allSubCategories.filter(sub => String(sub.mid) === String(selectedMid)); + + filtered.forEach(sub => { + const opt = document.createElement('option'); + opt.value = sub.id; + opt.textContent = sub.category_name; + subSelect.appendChild(opt); + }); + + const optNew = document.createElement('option'); + optNew.value = '__NEW__'; + optNew.textContent = '+ Add Custom Subcategory...'; + subSelect.appendChild(optNew); + + subSelect.disabled = false; + onSubCategoryChange(); + } + } + + function onSubCategoryChange() { + const subSelect = document.getElementById('subcategory-select'); + const customSubInput = document.getElementById('custom-subcategory-input'); + + if (subSelect.value === '__NEW__') { + customSubInput.style.display = 'block'; + customSubInput.required = true; + customSubInput.focus(); + } else { + customSubInput.style.display = 'none'; + customSubInput.required = false; + customSubInput.value = ''; + } } function loadFileTypes() { @@ -420,21 +526,6 @@ } } - function toggleCustomCategoryInput() { - const select = document.getElementById('category-select'); - const customInput = document.getElementById('custom-category-input'); - - if (select.value === '__NEW__') { - customInput.style.display = 'block'; - customInput.required = true; - customInput.focus(); - } else { - customInput.style.display = 'none'; - customInput.required = false; - customInput.value = ''; - } - } - function setupDragAndDrop() { const dropzone = document.getElementById('dropzone'); @@ -553,20 +644,31 @@ const title = document.getElementById('title-input').value.trim(); const type = document.getElementById('type-select').value; - const categorySelect = document.getElementById('category-select').value; - const customCategory = document.getElementById('custom-category-input').value.trim(); + const mainSelect = document.getElementById('main-category-select').value; + const subSelect = document.getElementById('subcategory-select').value; + const customMain = document.getElementById('custom-main-category-input').value.trim(); + const customSub = document.getElementById('custom-subcategory-input').value.trim(); + + let mainCategory = mainSelect; + if (mainSelect === '__NEW__') { + if (!customMain) { + alert("Please specify a custom main category name."); + return; + } + mainCategory = customMain; + } - let category = categorySelect; - if (categorySelect === '__NEW__') { - if (!customCategory) { - alert("Please specify a custom category name."); + let subCategory = subSelect; + if (subSelect === '__NEW__') { + if (!customSub) { + alert("Please specify a custom subcategory name."); return; } - category = customCategory; + subCategory = customSub; } - if (!category) { - alert("Please select or type a category."); + if (!mainCategory || !subCategory) { + alert("Please select or specify both Main Category and Subcategory."); return; } @@ -577,7 +679,8 @@ const formData = new FormData(); formData.append('title', title); formData.append('fileType', type); - formData.append('category', category); + formData.append('mainCategory', mainCategory); + formData.append('subCategory', subCategory); formData.append('file', selectedFile); const xhr = new XMLHttpRequest(); diff --git a/src/main/resources/templates/gccservicedocuments/view.html b/src/main/resources/templates/gccservicedocuments/view.html index 33a30f9..d561922 100644 --- a/src/main/resources/templates/gccservicedocuments/view.html +++ b/src/main/resources/templates/gccservicedocuments/view.html @@ -571,11 +571,25 @@ Service Documents & Videos - - - - Upload Documents - +
+ +
+ + User +
+ + + + + Upload Documents + + + + + + Logout + +
@@ -587,11 +601,19 @@ - + +
+ + +
+ +
- - +
@@ -655,6 +677,7 @@ S.No Title + Subcategory File Type Upload Date Original Filename @@ -734,19 +757,56 @@ }); } + let allFilterSubcategories = []; + function loadCategories() { - fetch(contextPath + 'gccservicedocuments/api/categories') + fetch(contextPath + 'gccservicedocuments/api/main-categories') .then(response => response.json()) - .then(categories => { - const select = document.getElementById('filter-category'); - categories.forEach(category => { + .then(mainCats => { + const select = document.getElementById('filter-main-category'); + mainCats.forEach(cat => { const option = document.createElement('option'); - option.value = category; - option.textContent = category; + option.value = cat.main_category_name; + option.setAttribute('data-id', cat.id); + option.textContent = cat.main_category_name; select.appendChild(option); }); }) - .catch(err => console.error("Error loading categories:", err)); + .catch(err => console.error("Error loading main categories:", err)); + + fetch(contextPath + 'gccservicedocuments/api/sub-categories') + .then(response => response.json()) + .then(subCats => { + allFilterSubcategories = subCats; + populateSubcategoryFilter(); + }) + .catch(err => console.error("Error loading subcategories:", err)); + } + + function populateSubcategoryFilter(filteredSubs) { + const select = document.getElementById('filter-subcategory'); + select.innerHTML = ''; + + const subs = filteredSubs || allFilterSubcategories; + subs.forEach(sub => { + const option = document.createElement('option'); + option.value = sub.category_name; + option.textContent = sub.category_name; + select.appendChild(option); + }); + } + + function onMainCategoryFilterChange() { + const mainSelect = document.getElementById('filter-main-category'); + const selectedOption = mainSelect.options[mainSelect.selectedIndex]; + const mainId = selectedOption ? selectedOption.getAttribute('data-id') : null; + + if (!mainId) { + populateSubcategoryFilter(); + } else { + const filtered = allFilterSubcategories.filter(sub => String(sub.mid) === String(mainId)); + populateSubcategoryFilter(filtered); + } } function loadDocuments() { @@ -763,7 +823,7 @@ function groupAndRenderCategories(documents) { const grouped = {}; documents.forEach(doc => { - const cat = doc.category || 'General'; + const cat = doc.main_category || 'General'; if (!grouped[cat]) { grouped[cat] = []; } @@ -852,7 +912,8 @@ function applyFilters() { const searchQuery = document.getElementById('search-title').value.toLowerCase().trim(); - const categoryQuery = document.getElementById('filter-category').value; + const mainCategoryQuery = document.getElementById('filter-main-category').value; + const subCategoryQuery = document.getElementById('filter-subcategory').value; const fromDateVal = document.getElementById('filter-from-date').value.trim(); const toDateVal = document.getElementById('filter-to-date').value.trim(); @@ -860,7 +921,7 @@ const toDateQuery = parseInputDate(toDateVal); // Validation logic: if no filter is selected, show Swal warning - if (!searchQuery && !categoryQuery && !fromDateVal && !toDateVal) { + if (!searchQuery && !mainCategoryQuery && !subCategoryQuery && !fromDateVal && !toDateVal) { Swal.fire({ title: 'Filter Selection Required', text: 'Please select at least one filter before searching.', @@ -888,8 +949,11 @@ // Title filter const matchesTitle = doc.title.toLowerCase().includes(searchQuery); - // Category filter - const matchesCategory = !categoryQuery || doc.category === categoryQuery; + // Main Category filter + const matchesMainCategory = !mainCategoryQuery || doc.main_category === mainCategoryQuery; + + // Subcategory filter + const matchesSubCategory = !subCategoryQuery || doc.category === subCategoryQuery; // Date filter let matchesDate = true; @@ -903,7 +967,7 @@ } } - return matchesTitle && matchesCategory && matchesDate; + return matchesTitle && matchesMainCategory && matchesSubCategory && matchesDate; }); groupAndRenderCategories(filteredDocuments); @@ -915,7 +979,9 @@ showLoadingOverlay("Resetting Filters..."); setTimeout(() => { document.getElementById('search-title').value = ''; - document.getElementById('filter-category').value = ''; + document.getElementById('filter-main-category').value = ''; + document.getElementById('filter-subcategory').value = ''; + populateSubcategoryFilter(); if (fromPicker) fromPicker.clear(); if (toPicker) toPicker.clear(); filteredDocuments = allDocuments; @@ -928,7 +994,7 @@ currentModalCategoryName = categoryName; document.getElementById('modal-category-name').textContent = categoryName; - currentCategoryFiles = filteredDocuments.filter(d => d.category === categoryName); + currentCategoryFiles = filteredDocuments.filter(d => (d.main_category || 'General') === categoryName); document.getElementById('modal-file-search-input').value = ''; const modalElement = document.getElementById('categoryFilesModal'); @@ -989,6 +1055,7 @@ tr.innerHTML = ` ${index + 1} ${escapeHtml(doc.title)} + ${escapeHtml(doc.category || 'General')}