feat(proceeding-type): explicit ערר/בל"מ field for cases + corpus
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m40s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m40s
Same case_number can exist as both a regular appeal (ערר) and an extension-of-time request (בל"מ), and we were inferring the difference from appeal_subtype prefixes — fragile, and case-number lookups weren't disambiguated. Now stored as a first-class field on both case_law (corpus) and cases (live cases), with partial unique indexes on (case_number, proceeding_type). - SCHEMA_V15: column + CHECK constraints + backfill from appeal_subtype LIKE 'extension_request_%' + partial unique indexes replace the old global UNIQUE(case_number). - derive_proceeding_type() centralizes the inference rule (extension_request_* → בל"מ; subject regex fallback; default ערר). - Metadata extractor prompt asks Claude to populate the new field explicitly; apply_to_record writes it for internal_committee rows. - internal_decision_upload, case_create, case_update accept an optional proceeding_type; FastAPI request models expose it. - Wizard + edit dialog get a sided Select; case header renders the resolved label (ערר / בל"מ). - Uploaded the 2 staged בל"מ decisions on betterment levy: 8126/24 (סופר נוח, 13 chunks), 8047/23 (הרנון, 48 chunks). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ import {
|
||||
import { PartiesField } from "@/components/wizard/parties-field";
|
||||
import { useCreateCase } from "@/lib/api/cases";
|
||||
import {
|
||||
caseCreateSchema, expectedOutcomes,
|
||||
caseCreateSchema, expectedOutcomes, proceedingTypes,
|
||||
type CaseCreateInput,
|
||||
} from "@/lib/schemas/case";
|
||||
import {
|
||||
@@ -35,7 +35,7 @@ type StepKey = (typeof STEPS)[number]["key"];
|
||||
/* Fields validated at each step — lets the user fix just what's on screen
|
||||
* before moving forward, instead of surfacing all errors from page 1. */
|
||||
const STEP_FIELDS: Record<StepKey, (keyof CaseCreateInput)[]> = {
|
||||
basics: ["case_number", "title", "practice_area", "appeal_subtype"],
|
||||
basics: ["case_number", "title", "proceeding_type", "practice_area", "appeal_subtype"],
|
||||
parties: ["appellants", "respondents"],
|
||||
details: ["subject", "hearing_date", "expected_outcome", "notes", "property_address", "permit_number"],
|
||||
};
|
||||
@@ -66,6 +66,7 @@ export function CaseWizard() {
|
||||
expected_outcome: "",
|
||||
practice_area: "appeals_committee",
|
||||
appeal_subtype: "unknown",
|
||||
proceeding_type: "ערר",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -74,11 +75,17 @@ export function CaseWizard() {
|
||||
* stop the moment they manually pick a value from the dropdown. Mirrors
|
||||
* the wireSubtypeAutofill() behaviour of the vanilla UI
|
||||
* (legal-ai/web/static/index.html around line 2770).
|
||||
*
|
||||
* proceeding_type follows the same pattern: if the user hasn't picked
|
||||
* a value yet, we derive 'בל"מ' whenever the subtype lands on an
|
||||
* extension_request_* value.
|
||||
*/
|
||||
const userTouchedSubtype = useRef(false);
|
||||
const userTouchedProceeding = useRef(false);
|
||||
const caseNumber = form.watch("case_number");
|
||||
const practiceArea = form.watch("practice_area");
|
||||
const subject = form.watch("subject");
|
||||
const appealSubtype = form.watch("appeal_subtype");
|
||||
useEffect(() => {
|
||||
if (userTouchedSubtype.current) return;
|
||||
/* derive_subtype_with_blam picks extension_request_* when subject
|
||||
@@ -89,6 +96,16 @@ export function CaseWizard() {
|
||||
}
|
||||
}, [caseNumber, practiceArea, subject, form]);
|
||||
|
||||
/* proceeding_type follows appeal_subtype when the user hasn't picked
|
||||
* one explicitly — extension_request_* always implies 'בל"מ'. */
|
||||
useEffect(() => {
|
||||
if (userTouchedProceeding.current) return;
|
||||
const proc = appealSubtype.startsWith("extension_request_") ? 'בל"מ' : "ערר";
|
||||
if (proc !== form.getValues("proceeding_type")) {
|
||||
form.setValue("proceeding_type", proc, { shouldValidate: false });
|
||||
}
|
||||
}, [appealSubtype, form]);
|
||||
|
||||
const stepIndex = STEPS.findIndex((s) => s.key === step);
|
||||
const isLast = stepIndex === STEPS.length - 1;
|
||||
|
||||
@@ -162,6 +179,39 @@ export function CaseWizard() {
|
||||
<Input id="title" {...form.register("title")} className="mt-1" />
|
||||
<FieldError message={form.formState.errors.title?.message} />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-navy">
|
||||
סוג תיק <span className="text-danger">*</span>
|
||||
</Label>
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="proceeding_type"
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={(v) => {
|
||||
userTouchedProceeding.current = true;
|
||||
field.onChange(v as CaseCreateInput["proceeding_type"]);
|
||||
}}
|
||||
dir="rtl"
|
||||
>
|
||||
<SelectTrigger className="mt-1">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{proceedingTypes.map((p) => (
|
||||
<SelectItem key={p.value} value={p.value}>
|
||||
{p.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
<p className="text-[0.7rem] text-ink-muted mt-1">
|
||||
ערר = הליך עיקרי; בל"מ = בקשה להארכת מועד להגשת ערר
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label className="text-navy">תחום משפטי</Label>
|
||||
|
||||
Reference in New Issue
Block a user