document.addEventListener("DOMContentLoaded", () => { document.querySelectorAll(".table-filterable").forEach((container) => { const table = container.querySelector(".data-table"); if (!table) return; const thead = table.querySelector("thead"); const headerRow = thead.querySelector("tr"); const headerCells = Array.from(headerRow.querySelectorAll("th")); const emptyNotice = container.querySelector(".table-filter-empty"); const rows = Array.from(table.querySelectorAll("tbody tr")); const activeFilters = new Map(); function getCellValue(cell, colName) { if (colName === "severity_icon" || colName === "status") { return (cell.querySelector(".status-badge")?.title || "").toLowerCase(); } return cell.textContent.trim().toLowerCase(); } function applyFilters() { let visibleCount = 0; rows.forEach((row) => { const cells = row.querySelectorAll("td"); let visible = true; activeFilters.forEach((filterValue, colIndex) => { if (!filterValue) return; const cell = cells[colIndex]; if (!cell) { visible = false; return; } const colName = headerCells[colIndex]?.dataset.col || ""; if (!getCellValue(cell, colName).includes(filterValue)) visible = false; }); row.hidden = !visible; if (visible) visibleCount++; }); if (emptyNotice) emptyNotice.hidden = visibleCount > 0; } const filterRow = document.createElement("tr"); filterRow.className = "filter-row"; headerCells.forEach((th, colIndex) => { const colName = th.dataset.col || ""; const filterTh = document.createElement("th"); if (th.className) filterTh.className = th.className; const isIconCol = colName === "severity_icon" || colName === "status"; if (isIconCol) { const select = document.createElement("select"); select.className = "col-filter col-filter-select"; select.setAttribute("aria-label", "filter " + (th.getAttribute("aria-label") || colName)); const allOpt = document.createElement("option"); allOpt.value = ""; allOpt.textContent = "All"; select.appendChild(allOpt); const seen = new Set(); rows.forEach((row) => { const cell = row.querySelectorAll("td")[colIndex]; if (!cell) return; const val = cell.querySelector(".status-badge")?.title || ""; if (val && !seen.has(val)) { seen.add(val); const opt = document.createElement("option"); opt.value = val.toLowerCase(); opt.textContent = val; select.appendChild(opt); } }); if (select.options.length > 1) { select.addEventListener("change", () => { activeFilters.set(colIndex, select.value); applyFilters(); }); filterTh.appendChild(select); } } else if (colName) { const input = document.createElement("input"); input.type = "text"; input.className = "col-filter col-filter-text"; input.setAttribute("aria-label", "filter " + colName); let debounceTimer; input.addEventListener("input", () => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { activeFilters.set(colIndex, input.value.trim().toLowerCase()); applyFilters(); }, 300); }); filterTh.appendChild(input); } filterRow.appendChild(filterTh); }); thead.appendChild(filterRow); }); });