'use server' import { revalidatePath, revalidateTag } from 'next/cache' import { redirect } from 'next/navigation' import { z } from 'zod' import { requireUser } from '@/lib/auth/current' import { AccessError, addEntry, changeStatus, createCase, reclassify } from '@/lib/cases/service' import { editEntry, restoreEntry, retractEntry } from '@/lib/cases/entryService' import { de } from '@/lib/i18n/de' export type ActionResult = { ok: true } | { ok: false; error: string } function toMessage(error: unknown): string { if (error instanceof AccessError) { return de.case.errors[error.code] ?? de.case.errors.default! } console.error('[faelle] unerwartet', error) return de.case.errors.default! } const createSchema = z.object({ title: z.string().trim().min(3).max(200), classification: z.coerce.number().int().min(0).max(5), templateKey: z.string().min(1), }) export type CreateState = { error?: string } /** * Wird über `useActionState` aufgerufen, damit Fehler an der Stelle stehen, an * der sie entstanden sind, und der Knopf einen Wartezustand bekommt. * Bei Erfolg leitet die Aktion auf die neue Akte weiter. */ export async function createCaseAction( _previous: CreateState, formData: FormData, ): Promise { const user = await requireUser() const parsed = createSchema.safeParse({ title: formData.get('title'), classification: formData.get('classification'), templateKey: formData.get('templateKey'), }) if (!parsed.success) return { error: de.case.errors.default! } let caseNumber: string try { const created = await createCase(user.permissions, parsed.data) caseNumber = created.caseNumber } catch (error) { return { error: toMessage(error) } } revalidatePath('/faelle') // redirect wirft, deshalb steht danach nichts mehr. redirect(`/faelle/${caseNumber}`) } const entrySchema = z.object({ id: z.uuid(), caseNumber: z.string().min(1), body: z.string().trim().min(1).max(20_000), minClearance: z.coerce.number().int().min(0).max(5).default(0), }) export async function addEntryAction(input: unknown): Promise { const user = await requireUser() const parsed = entrySchema.safeParse(input) if (!parsed.success) return { ok: false, error: de.case.errors.default! } try { await addEntry(user.permissions, parsed.data) } catch (error) { return { ok: false, error: toMessage(error) } } revalidateTag(`case:${parsed.data.caseNumber}`) revalidatePath(`/faelle/${parsed.data.caseNumber}`) return { ok: true } } const statusSchema = z.object({ caseNumber: z.string().min(1), to: z.enum(['DRAFT', 'OPEN', 'DORMANT', 'REVIEW', 'CLOSED', 'ARCHIVED', 'SEALED']), reason: z.string().trim().optional(), }) export async function changeStatusAction(input: unknown): Promise { const user = await requireUser() const parsed = statusSchema.safeParse(input) if (!parsed.success) return { ok: false, error: de.case.errors.default! } try { await changeStatus(user.permissions, parsed.data) } catch (error) { return { ok: false, error: toMessage(error) } } revalidatePath(`/faelle/${parsed.data.caseNumber}`) revalidatePath('/faelle') return { ok: true } } const reclassifySchema = z.object({ caseNumber: z.string().min(1), to: z.coerce.number().int().min(0).max(5), reason: z.string().trim().optional(), }) export async function reclassifyAction(input: unknown): Promise { const user = await requireUser() const parsed = reclassifySchema.safeParse(input) if (!parsed.success) return { ok: false, error: de.case.errors.default! } try { await reclassify(user.permissions, parsed.data) } catch (error) { return { ok: false, error: toMessage(error) } } revalidatePath(`/faelle/${parsed.data.caseNumber}`) revalidatePath('/faelle') return { ok: true } } const editSchema = z.object({ entryId: z.string().min(1), caseNumber: z.string().min(1), body: z.string().trim().min(1).max(20_000), minClearance: z.coerce.number().int().min(0).max(5).optional(), changeReason: z.string().trim().optional(), }) export async function editEntryAction(input: unknown): Promise { const user = await requireUser() const parsed = editSchema.safeParse(input) if (!parsed.success) return { ok: false, error: de.case.errors.default! } try { await editEntry(user.permissions, parsed.data) } catch (error) { return { ok: false, error: toMessage(error) } } revalidatePath(`/faelle/${parsed.data.caseNumber}`) return { ok: true } } const retractSchema = z.object({ entryId: z.string().min(1), caseNumber: z.string().min(1), reason: z.string().trim().min(10), }) export async function retractEntryAction(input: unknown): Promise { const user = await requireUser() const parsed = retractSchema.safeParse(input) if (!parsed.success) return { ok: false, error: de.entry.reasonTooShort } try { await retractEntry(user.permissions, parsed.data) } catch (error) { return { ok: false, error: toMessage(error) } } revalidatePath(`/faelle/${parsed.data.caseNumber}`) return { ok: true } } export async function restoreEntryAction(input: unknown): Promise { const user = await requireUser() const parsed = retractSchema.safeParse(input) if (!parsed.success) return { ok: false, error: de.entry.reasonTooShort } try { await restoreEntry(user.permissions, parsed.data) } catch (error) { return { ok: false, error: toMessage(error) } } revalidatePath(`/faelle/${parsed.data.caseNumber}`) return { ok: true } }