Phase 4a: shadcn form primitives + case inline edit
Add dialog/select/textarea/label/progress/sonner components and wire
a Toaster into Providers. New zod schemas in lib/schemas/case.ts
mirror CaseCreateRequest/CaseUpdateRequest and feed react-hook-form
validation.
CaseEditDialog on the case detail Actions tab posts PUT /api/cases/{n}
with optimistic cache patching via useUpdateCase, showing toast
feedback on success/error.
shadcn's <Form> registry entry skipped at init (missing from the
nova preset); the dialog uses RHF directly against the same Input/
Textarea/Select primitives.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
82
web-ui/src/lib/schemas/case.ts
Normal file
82
web-ui/src/lib/schemas/case.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Zod schemas for case mutations (create / update).
|
||||
* Shapes mirror the FastAPI Pydantic models in web/app.py:
|
||||
* - CaseCreateRequest
|
||||
* - CaseUpdateRequest
|
||||
*
|
||||
* Validation rules are stricter on the UI than on the backend so the user
|
||||
* gets Hebrew-localized error messages at the field level instead of a
|
||||
* 422 blob from FastAPI.
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
/* Appeal numbers follow the 1xxx / 8xxx / 9xxx convention from CLAUDE.md —
|
||||
* permissive regex that still catches obvious typos. */
|
||||
const caseNumberRe = /^[1-9]\d{3,}(?:[-/][\w\u0590-\u05FF]+)*$/;
|
||||
|
||||
const hebrewPartyRe = /[\u0590-\u05FFA-Za-z]/;
|
||||
|
||||
const dateString = z
|
||||
.string()
|
||||
.trim()
|
||||
.refine((v) => v === "" || /^\d{4}-\d{2}-\d{2}$/.test(v), {
|
||||
message: "תאריך חייב להיות בפורמט YYYY-MM-DD",
|
||||
});
|
||||
|
||||
export const committeeTypes = [
|
||||
"ועדה מקומית",
|
||||
"ועדה מחוזית",
|
||||
"ועדת ערר",
|
||||
] as const;
|
||||
|
||||
export const expectedOutcomes = [
|
||||
{ value: "", label: "— לא נקבע —" },
|
||||
{ value: "rejection", label: "דחייה" },
|
||||
{ value: "partial_acceptance", label: "קבלה חלקית" },
|
||||
{ value: "full_acceptance", label: "קבלה מלאה" },
|
||||
{ value: "betterment_levy", label: "היטל השבחה" },
|
||||
] as const;
|
||||
|
||||
export const caseCreateSchema = z.object({
|
||||
case_number: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "שדה חובה")
|
||||
.regex(caseNumberRe, "מספר תיק לא תקין (למשל 1234 או 8001/2026)"),
|
||||
title: z.string().trim().min(3, "כותרת קצרה מדי").max(200, "כותרת ארוכה מדי"),
|
||||
appellants: z
|
||||
.array(z.string().trim().min(1).refine((v) => hebrewPartyRe.test(v), "שם לא תקין"))
|
||||
.min(1, "חייב להיות לפחות עורר אחד"),
|
||||
respondents: z
|
||||
.array(z.string().trim().min(1).refine((v) => hebrewPartyRe.test(v), "שם לא תקין"))
|
||||
.min(1, "חייב להיות לפחות משיב אחד"),
|
||||
subject: z.string().trim().max(500).default(""),
|
||||
property_address: z.string().trim().max(200).default(""),
|
||||
permit_number: z.string().trim().max(100).default(""),
|
||||
committee_type: z.enum(committeeTypes).default("ועדה מקומית"),
|
||||
hearing_date: dateString.default(""),
|
||||
notes: z.string().trim().max(2000).default(""),
|
||||
expected_outcome: z
|
||||
.enum(expectedOutcomes.map((o) => o.value) as [string, ...string[]])
|
||||
.default(""),
|
||||
});
|
||||
|
||||
export type CaseCreateInput = z.infer<typeof caseCreateSchema>;
|
||||
|
||||
/* Update schema — all fields optional so the PUT can be used for a
|
||||
* single-field edit. Empty strings are tolerated (they mean "no change"
|
||||
* in the Pydantic model). */
|
||||
export const caseUpdateSchema = z.object({
|
||||
title: z.string().trim().max(200).optional(),
|
||||
subject: z.string().trim().max(500).optional(),
|
||||
notes: z.string().trim().max(2000).optional(),
|
||||
hearing_date: dateString.optional(),
|
||||
decision_date: dateString.optional(),
|
||||
expected_outcome: z
|
||||
.enum(expectedOutcomes.map((o) => o.value) as [string, ...string[]])
|
||||
.optional(),
|
||||
status: z.string().optional(),
|
||||
});
|
||||
|
||||
export type CaseUpdateInput = z.infer<typeof caseUpdateSchema>;
|
||||
Reference in New Issue
Block a user