"use client"; import { useMemo, type ReactNode } from "react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Skeleton } from "@/components/ui/skeleton"; import { PARTY_LABELS_HE, PRIORITY_LABELS_HE, PRIORITY_ORDER, useAggregateArguments, useLegalArguments, type AggregateArgumentsResult, type LegalArgument, type LegalArgumentParty, type LegalArgumentPriority, } from "@/lib/api/legal-arguments"; import { toast } from "sonner"; import { ListTree, Loader2, RefreshCw, Sparkles } from "lucide-react"; const PRIORITY_BADGE_TONE: Record = { threshold: "bg-danger-bg/60 text-danger-strong border-danger/40", substantive: "bg-gold-soft/50 text-navy border-gold/40", procedural: "bg-rule-soft text-ink border-rule", relief: "bg-emerald-50 text-emerald-900 border-emerald-200", }; function groupByPriority( args: LegalArgument[], ): Record { const out: Record = { threshold: [], substantive: [], procedural: [], relief: [], }; for (const a of args) { (out[a.priority] ?? out.substantive).push(a); } for (const key of PRIORITY_ORDER) { out[key].sort((x, y) => x.argument_index - y.argument_index); } return out; } type PartySectionProps = { party: LegalArgumentParty; args: LegalArgument[]; }; function PartySection({ party, args }: PartySectionProps) { const grouped = useMemo(() => groupByPriority(args), [args]); return (

{PARTY_LABELS_HE[party] ?? party}

{args.length} טיעונים
{PRIORITY_ORDER.map((priority) => { const list = grouped[priority]; if (!list?.length) return null; return (
{PRIORITY_LABELS_HE[priority]} {list.length} טיעונים
{list.map((arg) => (
{arg.argument_index}. {arg.argument_title} {arg.legal_topic && ( {arg.legal_topic} )}

{arg.argument_body}

{arg.supporting_claims.length > 0 && (arg.supporting_propositions && arg.supporting_propositions.length > 0 ? (

הטענות הגולמיות שמהן אוגד הטיעון:

    {arg.supporting_propositions.map((p, i) => (
  1. {i + 1}. {" "} {p.text} {p.source_document && ( מקור: {p.source_document} )}
  2. ))}
) : (

מסתמך על {arg.supporting_claims.length} פרופוזיציות גולמיות.

))}
))}
); })}
); } type LegalArgumentsPanelProps = { caseNumber: string; }; export function LegalArgumentsPanel({ caseNumber }: LegalArgumentsPanelProps) { const { data, isPending, isError, error } = useLegalArguments(caseNumber); const aggregate = useAggregateArguments(caseNumber); const parties = useMemo(() => { if (!data?.by_party) return []; const order: LegalArgumentParty[] = [ "appellant", "respondent", "committee", "permit_applicant", "unknown", ]; return order.filter((p) => (data.by_party[p]?.length ?? 0) > 0); }, [data]); const handleAggregate = (force: boolean) => { // Status feedback is rendered inline as a banner from `aggregate.data`; // only hard transport/HTTP errors fall through to a toast. aggregate.mutate(force, { onError: (e) => toast.error(`שגיאה: ${(e as Error).message}`), }); }; return (

טיעונים משפטיים

טיעונים מאוגדים מתוך הטענות הגולמיות, מקובצים לפי צד וקדימות. החישוב רץ אצל המנתח המשפטי ומדווח בחזרה.

{isPending ? (
) : isError ? (

שגיאה בטעינת טיעונים: {(error as Error).message}

) : !data?.total ? (

אין טיעונים מאוגדים עדיין. לחץ “חשב טיעונים” כדי לשלוח את החישוב למנתח המשפטי.

) : (
{parties.map((party) => ( ))}
)}
); } const BANNER_TONE = { info: "border-info/30 bg-info-bg", gold: "border-gold/40 bg-gold-wash", muted: "border-rule bg-rule-soft", warn: "border-warn/40 bg-warn-bg", } as const; const DOT_TONE = { info: "bg-info", gold: "bg-gold-deep", muted: "bg-ink-light", warn: "bg-warn", } as const; const TITLE_TONE = { info: "text-info", gold: "text-gold-deep", muted: "text-ink-soft", warn: "text-warn", } as const; /** * Inline status feedback for the aggregation trigger — mirrors the approved * Claude Design mockup (25-legal-arguments-panel). Shown only after a click; * the four states map to the endpoint's discriminated-union result. */ function AggregateStatusBanner({ result, force, }: { result: AggregateArgumentsResult | undefined; force: boolean; }) { if (!result) return null; let tone: keyof typeof BANNER_TONE; let title: string; let body: ReactNode; switch (result.status) { case "queued": tone = "info"; title = "נשלח לאנליטיקאי."; body = `${force ? "החישוב-מחדש" : "החישוב"} רץ ברקע אצל המנתח המשפטי; התוצאה תופיע תוך כמה דקות — רענן את הדף.`; break; case "exists": tone = "gold"; title = "כבר חושב."; body = result.message; break; case "no_claims": tone = "muted"; title = "אין טענות גולמיות."; body = result.message; break; case "skipped": tone = "warn"; title = "לא ניתן להפעיל אוטומטית"; body = ( <> {` (${result.reason}). הרץ ידנית מ-Claude Code: `} mcp__legal-ai__aggregate_claims_to_arguments ); break; default: return null; } return (

{title}{" "} {body}

); }