Edit document doc_type and appraiser side from the case UI
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m26s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m26s
Until now changing a document's doc_type required a manual SQL update.
Adds an inline editor on the document badge so the chair can retag
without leaving the case page, and threads an appraiser_side tag
(committee / appellant / deciding) through the appraisal pipeline so
betterment-levy cases — which usually have 2-3 appraisers — render
conflicts with the deciding appraiser's view marked as governing.
Backend
- New appraiser_facts.appraiser_side column (V5.1) populated from
documents.metadata.appraiser_side at extraction time.
- extract_appraiser_facts now returns status='sides_missing' with the
list of untagged appraisals instead of running with empty side
labels — chair must tag every appraisal first via the UI.
- Conflict detection orders entries committee → appellant → deciding so
the deciding appraiser appears last; block-tet's prompt instructs the
writer to phrase the deciding appraiser's view as the governing
factual finding ("ואולם, השמאי המכריע קבע...").
- New PATCH /api/cases/{n}/documents/{doc_id} (Pydantic model with
whitelist validation) and matching document_update MCP tool. Both
merge appraiser_side into metadata JSONB instead of touching the
schema.
UI
- New shared doc-types module exports the canonical 11 doc_type
options plus the 3 appraiser-side options; both upload-sheet and
the document badge now read from it instead of duplicating Hebrew
labels.
- New DocumentTypeEditor renders a Popover off the doc-type Badge
with two Selects. The save button stays disabled while doc_type is
appraisal but no side has been picked, mirroring the backend
enforcement so the user finds out before triggering extraction.
- usePatchDocument React-Query mutation invalidates the case detail
on success so the badge updates without a manual refresh.
This commit is contained in:
@@ -84,6 +84,58 @@ export function useUploadDocument(caseNumber: string) {
|
||||
});
|
||||
}
|
||||
|
||||
// ── PATCH document tags ───────────────────────────────────────────
|
||||
|
||||
export type DocumentPatch = {
|
||||
doc_type?: string;
|
||||
appraiser_side?: string; // "" clears; "committee" | "appellant" | "deciding" sets
|
||||
};
|
||||
|
||||
export type PatchDocumentResponse = {
|
||||
status: "completed" | "noop";
|
||||
document: {
|
||||
id: string;
|
||||
doc_type: string;
|
||||
title: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
|
||||
async function patchDocument(
|
||||
caseNumber: string,
|
||||
docId: string,
|
||||
patch: DocumentPatch,
|
||||
): Promise<PatchDocumentResponse> {
|
||||
const res = await fetch(
|
||||
`/api/cases/${encodeURIComponent(caseNumber)}/documents/${encodeURIComponent(docId)}`,
|
||||
{
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(patch),
|
||||
},
|
||||
);
|
||||
const contentType = res.headers.get("content-type") ?? "";
|
||||
const parsed = contentType.includes("application/json")
|
||||
? await res.json().catch(() => null)
|
||||
: await res.text().catch(() => null);
|
||||
if (!res.ok) {
|
||||
throw new ApiError(`Patch failed with ${res.status}`, res.status, parsed);
|
||||
}
|
||||
return parsed as PatchDocumentResponse;
|
||||
}
|
||||
|
||||
export function usePatchDocument(caseNumber: string) {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ docId, patch }: { docId: string; patch: DocumentPatch }) =>
|
||||
patchDocument(caseNumber, docId, patch),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: casesKeys.detail(caseNumber) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function useProgress(taskId: string | null) {
|
||||
const [event, setEvent] = useState<ProgressEvent | null>(null);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user