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>
This commit is contained in:
72
web-ui/src/lib/practice-area.ts
Normal file
72
web-ui/src/lib/practice-area.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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";
|
||||
}
|
||||
Reference in New Issue
Block a user