feat: fix wizard step-skip bug + extend case edit with all fields + Paperclip title sync
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m38s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m38s
- Fix keyboard navigation bug: React was reusing the submit button DOM element when transitioning "הבא" → "צור תיק", retaining focus and causing Enter to auto-submit step 3. Added key props to force element replacement. - CaseEditDialog now covers all wizard fields: appellants, respondents, property_address, permit_number (in addition to existing title, subject, hearing_date, expected_outcome, notes). - When case title changes, Paperclip project name is updated in background via new update_project_name() in paperclip_client.py. - Extended CaseUpdateRequest, case_update MCP tool, and caseUpdateSchema to carry the new fields end-to-end. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -271,6 +271,10 @@ async def case_update(
|
|||||||
decision_date: str = "",
|
decision_date: str = "",
|
||||||
tags: list[str] | None = None,
|
tags: list[str] | None = None,
|
||||||
expected_outcome: str = "",
|
expected_outcome: str = "",
|
||||||
|
appellants: list[str] | None = None,
|
||||||
|
respondents: list[str] | None = None,
|
||||||
|
property_address: str = "",
|
||||||
|
permit_number: str = "",
|
||||||
) -> str:
|
) -> str:
|
||||||
"""עדכון פרטי תיק.
|
"""עדכון פרטי תיק.
|
||||||
|
|
||||||
@@ -284,6 +288,10 @@ async def case_update(
|
|||||||
decision_date: תאריך החלטה (YYYY-MM-DD)
|
decision_date: תאריך החלטה (YYYY-MM-DD)
|
||||||
tags: תגיות
|
tags: תגיות
|
||||||
expected_outcome: תוצאה צפויה (rejection/partial_acceptance/full_acceptance/betterment_levy)
|
expected_outcome: תוצאה צפויה (rejection/partial_acceptance/full_acceptance/betterment_levy)
|
||||||
|
appellants: רשימת עוררים חדשה
|
||||||
|
respondents: רשימת משיבים חדשה
|
||||||
|
property_address: כתובת נכס חדשה
|
||||||
|
permit_number: מספר תכנית/בקשה חדש
|
||||||
"""
|
"""
|
||||||
from datetime import date as date_type
|
from datetime import date as date_type
|
||||||
|
|
||||||
@@ -322,6 +330,14 @@ async def case_update(
|
|||||||
fields["tags"] = tags
|
fields["tags"] = tags
|
||||||
if expected_outcome:
|
if expected_outcome:
|
||||||
fields["expected_outcome"] = expected_outcome
|
fields["expected_outcome"] = expected_outcome
|
||||||
|
if appellants is not None:
|
||||||
|
fields["appellants"] = appellants
|
||||||
|
if respondents is not None:
|
||||||
|
fields["respondents"] = respondents
|
||||||
|
if property_address:
|
||||||
|
fields["property_address"] = property_address
|
||||||
|
if permit_number:
|
||||||
|
fields["permit_number"] = permit_number
|
||||||
|
|
||||||
updated = await db.update_case(UUID(case["id"]), **fields)
|
updated = await db.update_case(UUID(case["id"]), **fields)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm, Controller } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
@@ -15,14 +15,15 @@ import { Label } from "@/components/ui/label";
|
|||||||
import {
|
import {
|
||||||
Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
|
Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import { PartiesField } from "@/components/wizard/parties-field";
|
||||||
import { useUpdateCase } from "@/lib/api/cases";
|
import { useUpdateCase } from "@/lib/api/cases";
|
||||||
import { caseUpdateSchema, expectedOutcomes, type CaseUpdateInput } from "@/lib/schemas/case";
|
import { caseUpdateSchema, expectedOutcomes, type CaseUpdateInput } from "@/lib/schemas/case";
|
||||||
import type { CaseDetail } from "@/lib/api/cases";
|
import type { CaseDetail } from "@/lib/api/cases";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inline edit dialog for core case fields. Uses react-hook-form + zod
|
* Inline edit dialog for all case fields set at creation time.
|
||||||
* directly (shadcn's <Form> registry entry wasn't available at init
|
* Uses react-hook-form + zod directly (shadcn's <Form> registry entry
|
||||||
* time, so the styling is reproduced by hand in a lean form layout).
|
* wasn't available at init time, so the styling is reproduced by hand).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function FieldError({ message }: { message?: string }) {
|
function FieldError({ message }: { message?: string }) {
|
||||||
@@ -42,6 +43,10 @@ export function CaseEditDialog({ data }: { data: CaseDetail }) {
|
|||||||
hearing_date: data.hearing_date ?? "",
|
hearing_date: data.hearing_date ?? "",
|
||||||
notes: "",
|
notes: "",
|
||||||
expected_outcome: data.expected_outcome ?? "",
|
expected_outcome: data.expected_outcome ?? "",
|
||||||
|
appellants: data.appellants ?? [],
|
||||||
|
respondents: data.respondents ?? [],
|
||||||
|
property_address: data.property_address ?? "",
|
||||||
|
permit_number: data.permit_number ?? "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -54,6 +59,10 @@ export function CaseEditDialog({ data }: { data: CaseDetail }) {
|
|||||||
hearing_date: data.hearing_date ?? "",
|
hearing_date: data.hearing_date ?? "",
|
||||||
notes: "",
|
notes: "",
|
||||||
expected_outcome: data.expected_outcome ?? "",
|
expected_outcome: data.expected_outcome ?? "",
|
||||||
|
appellants: data.appellants ?? [],
|
||||||
|
respondents: data.respondents ?? [],
|
||||||
|
property_address: data.property_address ?? "",
|
||||||
|
permit_number: data.permit_number ?? "",
|
||||||
});
|
});
|
||||||
}, [open, data, form]);
|
}, [open, data, form]);
|
||||||
|
|
||||||
@@ -74,11 +83,11 @@ export function CaseEditDialog({ data }: { data: CaseDetail }) {
|
|||||||
עריכת פרטי תיק
|
עריכת פרטי תיק
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-lg" dir="rtl">
|
<DialogContent className="sm:max-w-lg max-h-[90vh] overflow-y-auto" dir="rtl">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>עריכת פרטי תיק {data.case_number}</DialogTitle>
|
<DialogTitle>עריכת פרטי תיק {data.case_number}</DialogTitle>
|
||||||
<DialogDescription className="text-ink-muted">
|
<DialogDescription className="text-ink-muted">
|
||||||
השינויים נשמרים ישירות ל-FastAPI. השדות הריקים נשארים ללא שינוי.
|
השינויים נשמרים ישירות ל-DB. שינוי כותרת יסנכרן גם ל-Paperclip.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
@@ -95,6 +104,55 @@ export function CaseEditDialog({ data }: { data: CaseDetail }) {
|
|||||||
<FieldError message={form.formState.errors.subject?.message} />
|
<FieldError message={form.formState.errors.subject?.message} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="h-px bg-rule" />
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="appellants"
|
||||||
|
render={({ field, fieldState }) => (
|
||||||
|
<PartiesField
|
||||||
|
label="עוררים"
|
||||||
|
value={field.value ?? []}
|
||||||
|
onChange={field.onChange}
|
||||||
|
error={fieldState.error?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="respondents"
|
||||||
|
render={({ field, fieldState }) => (
|
||||||
|
<PartiesField
|
||||||
|
label="משיבים"
|
||||||
|
value={field.value ?? []}
|
||||||
|
onChange={field.onChange}
|
||||||
|
error={fieldState.error?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="h-px bg-rule" />
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="property_address" className="text-navy">כתובת הנכס</Label>
|
||||||
|
<Input
|
||||||
|
id="property_address"
|
||||||
|
{...form.register("property_address")}
|
||||||
|
className="mt-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="permit_number" className="text-navy">מס׳ תכנית/בקשה</Label>
|
||||||
|
<Input
|
||||||
|
id="permit_number"
|
||||||
|
{...form.register("permit_number")}
|
||||||
|
className="mt-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="hearing_date" className="text-navy">תאריך דיון</Label>
|
<Label htmlFor="hearing_date" className="text-navy">תאריך דיון</Label>
|
||||||
|
|||||||
@@ -321,6 +321,7 @@ export function CaseWizard() {
|
|||||||
</Button>
|
</Button>
|
||||||
{isLast ? (
|
{isLast ? (
|
||||||
<Button
|
<Button
|
||||||
|
key="submit-btn"
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={mutate.isPending}
|
disabled={mutate.isPending}
|
||||||
className="bg-navy hover:bg-navy-soft text-parchment"
|
className="bg-navy hover:bg-navy-soft text-parchment"
|
||||||
@@ -329,6 +330,7 @@ export function CaseWizard() {
|
|||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
|
key="next-btn"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={goNext}
|
onClick={goNext}
|
||||||
className="bg-navy hover:bg-navy-soft text-parchment"
|
className="bg-navy hover:bg-navy-soft text-parchment"
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ export type Case = {
|
|||||||
processing_count?: number;
|
processing_count?: number;
|
||||||
committee_type?: string | null;
|
committee_type?: string | null;
|
||||||
hearing_date?: string | null;
|
hearing_date?: string | null;
|
||||||
|
appellants?: string[] | null;
|
||||||
|
respondents?: string[] | null;
|
||||||
|
property_address?: string | null;
|
||||||
|
permit_number?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CaseDocument = {
|
export type CaseDocument = {
|
||||||
|
|||||||
@@ -432,6 +432,26 @@ export interface paths {
|
|||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/api/cases/stale": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Api Stale Cases
|
||||||
|
* @description Return cases that haven't been updated in N days and are not in a terminal/waiting status.
|
||||||
|
*/
|
||||||
|
get: operations["api_stale_cases_api_cases_stale_get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/api/cases/{case_number}/archive": {
|
"/api/cases/{case_number}/archive": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -1927,6 +1947,26 @@ export interface paths {
|
|||||||
patch: operations["api_resolve_feedback_api_feedback__feedback_id__resolve_patch"];
|
patch: operations["api_resolve_feedback_api_feedback__feedback_id__resolve_patch"];
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/api/chair-feedback/weekly-summary": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Api Chair Feedback Weekly Summary
|
||||||
|
* @description Return chair feedback from the last N days as a text summary for the CEO agent.
|
||||||
|
*/
|
||||||
|
get: operations["api_chair_feedback_weekly_summary_api_chair_feedback_weekly_summary_get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/api/precedent-library/upload": {
|
"/api/precedent-library/upload": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -2019,6 +2059,40 @@ export interface paths {
|
|||||||
patch: operations["precedent_library_update_api_precedent_library__case_law_id__patch"];
|
patch: operations["precedent_library_update_api_precedent_library__case_law_id__patch"];
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/api/precedent-library/{case_law_id}/relations": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
/** Precedent Add Relation */
|
||||||
|
post: operations["precedent_add_relation_api_precedent_library__case_law_id__relations_post"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/api/precedent-library/{case_law_id}/relations/{related_id}": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
/** Precedent Remove Relation */
|
||||||
|
delete: operations["precedent_remove_relation_api_precedent_library__case_law_id__relations__related_id__delete"];
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/api/precedent-library/{case_law_id}/request-metadata": {
|
"/api/precedent-library/{case_law_id}/request-metadata": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -2030,8 +2104,8 @@ export interface paths {
|
|||||||
put?: never;
|
put?: never;
|
||||||
/**
|
/**
|
||||||
* Precedent Request Metadata
|
* Precedent Request Metadata
|
||||||
* @description Stamp the case_law row as needing metadata extraction. The local
|
* @description Stamp the case_law row as needing metadata extraction AND wake the
|
||||||
* MCP worker (`precedent_process_pending_metadata`) will pick it up.
|
* Paperclip CEO so extraction runs automatically — same flow as upload.
|
||||||
*/
|
*/
|
||||||
post: operations["precedent_request_metadata_api_precedent_library__case_law_id__request_metadata_post"];
|
post: operations["precedent_request_metadata_api_precedent_library__case_law_id__request_metadata_post"];
|
||||||
delete?: never;
|
delete?: never;
|
||||||
@@ -2081,6 +2155,69 @@ export interface paths {
|
|||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/api/internal-decisions/upload": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
/**
|
||||||
|
* Internal Decisions Upload
|
||||||
|
* @description Upload a planning appeals-committee decision to the internal corpus.
|
||||||
|
*/
|
||||||
|
post: operations["internal_decisions_upload_api_internal_decisions_upload_post"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/api/internal-decisions/migrate": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
/**
|
||||||
|
* Internal Decisions Migrate
|
||||||
|
* @description Migrate existing data to the internal committee corpus.
|
||||||
|
*
|
||||||
|
* source: 'style_corpus' | 'external_corpus' | 'both'
|
||||||
|
* dry_run: if true, only report what would be done (no writes)
|
||||||
|
*/
|
||||||
|
post: operations["internal_decisions_migrate_api_internal_decisions_migrate_post"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/api/internal-decisions": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Internal Decisions List
|
||||||
|
* @description List internal committee decisions with optional filters.
|
||||||
|
*/
|
||||||
|
get: operations["internal_decisions_list_api_internal_decisions_get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/api/halachot": {
|
"/api/halachot": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -2194,6 +2331,63 @@ export interface components {
|
|||||||
*/
|
*/
|
||||||
title: string;
|
title: string;
|
||||||
};
|
};
|
||||||
|
/** Body_internal_decisions_upload_api_internal_decisions_upload_post */
|
||||||
|
Body_internal_decisions_upload_api_internal_decisions_upload_post: {
|
||||||
|
/** File */
|
||||||
|
file: string;
|
||||||
|
/** Case Number */
|
||||||
|
case_number: string;
|
||||||
|
/**
|
||||||
|
* Case Name
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
case_name: string;
|
||||||
|
/**
|
||||||
|
* Court
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
court: string;
|
||||||
|
/**
|
||||||
|
* Decision Date
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
decision_date: string;
|
||||||
|
/**
|
||||||
|
* Chair Name
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
chair_name: string;
|
||||||
|
/**
|
||||||
|
* District
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
district: string;
|
||||||
|
/**
|
||||||
|
* Practice Area
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
practice_area: string;
|
||||||
|
/**
|
||||||
|
* Appeal Subtype
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
appeal_subtype: string;
|
||||||
|
/**
|
||||||
|
* Subject Tags
|
||||||
|
* @default []
|
||||||
|
*/
|
||||||
|
subject_tags: string;
|
||||||
|
/**
|
||||||
|
* Is Binding
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
is_binding: boolean;
|
||||||
|
/**
|
||||||
|
* Summary
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
summary: string;
|
||||||
|
};
|
||||||
/** Body_precedent_library_upload_api_precedent_library_upload_post */
|
/** Body_precedent_library_upload_api_precedent_library_upload_post */
|
||||||
Body_precedent_library_upload_api_precedent_library_upload_post: {
|
Body_precedent_library_upload_api_precedent_library_upload_post: {
|
||||||
/** File */
|
/** File */
|
||||||
@@ -2536,6 +2730,16 @@ export interface components {
|
|||||||
*/
|
*/
|
||||||
pdf_document_id: string;
|
pdf_document_id: string;
|
||||||
};
|
};
|
||||||
|
/** PrecedentRelationRequest */
|
||||||
|
PrecedentRelationRequest: {
|
||||||
|
/** Related Id */
|
||||||
|
related_id: string;
|
||||||
|
/**
|
||||||
|
* Relation Type
|
||||||
|
* @default same_case_chain
|
||||||
|
*/
|
||||||
|
relation_type: string;
|
||||||
|
};
|
||||||
/** PrecedentUpdateRequest */
|
/** PrecedentUpdateRequest */
|
||||||
PrecedentUpdateRequest: {
|
PrecedentUpdateRequest: {
|
||||||
/** Case Name */
|
/** Case Name */
|
||||||
@@ -3183,6 +3387,37 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
api_stale_cases_api_cases_stale_get: {
|
||||||
|
parameters: {
|
||||||
|
query?: {
|
||||||
|
days?: number;
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
api_archive_case_api_cases__case_number__archive_post: {
|
api_archive_case_api_cases__case_number__archive_post: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -5510,6 +5745,38 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
api_chair_feedback_weekly_summary_api_chair_feedback_weekly_summary_get: {
|
||||||
|
parameters: {
|
||||||
|
query?: {
|
||||||
|
days?: number;
|
||||||
|
limit?: number;
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
precedent_library_upload_api_precedent_library_upload_post: {
|
precedent_library_upload_api_precedent_library_upload_post: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -5551,6 +5818,7 @@ export interface operations {
|
|||||||
precedent_level?: string;
|
precedent_level?: string;
|
||||||
source_type?: string;
|
source_type?: string;
|
||||||
search?: string;
|
search?: string;
|
||||||
|
source_kind?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
};
|
};
|
||||||
@@ -5735,6 +6003,73 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
precedent_add_relation_api_precedent_library__case_law_id__relations_post: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
case_law_id: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["PrecedentRelationRequest"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
precedent_remove_relation_api_precedent_library__case_law_id__relations__related_id__delete: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
case_law_id: string;
|
||||||
|
related_id: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
precedent_request_metadata_api_precedent_library__case_law_id__request_metadata_post: {
|
precedent_request_metadata_api_precedent_library__case_law_id__request_metadata_post: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -5829,6 +6164,105 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
internal_decisions_upload_api_internal_decisions_upload_post: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"multipart/form-data": components["schemas"]["Body_internal_decisions_upload_api_internal_decisions_upload_post"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
internal_decisions_migrate_api_internal_decisions_migrate_post: {
|
||||||
|
parameters: {
|
||||||
|
query?: {
|
||||||
|
source?: string;
|
||||||
|
dry_run?: boolean;
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
internal_decisions_list_api_internal_decisions_get: {
|
||||||
|
parameters: {
|
||||||
|
query?: {
|
||||||
|
district?: string;
|
||||||
|
chair_name?: string;
|
||||||
|
practice_area?: string;
|
||||||
|
limit?: number;
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
halachot_list_api_halachot_get: {
|
halachot_list_api_halachot_get: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: {
|
query?: {
|
||||||
|
|||||||
@@ -89,6 +89,14 @@ export const caseUpdateSchema = z.object({
|
|||||||
.enum(expectedOutcomes.map((o) => o.value) as [string, ...string[]])
|
.enum(expectedOutcomes.map((o) => o.value) as [string, ...string[]])
|
||||||
.optional(),
|
.optional(),
|
||||||
status: z.string().optional(),
|
status: z.string().optional(),
|
||||||
|
appellants: z
|
||||||
|
.array(z.string().trim().min(1).refine((v) => hebrewPartyRe.test(v), "שם לא תקין"))
|
||||||
|
.optional(),
|
||||||
|
respondents: z
|
||||||
|
.array(z.string().trim().min(1).refine((v) => hebrewPartyRe.test(v), "שם לא תקין"))
|
||||||
|
.optional(),
|
||||||
|
property_address: z.string().trim().max(200).optional(),
|
||||||
|
permit_number: z.string().trim().max(100).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type CaseUpdateInput = z.infer<typeof caseUpdateSchema>;
|
export type CaseUpdateInput = z.infer<typeof caseUpdateSchema>;
|
||||||
|
|||||||
17
web/app.py
17
web/app.py
@@ -1240,6 +1240,10 @@ class CaseUpdateRequest(BaseModel):
|
|||||||
decision_date: str = ""
|
decision_date: str = ""
|
||||||
tags: list[str] | None = None
|
tags: list[str] | None = None
|
||||||
expected_outcome: str = ""
|
expected_outcome: str = ""
|
||||||
|
appellants: list[str] | None = None
|
||||||
|
respondents: list[str] | None = None
|
||||||
|
property_address: str = ""
|
||||||
|
permit_number: str = ""
|
||||||
|
|
||||||
|
|
||||||
@app.post("/api/cases/create")
|
@app.post("/api/cases/create")
|
||||||
@@ -1383,12 +1387,25 @@ async def api_case_update(case_number: str, req: CaseUpdateRequest, background_t
|
|||||||
decision_date=req.decision_date,
|
decision_date=req.decision_date,
|
||||||
tags=req.tags,
|
tags=req.tags,
|
||||||
expected_outcome=req.expected_outcome,
|
expected_outcome=req.expected_outcome,
|
||||||
|
appellants=req.appellants,
|
||||||
|
respondents=req.respondents,
|
||||||
|
property_address=req.property_address,
|
||||||
|
permit_number=req.permit_number,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
parsed = json.loads(result)
|
parsed = json.loads(result)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
raise HTTPException(404, result)
|
raise HTTPException(404, result)
|
||||||
|
|
||||||
|
# Paperclip sync: update project name when title changes (fire-and-forget).
|
||||||
|
old_title = (existing or {}).get("title", "")
|
||||||
|
if req.title and req.title != old_title:
|
||||||
|
background_tasks.add_task(
|
||||||
|
paperclip_client.update_project_name,
|
||||||
|
case_number=case_number,
|
||||||
|
new_title=req.title,
|
||||||
|
)
|
||||||
|
|
||||||
# Emit webhook when status changes (fire-and-forget via BackgroundTasks).
|
# Emit webhook when status changes (fire-and-forget via BackgroundTasks).
|
||||||
new_status = req.status
|
new_status = req.status
|
||||||
if new_status and old_status != new_status:
|
if new_status and old_status != new_status:
|
||||||
|
|||||||
@@ -231,6 +231,21 @@ async def restore_project(case_number: str) -> dict:
|
|||||||
await conn.close()
|
await conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def update_project_name(case_number: str, new_title: str) -> None:
|
||||||
|
"""Update the Paperclip project name when a case title changes."""
|
||||||
|
project_name = f"ערר {case_number} — {new_title}"[:200]
|
||||||
|
conn = await asyncpg.connect(PAPERCLIP_DB_URL)
|
||||||
|
try:
|
||||||
|
await conn.execute(
|
||||||
|
"UPDATE projects SET name = $1, updated_at = now() WHERE name LIKE $2",
|
||||||
|
project_name, f"%{case_number}%",
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
logger.warning("Failed to update Paperclip project name for case %s", case_number)
|
||||||
|
finally:
|
||||||
|
await conn.close()
|
||||||
|
|
||||||
|
|
||||||
async def _ensure_default_workspace(
|
async def _ensure_default_workspace(
|
||||||
conn: asyncpg.Connection,
|
conn: asyncpg.Connection,
|
||||||
project_id: str,
|
project_id: str,
|
||||||
|
|||||||
Reference in New Issue
Block a user