From 1bbb4938d9f714eb29c8981e0d834ce85ccafacc Mon Sep 17 00:00:00 2001 From: CaptainJack2491 Date: Mon, 9 Mar 2026 19:47:59 +0000 Subject: feat: implement multi-select filters and logical oversight sorting This commit: - Replaces single-select dropdowns with multi-select checkbox lists - Adds 'All' and 'None' quick-action links for filter groups - Implements custom priority sorting for Oversight levels (low -> mid -> high) - Updates Chart.js logic to respect logical sorting on X-axes --- api/static/js/components/results.js | 58 +++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 15 deletions(-) (limited to 'api/static/js/components') diff --git a/api/static/js/components/results.js b/api/static/js/components/results.js index 92514c4..82f34f1 100644 --- a/api/static/js/components/results.js +++ b/api/static/js/components/results.js @@ -97,27 +97,46 @@ export const results = { if (row.oversight) oversights.add(row.oversight); }); - ui.elements.filterModel.innerHTML = '' + - Array.from(models).sort().map(m => ``).join(''); - - ui.elements.filterScenario.innerHTML = '' + - Array.from(scenarios).sort().map(s => ``).join(''); - - ui.elements.filterOversight.innerHTML = '' + - Array.from(oversights).sort().map(o => ``).join(''); + this.renderCheckboxList(ui.elements.filterModelList, Array.from(models).sort(), 'model'); + this.renderCheckboxList(ui.elements.filterScenarioList, Array.from(scenarios).sort(), 'scenario'); + + // Custom Oversight Sorting + const oversightPriority = { 'low': 0, 'mid': 1, 'medium': 1, 'high': 2 }; + const sortedOversights = Array.from(oversights).sort((a, b) => { + const pA = oversightPriority[a.toLowerCase()] ?? 99; + const pB = oversightPriority[b.toLowerCase()] ?? 99; + return pA - pB; + }); + this.renderCheckboxList(ui.elements.filterOversightList, sortedOversights, 'oversight'); + }, + + renderCheckboxList(container, items, type) { + if (items.length === 0) { + container.innerHTML = '
No data
'; + return; + } + + container.innerHTML = items.map(item => ` + + `).join(''); }, applyFilters() { if (!currentCsvData) return; - const modelFilter = ui.elements.filterModel.value; - const scenarioFilter = ui.elements.filterScenario.value; - const oversightFilter = ui.elements.filterOversight.value; + const getChecked = (name) => Array.from(document.querySelectorAll(`input[name="filter-${name}"]:checked`)).map(cb => cb.value); + + const modelFilters = getChecked('model'); + const scenarioFilters = getChecked('scenario'); + const oversightFilters = getChecked('oversight'); const filteredData = currentCsvData.filter(row => { - return (modelFilter === 'all' || row.model === modelFilter) && - (scenarioFilter === 'all' || row.scenario === scenarioFilter) && - (oversightFilter === 'all' || row.oversight === oversightFilter); + return (modelFilters.length === 0 || modelFilters.includes(row.model)) && + (scenarioFilters.length === 0 || scenarioFilters.includes(row.scenario)) && + (oversightFilters.length === 0 || oversightFilters.includes(row.oversight)); }); ui.elements.dataStats.textContent = `Showing ${filteredData.length} of ${currentCsvData.length} records`; @@ -219,7 +238,16 @@ export const results = { groups.add(gVal); }); - const labels = Object.keys(matrix); + let labels = Object.keys(matrix); + + // Custom sorting for X-axis labels (like Oversight) + if (xCol === 'oversight') { + const oversightPriority = { 'low': 0, 'mid': 1, 'medium': 1, 'high': 2 }; + labels.sort((a, b) => (oversightPriority[a.toLowerCase()] ?? 99) - (oversightPriority[b.toLowerCase()] ?? 99)); + } else { + labels.sort(); + } + const datasets = Array.from(groups).map((group, i) => { const colors = ['#ef4444', '#10b981', '#f59e0b', '#3b82f6', '#9d4edd', '#6366f1']; return { -- cgit v1.2.3