diff --git a/web/app/(app)/abteilungen/[key]/page.tsx b/web/app/(app)/abteilungen/[key]/page.tsx index 2579fea..60b7246 100644 --- a/web/app/(app)/abteilungen/[key]/page.tsx +++ b/web/app/(app)/abteilungen/[key]/page.tsx @@ -60,7 +60,7 @@ export default async function DepartmentPage({ params }: { params: Promise<{ key
- {abteilung.key} + {abteilung.key} {de.department.one} {abteilung.isArchived ? ( diff --git a/web/app/(app)/faelle/actions.ts b/web/app/(app)/faelle/actions.ts index 9471939..ed97057 100644 --- a/web/app/(app)/faelle/actions.ts +++ b/web/app/(app)/faelle/actions.ts @@ -9,7 +9,7 @@ import { editEntry, restoreEntry, retractEntry } from '@/lib/cases/entryService' import { addParticipant, removeParticipant } from '@/lib/cases/participants' import { linkCases, unlinkCases } from '@/lib/cases/links' import { toggleWatch } from '@/lib/cases/watch' -import { LINK_KINDS, PARTICIPANT_ROLES } from '@/lib/cases/terms' +import { LINK_KINDS, PARTICIPANT_ROLES, parseParticipantDrafts } from '@/lib/cases/terms' import { de } from '@/lib/i18n/de' export type ActionResult = { ok: true } | { ok: false; error: string } @@ -46,22 +46,28 @@ export async function createCaseAction( title: formData.get('title'), classification: formData.get('classification'), templateKey: formData.get('templateKey'), - personnelFileId: formData.get('personnelFileId') || undefined, }) if (!parsed.success) return { error: de.actionErrors.default! } + // Beliebig viele Beteiligte. Was nicht sauber ankommt, fällt weg: die Akte + // soll nicht an einer krummen Zeile scheitern. Siehe lib/cases/terms.ts. + const beteiligte = parseParticipantDrafts(formData.getAll('participants')) + let caseNumber: string try { const created = await createCase(user.permissions, parsed.data) caseNumber = created.caseNumber - // Die betroffene Person gleich mitverknüpfen, damit sie nicht in einem - // zweiten Schritt vergessen wird. Scheitert das, steht die Akte trotzdem. - if (parsed.data.personnelFileId) { + // Die Beteiligten gleich mitverknüpfen, damit sie nicht in einem zweiten + // Schritt vergessen werden. Scheitert einer, steht die Akte trotzdem — sie + // ist das Wichtigere, und nachtragen kann man auf der Aktenseite. + for (const eintrag of beteiligte) { await addParticipant(user.permissions, { caseNumber, - role: 'SUBJECT', - personnelFileId: parsed.data.personnelFileId, + role: eintrag.role, + ...(eintrag.kind === 'user' + ? { userId: eintrag.id } + : { personnelFileId: eintrag.id }), }) } } catch (error) { diff --git a/web/app/(app)/faelle/neu/NewCaseForm.tsx b/web/app/(app)/faelle/neu/NewCaseForm.tsx index 3a0193e..1a4f292 100644 --- a/web/app/(app)/faelle/neu/NewCaseForm.tsx +++ b/web/app/(app)/faelle/neu/NewCaseForm.tsx @@ -1,23 +1,48 @@ 'use client' -import { useActionState } from 'react' -import { caseText } from '@/lib/i18n/cases' +import { useActionState, useState } from 'react' +import { caseText, participantText } from '@/lib/i18n/cases' import { clearanceText } from '@/lib/i18n/common' +import { PARTICIPANT_ROLES } from '@/lib/cases/terms' import { createCaseAction, type CreateState } from '../actions' +/** Ein Beteiligter im Formular, bevor die Akte existiert. */ +type Entwurf = { schluessel: number; ziel: string; rolle: string } + export function NewCaseForm({ templates, levels, people, + members, defaultLevel, }: { templates: { key: string; name: string }[] levels: { level: number; name: string }[] people: { id: string; displayName: string }[] + members: { id: string; displayName: string }[] defaultLevel: number }) { const [state, action, pending] = useActionState(createCaseAction, {}) + // Beliebig viele Beteiligte, jeder mit seiner Rolle. Vorher gab es genau + // eine „betroffene Person" — wer einen Zeugen und einen Täter eintragen + // wollte, musste die Akte erst anlegen und dann noch einmal hinein. + const [beteiligte, setBeteiligte] = useState([]) + const [naechster, setNaechster] = useState(1) + + function hinzufuegen() { + setBeteiligte((bisher) => [...bisher, { schluessel: naechster, ziel: '', rolle: 'SUBJECT' }]) + setNaechster((n) => n + 1) + } + + function aendern(schluessel: number, teil: Partial) { + setBeteiligte((bisher) => + bisher.map((eintrag) => + eintrag.schluessel === schluessel ? { ...eintrag, ...teil } : eintrag, + ), + ) + } + return (
@@ -71,28 +96,79 @@ export function NewCaseForm({
- {people.length > 0 ? ( -
- - {/* Gleich beim Anlegen verknüpfen erspart den zweiten Schritt, den - man sonst vergisst. Nachträglich geht es genauso. */} - + + + + ))} - -
- ) : null} + + )} + + +
diff --git a/web/app/(app)/faelle/page.tsx b/web/app/(app)/faelle/page.tsx index b9ae0f1..f5424d5 100644 --- a/web/app/(app)/faelle/page.tsx +++ b/web/app/(app)/faelle/page.tsx @@ -4,6 +4,7 @@ import { db } from '@/lib/db' import { requireUser } from '@/lib/auth/current' import { visibleCaseWhere } from '@/lib/access/caseFilter' import { de } from '@/lib/i18n/de' +import { CASE_STATUSES } from '@/lib/cases/terms' import { StampMark, classificationEdgeStyle } from '@/components/StampMark' export const metadata: Metadata = { title: de.case.listHeading } @@ -72,18 +73,35 @@ export default async function CaseListPage({

{de.case.listHeading}

- {de.case.countLabel(total)} + {de.case.countLabel(total)}
-