'use server' import { revalidatePath } from 'next/cache' import { redirect } from 'next/navigation' import { z } from 'zod' import { requireUser } from '@/lib/auth/current' import { AccessError, ConflictError } from '@/lib/errors' import { createDocument, finalizeDocument, reclassifyDocument, retractDocument, saveDocumentSection, type Owner, } from '@/lib/documents/service' import { de } from '@/lib/i18n/de' import type { SaveSectionResult } from '@/components/sections/types' export type ActionResult = { ok: true } | { ok: false; error: string } function toMessage(error: unknown): string { if (error instanceof AccessError) { return de.actionErrors[error.code] ?? de.actionErrors.default! } console.error('[dokumente] unerwartet', error) return de.actionErrors.default! } const createSchema = z.object({ templateKey: z.string().min(1), title: z.string().max(200).optional(), classification: z.coerce.number().int().min(0).max(5), caseNumber: z.string().max(60).optional(), personnelFileId: z.string().max(64).optional(), }) export async function createDocumentAction( _prev: ActionResult | null, formData: FormData, ): Promise { const user = await requireUser() const parsed = createSchema.safeParse(Object.fromEntries(formData)) if (!parsed.success) return { ok: false, error: de.actionErrors.default! } const owner: Owner = parsed.data.caseNumber ? { kind: 'case', caseNumber: parsed.data.caseNumber } : parsed.data.personnelFileId ? { kind: 'personnel', personnelFileId: parsed.data.personnelFileId } : { kind: 'standalone' } let documentNumber: string try { const created = await createDocument(user.permissions, { templateKey: parsed.data.templateKey, title: parsed.data.title, classification: parsed.data.classification, owner, }) documentNumber = created.documentNumber } catch (error) { return { ok: false, error: toMessage(error) } } redirect(`/dokumente/${documentNumber}`) } const sectionSchema = z.object({ fieldKey: z.string().min(1), value: z.unknown(), baseVersion: z.coerce.number().int().min(0), minClearance: z.coerce.number().int().min(0).max(5).optional(), }) /** Das Dokument kommt gebunden herein, siehe `.bind`. */ export async function saveDocumentSectionAction( documentNumber: string, input: unknown, ): Promise { const user = await requireUser() const parsed = sectionSchema.safeParse(input) if (!parsed.success) return { ok: false, conflict: false, error: de.actionErrors.default! } try { const { version } = await saveDocumentSection(user.permissions, { documentNumber, ...parsed.data, }) revalidatePath(`/dokumente/${documentNumber}`) return { ok: true, version } } catch (error) { if (error instanceof ConflictError) { return { ok: false, conflict: true, current: { value: error.current.value, version: error.current.version }, } } return { ok: false, conflict: false, error: toMessage(error) } } } const numberSchema = z.object({ documentNumber: z.string().min(1) }) export async function finalizeDocumentAction(input: unknown): Promise { const user = await requireUser() const parsed = numberSchema.safeParse(input) if (!parsed.success) return { ok: false, error: de.actionErrors.default! } try { await finalizeDocument(user.permissions, parsed.data.documentNumber) revalidatePath(`/dokumente/${parsed.data.documentNumber}`) return { ok: true } } catch (error) { return { ok: false, error: toMessage(error) } } } const retractSchema = numberSchema.extend({ reason: z.string().min(10).max(2000) }) export async function retractDocumentAction(input: unknown): Promise { const user = await requireUser() const parsed = retractSchema.safeParse(input) if (!parsed.success) return { ok: false, error: de.actionErrors['reason-required']! } try { await retractDocument(user.permissions, parsed.data.documentNumber, parsed.data.reason) revalidatePath(`/dokumente/${parsed.data.documentNumber}`) return { ok: true } } catch (error) { return { ok: false, error: toMessage(error) } } } const reclassifySchema = numberSchema.extend({ to: z.coerce.number().int().min(0).max(5), reason: z.string().max(2000).optional(), }) export async function reclassifyDocumentAction(input: unknown): Promise { const user = await requireUser() const parsed = reclassifySchema.safeParse(input) if (!parsed.success) return { ok: false, error: de.actionErrors.default! } try { await reclassifyDocument( user.permissions, parsed.data.documentNumber, parsed.data.to, parsed.data.reason, ) revalidatePath(`/dokumente/${parsed.data.documentNumber}`) return { ok: true } } catch (error) { return { ok: false, error: toMessage(error) } } }