feat(ui): תור-אישור הלכות מאוחד — 2 תצוגות לפי פעולה (#133)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
מבטל את ה-toggle "תור נקי / דורש תיקון-חילוץ" שבו "תור נקי" ריק לגמרי (כל ההלכות-הנקיות נפתרו), והעבודה האמיתית חבויה מאחורי הכפתור השני שגם מערבב התלבטות-פאנל עם פגמי-חילוץ. אושר ב-Claude Design (כרטיס 19-halacha-queue-unified). במקום זה — תור אחד, fetch אחד, פיצול client-side לפי **סוג-הפעולה**: - "להכרעתך" = הלכות שהפאנל דן בהן (יש panel_round) או נקיות → אשר/דחה, עם טבלת-ההתלבטות; ממוין פיצול-פאנל-תחילה (FU-3). - "דורש תיקון-חילוץ" = מסומנות-דגל שלא עברו התלבטות → תיקון-חילוץ. `useHalachotPending` אוחד לקריאה אחת (exclude_low_quality=false + order_by_priority + cluster + include_equivalents + include_panel_round); נוסף `isExtractionFixItem(h)` (= !panel_round && יש דגל). PendingPanel מפצל ב-useMemo, segmented-control עם מוני שני הדליים. אפס שינוי-backend (הפרמטרים כבר קיימים מ-#220/#222). display-only, שער-אישור יחיד (INV-IA/G10). ולידציה: tsc + eslint נקי. חלק מ-#133. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,8 @@ import { Textarea } from "@/components/ui/textarea";
|
||||
import { CorroborationBadge } from "./corroboration-badge";
|
||||
import { practiceAreaLabel } from "./practice-area";
|
||||
import {
|
||||
useHalachotPending, useHalachotByStatus, useUpdateHalacha, useBatchReviewHalachot, type Halacha,
|
||||
useHalachotPending, useHalachotByStatus, useUpdateHalacha, useBatchReviewHalachot,
|
||||
isExtractionFixItem, type Halacha,
|
||||
} from "@/lib/api/precedent-library";
|
||||
import { AuthorityBadge, ruleTypeLabel } from "./halacha-meta";
|
||||
|
||||
@@ -628,23 +629,29 @@ function RestorePanel({
|
||||
// ─── Pending queue panel (main review flow) ───────────────────────────────────
|
||||
|
||||
function PendingPanel() {
|
||||
// #84.1 — "clean" = quality-gated + prioritized + clustered review queue;
|
||||
// "needsfix" = the flagged 'needs extraction fix' bucket.
|
||||
const [view, setView] = useState<"clean" | "needsfix">("clean");
|
||||
const { data, isPending, error } = useHalachotPending({
|
||||
limit: 500, needsFix: view === "needsfix",
|
||||
});
|
||||
// #133 unified queue — ONE fetch of all pending, split client-side by ACTION:
|
||||
// "judgment" = items the panel deliberated (or clean) → chair approves/rejects;
|
||||
// "fix" = flagged-but-never-adjudicated → extraction repair. (No empty "תור נקי".)
|
||||
const [view, setView] = useState<"judgment" | "fix">("judgment");
|
||||
const { data, isPending, error } = useHalachotPending({ limit: 500 });
|
||||
const update = useUpdateHalacha();
|
||||
const batch = useBatchReviewHalachot();
|
||||
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
|
||||
const [focusedId, setFocusedId] = useState<string | null>(null);
|
||||
|
||||
const groups = useMemo<Group[]>(
|
||||
() => buildGroups(data?.items ?? []),
|
||||
[data],
|
||||
);
|
||||
const allItems = useMemo(() => data?.items ?? [], [data]);
|
||||
const judgmentCount = useMemo(
|
||||
() => allItems.filter((h) => !isExtractionFixItem(h)).length, [allItems]);
|
||||
const fixCount = useMemo(
|
||||
() => allItems.filter(isExtractionFixItem).length, [allItems]);
|
||||
|
||||
const totalCount = data?.items.length ?? 0;
|
||||
const segmentItems = useMemo(
|
||||
() => allItems.filter((h) =>
|
||||
view === "fix" ? isExtractionFixItem(h) : !isExtractionFixItem(h)),
|
||||
[allItems, view],
|
||||
);
|
||||
const groups = useMemo<Group[]>(() => buildGroups(segmentItems), [segmentItems]);
|
||||
const totalCount = segmentItems.length;
|
||||
|
||||
const visibleItems = useMemo<ReviewItem[]>(() => {
|
||||
const out: ReviewItem[] = [];
|
||||
@@ -766,24 +773,26 @@ function PendingPanel() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [focused, visibleItems]);
|
||||
|
||||
const segBtn = (key: "judgment" | "fix", label: string, count: number) => (
|
||||
<Button
|
||||
size="sm"
|
||||
variant={view === key ? "default" : "ghost"}
|
||||
className={view === key ? "bg-gold text-navy hover:bg-gold-deep" : ""}
|
||||
onClick={() => setView(key)}
|
||||
>
|
||||
{label}
|
||||
<span className={`ms-2 text-[0.7rem] px-1.5 py-0.5 rounded-full tabular-nums
|
||||
${view === key ? "bg-navy/15" : "bg-black/10 text-ink-muted"}`}>
|
||||
{count}
|
||||
</span>
|
||||
</Button>
|
||||
);
|
||||
|
||||
// #133 — two segments by ACTION (deliberated→judge vs flagged→fix); no empty "תור נקי".
|
||||
const viewToggle = (
|
||||
<div className="flex items-center gap-1 rounded-lg border border-rule p-0.5 bg-rule-soft/30 w-fit">
|
||||
<Button
|
||||
size="sm"
|
||||
variant={view === "clean" ? "default" : "ghost"}
|
||||
className={view === "clean" ? "bg-gold text-navy hover:bg-gold-deep" : ""}
|
||||
onClick={() => setView("clean")}
|
||||
>
|
||||
תור נקי
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={view === "needsfix" ? "default" : "ghost"}
|
||||
className={view === "needsfix" ? "bg-gold text-navy hover:bg-gold-deep" : ""}
|
||||
onClick={() => setView("needsfix")}
|
||||
>
|
||||
דורש תיקון-חילוץ
|
||||
</Button>
|
||||
{segBtn("judgment", "להכרעתך", judgmentCount)}
|
||||
{segBtn("fix", "דורש תיקון-חילוץ", fixCount)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -804,14 +813,14 @@ function PendingPanel() {
|
||||
body = (
|
||||
<div className="text-center text-ink-muted py-16">
|
||||
<p className="text-lg">
|
||||
{view === "needsfix"
|
||||
? "בקט התיקון ריק — אין הלכות מסומנות-איכות."
|
||||
: "אין הלכות נקיות הממתינות לאישור."}
|
||||
{view === "fix"
|
||||
? "אין הלכות הממתינות לתיקון-חילוץ."
|
||||
: "אין הלכות הממתינות להכרעתך."}
|
||||
</p>
|
||||
<p className="text-sm mt-2">
|
||||
{view === "needsfix"
|
||||
? "פריטים שסומנו (ציטוט לא-מאומת, יישום, כפילות-קרובה וכו') יופיעו כאן."
|
||||
: "העלה פסיקה חדשה — ההלכות שיחולצו ממנה יופיעו כאן."}
|
||||
{view === "fix"
|
||||
? "פריטים שסומנו בפגם-חילוץ (ציטוט לא-מאומת / קטוע / כפילות) ושלא עברו התלבטות-פאנל יופיעו כאן."
|
||||
: "הלכות שהפאנל הכריע או דן בהן יופיעו כאן לאישורך."}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@@ -821,8 +830,11 @@ function PendingPanel() {
|
||||
<div className="flex items-center gap-3 text-sm text-ink-muted flex-wrap">
|
||||
<span>
|
||||
<span className="text-navy font-semibold">{totalCount}</span>
|
||||
{view === "needsfix" ? " מסומנות" : " ממתינות"}
|
||||
{view === "fix" ? " לתיקון-חילוץ" : " להכרעה"}
|
||||
{" "}ב-<span className="text-navy font-semibold">{groups.length}</span> פסיקות
|
||||
{view === "judgment" && (
|
||||
<span className="text-[0.72rem] ms-2">· ממוין: פיצול-פאנל תחילה</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="me-auto text-[0.72rem]">
|
||||
ניווט: <kbd className="bg-rule-soft px-1.5 rounded">J</kbd>/<kbd className="bg-rule-soft px-1.5 rounded">K</kbd>
|
||||
|
||||
Reference in New Issue
Block a user