fix(goldset): single view-mode filter (can't get stuck hiding untagged) #108

Merged
chaim merged 1 commits from worktree-goldset-filter-fix into main 2026-06-07 14:48:16 +00:00

View File

@@ -347,13 +347,16 @@ export function GoldsetPanel() {
const tag = useTagGoldset(batch); const tag = useTagGoldset(batch);
const createSample = useCreateGoldsetSample(batch); const createSample = useCreateGoldsetSample(batch);
const [focusedId, setFocusedId] = useState<string | null>(null); const [focusedId, setFocusedId] = useState<string | null>(null);
const [hideTagged, setHideTagged] = useState(false); // Single mutually-exclusive view mode — can't get "stuck" like the old
const [disagreeOnly, setDisagreeOnly] = useState(false); // independent toggles (where the disagree filter hid the untagged items).
const [viewMode, setViewMode] =
useState<"all" | "untagged" | "tagged" | "disagree">("all");
const [sourceFilter, setSourceFilter] = const [sourceFilter, setSourceFilter] =
useState<"all" | "court_ruling" | "appeals_committee">("all"); useState<"all" | "court_ruling" | "appeals_committee">("all");
const items = useMemo(() => data?.items ?? [], [data]); const items = useMemo(() => data?.items ?? [], [data]);
const taggedCount = items.filter(isTagged).length; const taggedCount = items.filter(isTagged).length;
const untaggedCount = items.length - taggedCount;
const disagreeCount = items.filter(aiDisagrees).length; const disagreeCount = items.filter(aiDisagrees).length;
const sourceCounts = useMemo(() => ({ const sourceCounts = useMemo(() => ({
court_ruling: items.filter((i) => i.source_type === "court_ruling").length, court_ruling: items.filter((i) => i.source_type === "court_ruling").length,
@@ -362,13 +365,14 @@ export function GoldsetPanel() {
const visible = useMemo(() => { const visible = useMemo(() => {
let v = items; let v = items;
if (sourceFilter !== "all") v = v.filter((i) => i.source_type === sourceFilter); if (sourceFilter !== "all") v = v.filter((i) => i.source_type === sourceFilter);
if (hideTagged) v = v.filter((i) => !isTagged(i)); if (viewMode === "untagged") v = v.filter((i) => !isTagged(i));
if (disagreeOnly) v = v.filter(aiDisagrees); else if (viewMode === "tagged") v = v.filter(isTagged);
else if (viewMode === "disagree") v = v.filter(aiDisagrees);
// group-sort: כל פסקי-הדין יחד, ואז כל החלטות ועדת-הערר (הפרדה ברורה). // group-sort: כל פסקי-הדין יחד, ואז כל החלטות ועדת-הערר (הפרדה ברורה).
const order = (s: string | null) => const order = (s: string | null) =>
s === "court_ruling" ? 0 : s === "appeals_committee" ? 1 : 2; s === "court_ruling" ? 0 : s === "appeals_committee" ? 1 : 2;
return [...v].sort((a, b) => order(a.source_type) - order(b.source_type)); 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; const focused = focusedId ? visible.find((i) => i.id === focusedId) ?? null : null;
@@ -467,16 +471,23 @@ export function GoldsetPanel() {
{" "}· הלכה <kbd className="bg-rule-soft px-1.5 rounded">H</kbd> / לא <kbd className="bg-rule-soft px-1.5 rounded">N</kbd> {" "}· הלכה <kbd className="bg-rule-soft px-1.5 rounded">H</kbd> / לא <kbd className="bg-rule-soft px-1.5 rounded">N</kbd>
{" "}· ציטוט שלם <kbd className="bg-rule-soft px-1.5 rounded">C</kbd> / קטוע <kbd className="bg-rule-soft px-1.5 rounded">X</kbd> {" "}· ציטוט שלם <kbd className="bg-rule-soft px-1.5 rounded">C</kbd> / קטוע <kbd className="bg-rule-soft px-1.5 rounded">X</kbd>
</span> </span>
{disagreeCount > 0 && ( <div className="ms-auto flex items-center gap-1 rounded-lg border border-rule p-0.5 bg-rule-soft/30">
<Button size="sm" variant={disagreeOnly ? "default" : "ghost"} {([
className={disagreeOnly ? "ms-auto bg-amber-500 text-white hover:bg-amber-600" : "ms-auto text-amber-700"} { v: "all", label: `הכל (${items.length})` },
onClick={() => setDisagreeOnly((v) => !v)}> { v: "untagged", label: `לא תויגו (${untaggedCount})` },
אי-הסכמות AI ({disagreeCount}) { v: "tagged", label: `תויגו (${taggedCount})` },
</Button> { v: "disagree", label: `⚠ אי-הסכמות (${disagreeCount})` },
)} ] as const).map((m) => (
<Button size="sm" variant="ghost" className={disagreeCount > 0 ? "" : "ms-auto"} onClick={() => setHideTagged((v) => !v)}> <Button key={m.v} size="sm"
{hideTagged ? "הצג הכל" : "הסתר מתויגים"} variant={viewMode === m.v ? "default" : "ghost"}
</Button> className={viewMode === m.v
? (m.v === "disagree" ? "bg-amber-500 text-white hover:bg-amber-600" : "bg-gold text-navy hover:bg-gold-deep")
: (m.v === "disagree" ? "text-amber-700" : "")}
onClick={() => setViewMode(m.v)}>
{m.label}
</Button>
))}
</div>
</div> </div>
<div className="space-y-3"> <div className="space-y-3">