From 90f3c472b553bef49617fc1d39b5aa6c8eddc957 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sun, 7 Jun 2026 14:47:53 +0000 Subject: [PATCH] =?UTF-8?q?fix(goldset):=20single=20view-mode=20filter=20?= =?UTF-8?q?=E2=80=94=20can't=20get=20stuck=20hiding=20untagged?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old independent toggles had a trap: clicking "אי-הסכמות AI" set a filter, and once all disagreements were resolved the toggle button disappeared (rendered only when count>0) while the filter stayed ON — so the list showed zero items and the untagged ones were unreachable. Replaced hideTagged + disagreeOnly with one mutually-exclusive segmented control: הכל / לא תויגו / תויגו / ⚠ אי-הסכמות, each with a live count and always visible. No stuck state; "לא תויגו" makes the remaining work obvious. Verified: tsc --noEmit 0. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/components/goldset/goldset-panel.tsx | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/web-ui/src/components/goldset/goldset-panel.tsx b/web-ui/src/components/goldset/goldset-panel.tsx index 0bd2ebe..5144440 100644 --- a/web-ui/src/components/goldset/goldset-panel.tsx +++ b/web-ui/src/components/goldset/goldset-panel.tsx @@ -347,13 +347,16 @@ export function GoldsetPanel() { const tag = useTagGoldset(batch); const createSample = useCreateGoldsetSample(batch); const [focusedId, setFocusedId] = useState(null); - const [hideTagged, setHideTagged] = useState(false); - const [disagreeOnly, setDisagreeOnly] = useState(false); + // Single mutually-exclusive view mode — can't get "stuck" like the old + // independent toggles (where the disagree filter hid the untagged items). + const [viewMode, setViewMode] = + useState<"all" | "untagged" | "tagged" | "disagree">("all"); const [sourceFilter, setSourceFilter] = useState<"all" | "court_ruling" | "appeals_committee">("all"); const items = useMemo(() => data?.items ?? [], [data]); const taggedCount = items.filter(isTagged).length; + const untaggedCount = items.length - taggedCount; const disagreeCount = items.filter(aiDisagrees).length; const sourceCounts = useMemo(() => ({ court_ruling: items.filter((i) => i.source_type === "court_ruling").length, @@ -362,13 +365,14 @@ export function GoldsetPanel() { const visible = useMemo(() => { let v = items; if (sourceFilter !== "all") v = v.filter((i) => i.source_type === sourceFilter); - if (hideTagged) v = v.filter((i) => !isTagged(i)); - if (disagreeOnly) v = v.filter(aiDisagrees); + if (viewMode === "untagged") v = v.filter((i) => !isTagged(i)); + else if (viewMode === "tagged") v = v.filter(isTagged); + else if (viewMode === "disagree") v = v.filter(aiDisagrees); // group-sort: כל פסקי-הדין יחד, ואז כל החלטות ועדת-הערר (הפרדה ברורה). const order = (s: string | null) => s === "court_ruling" ? 0 : s === "appeals_committee" ? 1 : 2; return [...v].sort((a, b) => order(a.source_type) - order(b.source_type)); - }, [items, hideTagged, sourceFilter, disagreeOnly]); + }, [items, viewMode, sourceFilter]); const focused = focusedId ? visible.find((i) => i.id === focusedId) ?? null : null; @@ -467,16 +471,23 @@ export function GoldsetPanel() { {" "}· הלכה H / לא N {" "}· ציטוט שלם C / קטוע X - {disagreeCount > 0 && ( - - )} - +
+ {([ + { v: "all", label: `הכל (${items.length})` }, + { v: "untagged", label: `לא תויגו (${untaggedCount})` }, + { v: "tagged", label: `תויגו (${taggedCount})` }, + { v: "disagree", label: `⚠ אי-הסכמות (${disagreeCount})` }, + ] as const).map((m) => ( + + ))} +