/** * Frontend mirror of the canonical case-status lifecycle. * * The ONE source of truth is the backend status model * (`mcp-server/src/legal_mcp/case_status_model.py`), exposed at * `GET /api/status-model`; this file mirrors it (order / labels / phases) so the * UI keeps compile-time `CaseStatus` typing. Keep the two in sync — the backend * registry is authoritative (it also declares per-status `on_enter` actions). * * The analyst/research intermediate states (`analyst_verified`, * `research_complete`) are first-class canonical statuses — the agents set them, * so the chip, stepper and manual changer all agree instead of the status * falling between the canonical set and a legacy bucket. * * Every status consumer (badge, changer, timeline, guide, donut, KPI cards) * imports the list / labels / phases from here instead of re-declaring its own. */ /** Ordered lifecycle — also the order shown in the manual status dropdown. */ export const CASE_STATUSES = [ "new", "processing", "documents_ready", "analyst_verified", "research_complete", "outcome_set", "direction_approved", "qa_review", "drafted", "exported", "reviewed", "final", ] as const; export type CaseStatus = (typeof CASE_STATUSES)[number]; export type PhaseKey = "intake" | "prep" | "thinking" | "writing" | "done"; /** The 5 visual phases the lifecycle collapses into across the app. */ export const PHASES: { key: PhaseKey; label: string; statuses: CaseStatus[] }[] = [ { key: "intake", label: "קליטה ועיבוד", statuses: ["new", "processing"] }, { key: "prep", label: "הכנת תיק", statuses: ["documents_ready"] }, { key: "thinking", label: "ניתוח וכיוון", statuses: ["analyst_verified", "research_complete", "outcome_set", "direction_approved"] }, { key: "writing", label: "כתיבת טיוטה", statuses: ["qa_review", "drafted"] }, { key: "done", label: "סגירה", statuses: ["exported", "reviewed", "final"] }, ]; /** * Truly-legacy statuses that are NOT in the canonical set but might still arrive * from an old client / un-migrated row — mapped to a phase for display only, so * the stepper never goes blank. (The analyst/research intermediates are NO * longer here — they are canonical statuses in CASE_STATUSES above.) */ const LEGACY_STATUS_PHASE: Record = { analysis_enriched: "thinking", ready_for_writing: "writing", drafting: "writing", in_progress: "thinking", }; /** Which phase a status belongs to (undefined only for truly unknown values). */ export function phaseOf(status?: CaseStatus | string): PhaseKey | undefined { if (!status) return undefined; return ( PHASES.find((p) => (p.statuses as string[]).includes(status))?.key ?? LEGACY_STATUS_PHASE[status] ); } /** * Index of a status within the 5-phase pipeline (−1 only for unknown values). * Single source of truth for the stepper — handles legacy statuses via phaseOf, * so a case on `analyst_verified` lands on "ניתוח וכיוון" instead of blank. */ export function phaseIndexOf(status?: CaseStatus | string): number { const key = phaseOf(status); return key ? PHASES.findIndex((p) => p.key === key) : -1; } export const STATUS_LABELS: Record = { new: "חדש", processing: "בעיבוד", documents_ready: "מסמכים מוכנים", analyst_verified: "ניתוח אומת", research_complete: "מחקר הושלם", outcome_set: "תוצאה נקבעה", direction_approved: "כיוון אושר", qa_review: "בדיקת איכות", drafted: "טיוטה", exported: "יוצא", reviewed: "נבדק", final: "סופי", }; /** * Hebrew labels for the removed/legacy statuses — display-only fallback so a * row that hasn't been migrated yet (or arrives from an old client) never * renders a raw English slug as a label. These are NOT selectable statuses. */ const LEGACY_STATUS_LABELS: Record = { in_progress: "בעבודה", uploading: "מעלה", brainstorming: "סיעור מוחות", analysis_enriched: "ניתוח הועמק", ready_for_writing: "מוכן לכתיבה", drafting: "בכתיבה", qa_failed: "בדיקת איכות נכשלה", qa_passed: "טיוטה", }; /** * Always-Hebrew label for any status string. Use this everywhere a status is * rendered as a label so an unknown/legacy value never leaks an English slug. */ export function statusLabel(status?: string): string { if (!status) return ""; return ( STATUS_LABELS[status as CaseStatus] ?? LEGACY_STATUS_LABELS[status] ?? "לא ידוע" ); } export const STATUS_DESCRIPTIONS: Record = { new: "התיק נוצר וממתין להעלאת מסמכים", processing: "המערכת מעבדת ומנתחת את המסמכים", documents_ready: "כל המסמכים עובדו ומוכנים לעבודה", analyst_verified: "המנתח סיים ואימת את הניתוח — ממתין להכרעת תוצאה", research_complete: "חקר התקדימים הושלם (מסלול נפרד מהמנתח)", outcome_set: "נקבעה תוצאה צפויה לערר", direction_approved: "כיוון ההחלטה אושר — בהעמקת ניתוח וכתיבה", qa_review: "הטיוטה בבדיקת איכות אוטומטית", drafted: "טיוטה מוכנה לעיון", exported: "ההחלטה יוצאה לקובץ DOCX", reviewed: 'ההחלטה נבדקה ע"י היו"ר', final: "החלטה סופית — מוכנה להגשה", };