fix(web): category master switch reads as partial, not off, when only some agents are deselected

Unchecking one agent in an 8-agent category (7/8 left on) rendered the
category header switch fully unchecked — visually indistinguishable from
'category disabled', even though 7 of 8 agents were still on. The
checkbox's checked state only had two positions; a partial selection had
nowhere to render but off.

Fix: set the master switch's .indeterminate property when 0 < selected <
total, with its own CSS state (grey track, thumb parked halfway) instead
of the on/off track+thumb. Clicking a checkbox out of indeterminate
selects everything, per browser default — unchanged behavior, just an
honest visual for the in-between state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129WdYHccPsH27k5GGuwijd
This commit is contained in:
CyberSecurityUP
2026-08-23 14:27:27 -03:00
co-authored by Claude Sonnet 5
parent 3dcfeb7377
commit 040170e54a
2 changed files with 10 additions and 1 deletions
+7 -1
View File
@@ -183,7 +183,13 @@ function renderBoard() {
if (e.target.closest('.switch')) return;
card.classList.toggle('collapsed');
});
card.querySelector('.cat-toggle').addEventListener('change', (e) => {
const catToggle = card.querySelector('.cat-toggle');
// A partial selection (some but not all agents on) must look "partial",
// not "off" — an unchecked master switch reads as "category disabled"
// even when most of its agents are still on. Indeterminate = the middle
// state; clicking it from there selects everything (browser default).
catToggle.indeterminate = selCount > 0 && selCount < group.agents.length;
catToggle.addEventListener('change', (e) => {
const on = e.target.checked;
for (const a of group.agents) { if (on) state.selected.add(a.id); else state.selected.delete(a.id); }
renderBoard();
+3
View File
@@ -253,6 +253,9 @@ textarea { resize: vertical; min-height: 72px; }
.switch .thumb { position: absolute; top: 2px; left: 2px; width: 13px; height: 13px; border-radius: 50%; background: var(--surface); transition: transform .15s; box-shadow: 0 1px 2px rgba(0,0,0,.25); }
.switch input:checked + .track { background: var(--accent); }
.switch input:checked + .track + .thumb { transform: translateX(13px); }
/* partial selection (some agents on, not all) — reads as "partial", not "off" */
.switch input:indeterminate + .track { background: var(--border-strong); }
.switch input:indeterminate + .track + .thumb { transform: translateX(7px); background: var(--accent); }
.custom-leads { display: flex; flex-direction: column; gap: var(--sp-2); }
.custom-lead-chip { display: flex; align-items: center; gap: var(--sp-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px var(--sp-3); font-size: 12px; background: var(--surface-2); }