ui(precedents): upload sheet routes ערר/בל"מ to internal-decisions endpoint
Some checks failed
Build & Deploy / build-and-deploy (push) Has been cancelled
Some checks failed
Build & Deploy / build-and-deploy (push) Has been cancelled
Citations starting with ערר/בל"מ/ARAR are committee decisions and must carry chair_name + district. The /precedents upload form previously errored out for these (precedent_library service rejects them) with no in-UI path forward — internal_decision_upload was only reachable via the /missing-precedents flow. The form now auto-detects committee citations, reveals chair_name + district fields, hides the irrelevant source_type/precedent_level (derived server-side), and posts to /api/internal-decisions/upload. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -355,6 +355,85 @@ export function useUploadPrecedent() {
|
||||
});
|
||||
}
|
||||
|
||||
// Valid Hebrew districts for appeals-committee decisions. Mirrors
|
||||
// VALID_DISTRICTS in mcp-server/src/legal_mcp/tools/internal_decisions.py —
|
||||
// keep in sync with the service-side guard.
|
||||
export const COMMITTEE_DISTRICTS = [
|
||||
"ירושלים",
|
||||
"תל אביב",
|
||||
"מרכז",
|
||||
"חיפה",
|
||||
"צפון",
|
||||
"דרום",
|
||||
"ארצי",
|
||||
] as const;
|
||||
|
||||
export type CommitteeDistrict = (typeof COMMITTEE_DISTRICTS)[number];
|
||||
|
||||
// A citation that targets internal_decision_upload, not the external library.
|
||||
// Matches the prefix list in precedent_library service (ערר/בל"מ/ARAR).
|
||||
const COMMITTEE_PREFIXES = ["ערר ", "ערר(", "בל\"מ ", "בל\"מ(", "ARAR "];
|
||||
|
||||
export function isCommitteeCitation(citation: string): boolean {
|
||||
const c = citation.trimStart();
|
||||
return COMMITTEE_PREFIXES.some((p) => c.startsWith(p));
|
||||
}
|
||||
|
||||
export type InternalDecisionUploadInput = {
|
||||
file: File;
|
||||
case_number: string;
|
||||
chair_name: string;
|
||||
district: CommitteeDistrict | string;
|
||||
case_name?: string;
|
||||
court?: string;
|
||||
decision_date?: string;
|
||||
practice_area?: PracticeArea;
|
||||
appeal_subtype?: string;
|
||||
subject_tags?: string[];
|
||||
is_binding?: boolean;
|
||||
summary?: string;
|
||||
};
|
||||
|
||||
export function useUploadInternalDecision() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (input: InternalDecisionUploadInput) => {
|
||||
const fd = new FormData();
|
||||
fd.append("file", input.file);
|
||||
fd.append("case_number", input.case_number);
|
||||
fd.append("chair_name", input.chair_name);
|
||||
fd.append("district", input.district);
|
||||
if (input.case_name) fd.append("case_name", input.case_name);
|
||||
if (input.court) fd.append("court", input.court);
|
||||
if (input.decision_date) fd.append("decision_date", input.decision_date);
|
||||
if (input.practice_area) fd.append("practice_area", input.practice_area);
|
||||
if (input.appeal_subtype)
|
||||
fd.append("appeal_subtype", input.appeal_subtype);
|
||||
if (input.subject_tags && input.subject_tags.length)
|
||||
fd.append("subject_tags", JSON.stringify(input.subject_tags));
|
||||
fd.append("is_binding", String(input.is_binding ?? false));
|
||||
if (input.summary) fd.append("summary", input.summary);
|
||||
|
||||
const res = await fetch("/api/internal-decisions/upload", {
|
||||
method: "POST",
|
||||
body: fd,
|
||||
});
|
||||
const parsed = await res.json().catch(() => null);
|
||||
if (!res.ok) {
|
||||
throw new ApiError(
|
||||
`Upload failed with ${res.status}`,
|
||||
res.status,
|
||||
parsed,
|
||||
);
|
||||
}
|
||||
return parsed as { task_id: string };
|
||||
},
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: libraryKeys.all });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeletePrecedent() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
|
||||
Reference in New Issue
Block a user