From 59798f72f128c6f2fa330802fa63c374593de541 Mon Sep 17 00:00:00 2001 From: iris Date: Fri, 5 Jun 2026 11:41:57 +0200 Subject: [PATCH] fix(dashboard): auto-reset stale per-agent questions filter on re-render MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the operator had an agent:foo filter active and all of foo's questions were resolved, foo's chip disappears from the filter row on the next render — but the stored filter value is still agent:foo. The section then shows "no questions match this filter" with no active chip visible, leaving the operator confused. Fix: compute the set of valid filter values (all, operator, peer, plus one agent: per current participant) before rendering. If the stored value is not in the set, silently reset it to 'all'. Write directly to localStorage rather than via setQuestionsFilter() to avoid a re-entrant renderQuestions() call. --- frontend/packages/dashboard/src/tabs.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/frontend/packages/dashboard/src/tabs.js b/frontend/packages/dashboard/src/tabs.js index 60feefbb..c9ef2aa6 100644 --- a/frontend/packages/dashboard/src/tabs.js +++ b/frontend/packages/dashboard/src/tabs.js @@ -1574,8 +1574,6 @@ window.marked = marked; root.replaceChildren(); const fmt = (n) => new Date(n * 1000).toISOString().replace('T', ' ').slice(0, 19); const allPending = questionsState.pending; - const activeFilter = getQuestionsFilter(); - const pending = allPending.filter((q) => questionMatchesFilter(q, activeFilter)); // Filter chips. Always include `all` / `operator` / `peer`; add // per-agent chips for any agent that appears as asker or target @@ -1586,6 +1584,24 @@ window.marked = marked; participants.add(q.asker); if (q.target) participants.add(q.target); } + + // Auto-reset a stale per-agent filter: if the operator had `agent:foo` + // selected and all of foo's questions resolved, foo's chip disappears + // from the row. Without a reset the section would show "no questions + // match this filter" with no active chip visible — confusing. Fall back + // to `all` whenever the stored value is no longer a valid chip value. + let activeFilter = getQuestionsFilter(); + const validFilters = new Set(['all', 'operator', 'peer', + ...Array.from(participants).map((n) => 'agent:' + n)]); + if (!validFilters.has(activeFilter)) { + activeFilter = 'all'; + // Write directly to localStorage to avoid the re-render that + // setQuestionsFilter() triggers (we're already mid-render). + localStorage.setItem(QUESTIONS_FILTER_KEY, 'all'); + } + + const pending = allPending.filter((q) => questionMatchesFilter(q, activeFilter)); + const filterRow = el('div', { class: 'questions-filters' }); const mkChip = (value, label) => { const b = el('button', {