Files
legal-ai/web-ui/src/lib/practice-area.ts
Chaim ac0a5ee30b Phase 4.5: practice area integration in the Next.js UI
Backend commit 26d09d6 introduced a multi-tenant axis (practice_area +
appeal_subtype) that the vanilla UI picked up but the new Next.js
rewrite did not. Close the gap in the screens we already shipped so
future search/filter work in Phase 5 has the right data on screen.

- lib/practice-area.ts — new: enum + label maps + deriveSubtype(),
  mirrors mcp-server/src/legal_mcp/services/practice_area.py.
- lib/schemas/case.ts — two new z.enum fields on caseCreateSchema.
- lib/api/cases.ts — Case / CaseDetail gain practice_area and
  appeal_subtype as optional (cached pre-migration responses still
  parse cleanly).
- wizard/case-wizard.tsx — basics step gains a practice_area dropdown
  (future domains disabled with "(בקרוב)") and an appeal_subtype
  dropdown with auto-fill effect tracking a userTouchedSubtype ref,
  same behaviour as wireSubtypeAutofill() in the vanilla UI.
- cases/case-header.tsx — gold badge next to the status shows
  "ועדת ערר · רישוי ובנייה" when both fields are populated.
- cases/cases-table.tsx — new "תחום" column showing subtype label
  (dash for unknown). No filter yet — that's phase 5 when a second
  domain actually exists.

Plan: ~/.claude/plans/woolly-cooking-graham.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 17:15:48 +00:00

73 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Client-side mirror of mcp-server/src/legal_mcp/services/practice_area.py.
*
* Keep the enum values and derivation logic in sync with the backend — the
* server is the authority, but the UI needs the labels and derivation for
* UX (auto-fill, badges, filters). If the server adds a new practice_area
* or subtype, extend the arrays below.
*
* See also: legal-ai/docs/practice-area-separation.md
*/
export type PracticeArea =
| "appeals_committee"
| "national_insurance"
| "labor_law";
export type AppealSubtype =
| "building_permit"
| "betterment_levy"
| "compensation_197"
| "unknown";
export const PRACTICE_AREAS: ReadonlyArray<{
value: PracticeArea;
label: string;
enabled: boolean;
}> = [
{ value: "appeals_committee", label: "ועדת ערר", enabled: true },
{ value: "national_insurance", label: "ביטוח לאומי", enabled: false },
{ value: "labor_law", label: "דיני עבודה", enabled: false },
];
export const APPEAL_SUBTYPES: ReadonlyArray<{
value: AppealSubtype;
label: string;
}> = [
{ value: "building_permit", label: "רישוי ובנייה" },
{ value: "betterment_levy", label: "היטל השבחה" },
{ value: "compensation_197", label: "פיצויים (ס' 197)" },
{ value: "unknown", label: "לא ידוע" },
];
export const PRACTICE_AREA_LABELS: Record<PracticeArea, string> =
Object.fromEntries(PRACTICE_AREAS.map((p) => [p.value, p.label])) as Record<
PracticeArea,
string
>;
export const APPEAL_SUBTYPE_LABELS: Record<AppealSubtype, string> =
Object.fromEntries(APPEAL_SUBTYPES.map((s) => [s.value, s.label])) as Record<
AppealSubtype,
string
>;
/*
* Derive the appeal_subtype from a case number. Mirrors the Python
* `derive_subtype` in practice_area.py. The convention is the case-number
* first digit: 1xxx → building_permit, 8xxx → betterment_levy,
* 9xxx → compensation_197. Everything else, including non-appeals_committee
* domains, returns 'unknown'.
*/
export function deriveSubtype(
caseNumber: string,
practiceArea: PracticeArea = "appeals_committee",
): AppealSubtype {
if (practiceArea !== "appeals_committee") return "unknown";
const first = caseNumber.trim().match(/^(\d)/)?.[1];
if (first === "1") return "building_permit";
if (first === "8") return "betterment_levy";
if (first === "9") return "compensation_197";
return "unknown";
}