'use server' import { revalidatePath } from 'next/cache' import { z } from 'zod' import { requireUser } from '@/lib/auth/current' import { AccessError } from '@/lib/errors' import { approveExport, rejectExport, requestExport, runExport, type ExportTarget, } from '@/lib/export/service' import { de } from '@/lib/i18n/de' export type ExportState = { jobId?: string; status?: string; error?: string } function toMessage(error: unknown): string { if (error instanceof AccessError) { return de.actionErrors[error.code] ?? de.actionErrors.default! } console.error('[export] unerwartet', error) return de.actionErrors.default! } const requestSchema = z.union([ z.object({ kind: z.literal('CASE'), caseNumber: z.string().min(1) }), z.object({ kind: z.literal('DOCUMENT'), documentNumber: z.string().min(1) }), ]) /** * Export anfordern. * * Der Auftrag läuft im Hintergrund weiter, nachdem die Antwort raus ist — das * Rendern dauert, und niemand soll auf einen Fortschrittsbalken starren, der * auf Playwright wartet. Siehe docs/09-performance-und-ux.md. */ export async function requestExportAction( _prev: ExportState | null, formData: FormData, ): Promise { const user = await requireUser() const parsed = requestSchema.safeParse(Object.fromEntries(formData)) if (!parsed.success) return { error: de.actionErrors.default! } try { const auftrag = await requestExport(user.permissions, parsed.data as ExportTarget) if (auftrag.status === 'QUEUED') void runExport(auftrag.jobId) return { jobId: auftrag.jobId, status: auftrag.status } } catch (error) { return { error: toMessage(error) } } } const idSchema = z.object({ id: z.string().min(1) }) export async function approveExportAction(input: unknown): Promise { const user = await requireUser() const parsed = idSchema.safeParse(input) if (!parsed.success) return { error: de.actionErrors.default! } try { await approveExport(user.permissions, parsed.data.id) // Erst nach der Freigabe wird gerendert. void runExport(parsed.data.id) revalidatePath('/uebersicht') return { jobId: parsed.data.id, status: 'QUEUED' } } catch (error) { return { error: toMessage(error) } } } const rejectSchema = idSchema.extend({ reason: z.string().min(10).max(500) }) export async function rejectExportAction(input: unknown): Promise { const user = await requireUser() const parsed = rejectSchema.safeParse(input) if (!parsed.success) return { error: de.actionErrors['reason-required']! } try { await rejectExport(user.permissions, parsed.data.id, parsed.data.reason) revalidatePath('/uebersicht') return { jobId: parsed.data.id, status: 'REJECTED' } } catch (error) { return { error: toMessage(error) } } }