From 58d96864cccbe22fb343aa337fd2c626c2e80231 Mon Sep 17 00:00:00 2001 From: "CTO (LegalAI)" Date: Thu, 9 Apr 2026 11:31:11 +0000 Subject: [PATCH] feat: Frontend-Formulare fuer Entscheidungen und Normen anlegen - /entscheidungen/new: Formular fuer neue Entscheidungen (Typ, Gericht, Aktenzeichen, Datum, Leitsatz, Tenor, Sachverhalt, Gruende, Volltext, Rechtsgebiete, Schlagwoerter) - /normen/new: Formular fuer neue Regelwerke mit optionalen Paragraphen inline - POST /api/norms: Neue API-Route fuer normInstrument-Erstellung - Buttons auf den Listenseiten /entscheidungen und /normen Co-Authored-By: Paperclip --- .../(dashboard)/entscheidungen/new/page.tsx | 262 ++++++++++++++ src/app/(dashboard)/entscheidungen/page.tsx | 16 +- src/app/(dashboard)/normen/new/page.tsx | 324 ++++++++++++++++++ src/app/(dashboard)/normen/page.tsx | 10 +- src/app/api/norms/route.ts | 45 +++ 5 files changed, 652 insertions(+), 5 deletions(-) create mode 100644 src/app/(dashboard)/entscheidungen/new/page.tsx create mode 100644 src/app/(dashboard)/normen/new/page.tsx create mode 100644 src/app/api/norms/route.ts diff --git a/src/app/(dashboard)/entscheidungen/new/page.tsx b/src/app/(dashboard)/entscheidungen/new/page.tsx new file mode 100644 index 0000000..591c314 --- /dev/null +++ b/src/app/(dashboard)/entscheidungen/new/page.tsx @@ -0,0 +1,262 @@ +'use client'; + +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import Link from 'next/link'; + +const DECISION_TYPES = [ + { value: 'schiedsspruch', label: 'Schiedsspruch' }, + { value: 'urteil', label: 'Urteil' }, + { value: 'beschluss', label: 'Beschluss' }, + { value: 'vergleich', label: 'Vergleich' }, + { value: 'einstweilige_verfuegung', label: 'Einstweilige Verfügung' }, +]; + +export default function NewDecisionPage() { + const router = useRouter(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + setError(''); + setLoading(true); + + const form = new FormData(e.currentTarget); + const domainsRaw = (form.get('domains') as string) || ''; + const keywordsRaw = (form.get('keywords') as string) || ''; + + const body = { + type: form.get('type'), + court: form.get('court'), + caseReference: form.get('caseReference') || undefined, + decisionDate: form.get('decisionDate'), + chamber: form.get('chamber') || undefined, + headnote: form.get('headnote') || undefined, + tenor: form.get('tenor') || undefined, + facts: form.get('facts') || undefined, + reasoning: form.get('reasoning') || undefined, + fullText: form.get('fullText') || undefined, + domains: domainsRaw ? domainsRaw.split(',').map((s) => s.trim()).filter(Boolean) : [], + keywords: keywordsRaw ? keywordsRaw.split(',').map((s) => s.trim()).filter(Boolean) : [], + }; + + try { + const res = await fetch('/api/decisions', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + + if (!res.ok) { + const data = await res.json().catch(() => null); + throw new Error(data?.error ?? 'Entscheidung konnte nicht angelegt werden.'); + } + + const data = await res.json(); + router.push(`/entscheidungen/${data.decision.id}`); + } catch (err) { + setError(err instanceof Error ? err.message : 'Ein Fehler ist aufgetreten.'); + setLoading(false); + } + } + + const inputClass = + 'w-full rounded-lg border border-card-border px-3 py-2.5 text-sm bg-white focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary'; + + return ( +
+
+ + Entscheidungen + + / + Neue Entscheidung +
+ +

Neue Entscheidung anlegen

+ + {error && ( +
+ {error} +
+ )} + +
+
+
+ + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+ +