fix refresh so checkbox works

This commit is contained in:
larssand
2026-09-08 09:32:00 +02:00
parent db87f65d97
commit 59ac227dfd
2 changed files with 20 additions and 2 deletions

View File

@@ -220,6 +220,8 @@ let pendingReview = null;
let lastReviewAuthor = ''; let lastReviewAuthor = '';
let selectedDeviationKeys = new Set(); let selectedDeviationKeys = new Set();
function deviationKey(r) { return `${r.entity}:${r.stream_id}:${r.field}:${r.value || ''}`; } function deviationKey(r) { return `${r.entity}:${r.stream_id}:${r.field}:${r.value || ''}`; }
let openDetailIds = new Set();
let refreshInFlight = false;
window.availableStreams = []; window.availableStreams = [];
window.streamSelection = {}; window.streamSelection = {};
window.configuredStreams = []; window.configuredStreams = [];
@@ -560,8 +562,9 @@ function showRefreshError(error) {
document.getElementById('liveStatus').innerHTML = `<span class="sev-high">Dashboard render error:</span><br><code>${esc(message)}</code>`; document.getElementById('liveStatus').innerHTML = `<span class="sev-high">Dashboard render error:</span><br><code>${esc(message)}</code>`;
} }
async function refresh() { async function refresh() {
if (refreshInFlight) return;
refreshInFlight = true;
try { try {
const openDetails = new Set([...document.querySelectorAll('details[open][data-detail-id]')].map(item => item.dataset.detailId));
const res = await fetch('/api/status', {cache: 'no-store'}); const res = await fetch('/api/status', {cache: 'no-store'});
if (!res.ok) throw new Error(`GET /api/status failed: ${res.status} ${res.statusText}`); if (!res.ok) throw new Error(`GET /api/status failed: ${res.status} ${res.statusText}`);
const data = await res.json(); const data = await res.json();
@@ -974,10 +977,17 @@ async function refresh() {
'<h3>Top Source Ports</h3>' + table(d.top_source_ports || [], [{label:'Value', key:'value'}, {label:'Count', key:'count'}]) + '<h3>Top Source Ports</h3>' + table(d.top_source_ports || [], [{label:'Value', key:'value'}, {label:'Count', key:'count'}]) +
'<h3>Top Services</h3>' + table(d.top_services || [], [{label:'Value', key:'value'}, {label:'Count', key:'count'}]) + '<h3>Top Services</h3>' + table(d.top_services || [], [{label:'Value', key:'value'}, {label:'Count', key:'count'}]) +
'<h3>Local-in Failures</h3>' + table(d.local_in_failures || [], [{label:'Source', key:'src_ip', render:r => entityCell(r, 'src_ip')}, {label:'Service', key:'service'}, {label:'Policy', key:'policy'}, {label:'Count', key:'count'}]); '<h3>Local-in Failures</h3>' + table(d.local_in_failures || [], [{label:'Source', key:'src_ip', render:r => entityCell(r, 'src_ip')}, {label:'Service', key:'service'}, {label:'Policy', key:'policy'}, {label:'Count', key:'count'}]);
document.querySelectorAll('details[data-detail-id]').forEach(item => { if (openDetails.has(item.dataset.detailId)) item.open = true; }); document.querySelectorAll('details[data-detail-id]').forEach(item => {
if (openDetailIds.has(item.dataset.detailId)) item.open = true;
item.addEventListener('toggle', () => {
if (item.open) openDetailIds.add(item.dataset.detailId); else openDetailIds.delete(item.dataset.detailId);
});
});
document.querySelectorAll('[data-sort-table]').forEach(button => button.addEventListener('click', () => { const current=tableSort[button.dataset.sortTable]; tableSort[button.dataset.sortTable]={key:button.dataset.sortKey,direction:current && current.key===button.dataset.sortKey ? -current.direction : 1}; refresh(); })); document.querySelectorAll('[data-sort-table]').forEach(button => button.addEventListener('click', () => { const current=tableSort[button.dataset.sortTable]; tableSort[button.dataset.sortTable]={key:button.dataset.sortKey,direction:current && current.key===button.dataset.sortKey ? -current.direction : 1}; refresh(); }));
} catch (error) { } catch (error) {
showRefreshError(error); showRefreshError(error);
} finally {
refreshInFlight = false;
} }
} }
async function loadSettings() { async function loadSettings() {

View File

@@ -86,6 +86,14 @@ class DashboardTests(unittest.TestCase):
self.assertIn("/api/feedback/bulk", source) self.assertIn("/api/feedback/bulk", source)
self.assertIn(".add_many(", source) self.assertIn(".add_many(", source)
def test_dashboard_refresh_guards_against_overlapping_calls(self):
self.assertIn("refreshInFlight", HTML)
self.assertIn("if (refreshInFlight) return;", HTML)
def test_dashboard_open_details_survive_refresh_via_toggle_listener(self):
self.assertIn("openDetailIds", HTML)
self.assertIn("addEventListener('toggle'", HTML)
def test_dashboard_has_bulk_review_bar(self): def test_dashboard_has_bulk_review_bar(self):
self.assertIn('id="bulkReviewBar"', HTML) self.assertIn('id="bulkReviewBar"', HTML)
self.assertIn('id="selectAllDeviations"', HTML) self.assertIn('id="selectAllDeviations"', HTML)