Fixes the case where the H1 chip, the pipeline stepper and the manual-changer dropdown disagreed (e.g. 1043-02-26 on analyst_verified): the status sat between the canonical 10 and a legacy bucket, so each surface read a different list. Root fix — ONE authoritative status model + canonicalize the real intermediate states (chair-approved): - New mcp-server/.../case_status_model.py — the single registry: ordered StatusDef list (key/label/description/phase/selectable/terminal/on_enter), the 5 phases, STATUS_ORDER, phase_of/label_of, and a drift assertion that the CaseStatus enum matches it. `on_enter` is the seam for a status to *do* something on entry, declared next to its definition (no dispatcher wired yet). - models.CaseStatus enum: analyst_verified + research_complete promoted to first-class canonical statuses (the agents set them — they belong in the model, not a legacy fallback). - tools/cases.py: the forward-only STATUS_ORDER guard now derives from the registry instead of an inline list. - GET /api/status-model exposes the model so the frontend mirror can be generated against it. - web-ui case-status.ts mirrors it: the two intermediates moved from the legacy map into CASE_STATUSES / PHASES(thinking) / STATUS_LABELS / STATUS_DESCRIPTIONS; status-badge icons+tones extended. So the chip (status), stepper (its phase) and dropdown (now includes it) all agree. Invariants: G2 — collapses the scattered status definitions (enum, inline STATUS_ORDER, two frontend label maps) onto one backend authority + a documented frontend mirror; no parallel status list remains. Agent-prompt alignment + backfill follow in a separate change. py_compile + tsc + eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
66 lines
2.6 KiB
TypeScript
66 lines
2.6 KiB
TypeScript
import { Badge } from "@/components/ui/badge";
|
|
import {
|
|
FilePlus2, Loader2, FileCheck, Target,
|
|
Compass, SearchCheck, FileText,
|
|
FileOutput, CheckCircle2, Award,
|
|
} from "lucide-react";
|
|
import {
|
|
STATUS_LABELS, STATUS_DESCRIPTIONS, statusLabel, type CaseStatus,
|
|
} from "@/lib/api/case-status";
|
|
import type { LucideIcon } from "lucide-react";
|
|
|
|
/* Labels + descriptions come from the SSoT (@/lib/api/case-status).
|
|
* Icons + color tones are view-layer concerns and live here, keyed off the
|
|
* same 10 core statuses. */
|
|
|
|
const STATUS_ICONS: Record<CaseStatus, LucideIcon> = {
|
|
new: FilePlus2,
|
|
processing: Loader2,
|
|
documents_ready: FileCheck,
|
|
analyst_verified: SearchCheck,
|
|
research_complete: Compass,
|
|
outcome_set: Target,
|
|
direction_approved: Compass,
|
|
qa_review: SearchCheck,
|
|
drafted: FileText,
|
|
exported: FileOutput,
|
|
reviewed: CheckCircle2,
|
|
final: Award,
|
|
};
|
|
|
|
/* Status color groups:
|
|
* intake → new, processing (muted parchment / info)
|
|
* prep → documents_ready (info blue)
|
|
* thinking→ outcome_set, direction_approved (gold)
|
|
* writing → qa_review, drafted (warn amber)
|
|
* done → exported, reviewed, final (success green) */
|
|
const STATUS_TONE: Record<CaseStatus, string> = {
|
|
new: "bg-rule-soft text-ink-muted border-rule",
|
|
processing: "bg-info-bg text-info border-info/30",
|
|
documents_ready: "bg-info-bg text-info border-info/40",
|
|
analyst_verified: "bg-gold-wash text-gold-deep border-gold/40",
|
|
research_complete: "bg-gold-wash text-gold-deep border-gold/40",
|
|
outcome_set: "bg-gold-wash text-gold-deep border-gold/40",
|
|
direction_approved:"bg-gold-wash text-gold-deep border-gold/50",
|
|
qa_review: "bg-warn-bg text-warn border-warn/40",
|
|
drafted: "bg-warn-bg text-warn border-warn/50",
|
|
exported: "bg-success-bg text-success border-success/40",
|
|
reviewed: "bg-success-bg text-success border-success/50",
|
|
final: "bg-success-bg text-success border-success/60 font-semibold",
|
|
};
|
|
|
|
export function StatusBadge({ status }: { status: CaseStatus }) {
|
|
const Icon = STATUS_ICONS[status];
|
|
return (
|
|
<Badge
|
|
variant="outline"
|
|
className={`rounded-full px-2.5 py-0.5 text-[0.72rem] font-medium inline-flex items-center gap-1 ${STATUS_TONE[status] ?? ""}`}
|
|
>
|
|
{Icon && <Icon className="w-3 h-3 shrink-0" />}
|
|
{statusLabel(status)}
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
export { STATUS_LABELS, STATUS_ICONS, STATUS_DESCRIPTIONS, STATUS_TONE };
|