diff --git a/web-ui/src/app/cases/new/page.tsx b/web-ui/src/app/cases/new/page.tsx new file mode 100644 index 0000000..3fa0d32 --- /dev/null +++ b/web-ui/src/app/cases/new/page.tsx @@ -0,0 +1,26 @@ +import Link from "next/link"; +import { AppShell } from "@/components/app-shell"; +import { CaseWizard } from "@/components/wizard/case-wizard"; + +export default function NewCasePage() { + return ( + +
+
+ +

יצירת תיק ערר

+

+ שלושה שלבים קצרים — פרטי יסוד, צדדים, והשלמות. התיק ייווצר ב-DB + וב-Gitea מיד בשמירה. +

+
+ + +
+
+ ); +} diff --git a/web-ui/src/app/page.tsx b/web-ui/src/app/page.tsx index a8e3992..607232b 100644 --- a/web-ui/src/app/page.tsx +++ b/web-ui/src/app/page.tsx @@ -1,10 +1,12 @@ "use client"; +import Link from "next/link"; import { AppShell } from "@/components/app-shell"; import { KPICards } from "@/components/cases/kpi-cards"; import { StatusDonut } from "@/components/cases/status-donut"; import { CasesTable } from "@/components/cases/cases-table"; import { Card, CardContent } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; import { useCases } from "@/lib/api/cases"; export default function HomePage() { @@ -24,6 +26,9 @@ export default function HomePage() { 12 הבלוקים.

+
diff --git a/web-ui/src/components/wizard/case-wizard.tsx b/web-ui/src/components/wizard/case-wizard.tsx new file mode 100644 index 0000000..30b89fe --- /dev/null +++ b/web-ui/src/components/wizard/case-wizard.tsx @@ -0,0 +1,283 @@ +"use client"; + +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { useForm, Controller } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { toast } from "sonner"; +import { Card, CardContent } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Label } from "@/components/ui/label"; +import { + Select, SelectContent, SelectItem, SelectTrigger, SelectValue, +} from "@/components/ui/select"; +import { PartiesField } from "@/components/wizard/parties-field"; +import { useCreateCase } from "@/lib/api/cases"; +import { + caseCreateSchema, committeeTypes, expectedOutcomes, + type CaseCreateInput, +} from "@/lib/schemas/case"; + +const STEPS = [ + { key: "basics", label: "פרטי יסוד" }, + { key: "parties", label: "צדדים" }, + { key: "details", label: "השלמות" }, +] as const; + +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 = { + basics: ["case_number", "title", "committee_type"], + parties: ["appellants", "respondents"], + details: ["subject", "hearing_date", "expected_outcome", "notes", "property_address", "permit_number"], +}; + +function FieldError({ message }: { message?: string }) { + if (!message) return null; + return

{message}

; +} + +export function CaseWizard() { + const router = useRouter(); + const [step, setStep] = useState("basics"); + const mutate = useCreateCase(); + + const form = useForm({ + resolver: zodResolver(caseCreateSchema), + mode: "onBlur", + defaultValues: { + case_number: "", + title: "", + appellants: [], + respondents: [], + subject: "", + property_address: "", + permit_number: "", + committee_type: "ועדה מקומית", + hearing_date: "", + notes: "", + expected_outcome: "", + }, + }); + + const stepIndex = STEPS.findIndex((s) => s.key === step); + const isLast = stepIndex === STEPS.length - 1; + + const goNext = async () => { + const ok = await form.trigger(STEP_FIELDS[step]); + if (!ok) return; + setStep(STEPS[stepIndex + 1].key); + }; + const goBack = () => setStep(STEPS[stepIndex - 1].key); + + const onSubmit = form.handleSubmit(async (values) => { + try { + const res = await mutate.mutateAsync(values); + toast.success("תיק חדש נוצר"); + const created = res?.case_number || values.case_number; + router.push(`/cases/${encodeURIComponent(created)}`); + } catch (e) { + toast.error(e instanceof Error ? e.message : "שגיאה ביצירת תיק"); + } + }); + + return ( + + + {/* Stepper */} +
    + {STEPS.map((s, i) => { + const active = i === stepIndex; + const done = i < stepIndex; + return ( +
  1. + + {done ? "✓" : i + 1} + + + {s.label} + + {i < STEPS.length - 1 && ( + + )} +
  2. + ); + })} +
+ +
+ {step === "basics" && ( +
+
+ + + +
+
+ + + +
+
+ + ( + + )} + /> +
+
+ )} + + {step === "parties" && ( +
+ ( + + )} + /> +
+ ( + + )} + /> +
+ )} + + {step === "details" && ( +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + + +
+
+ + ( + + )} + /> +
+
+
+ +