'use server' import { revalidatePath } from 'next/cache' import type { z } from 'zod' import { projektSchema } from '@/lib/aufgabe' import { auditSchreiben } from '@/server/audit' import { NichtErlaubt, pruefeRolle } from '@/server/berechtigung' import { db } from '@/server/db' export type Rueckmeldung = | { ok: true; meldung: string; id?: string } | { ok: false; fehler: string; felder?: Record } function feldFehler(fehler: z.ZodError): Record { const felder: Record = {} for (const problem of fehler.issues) { const feld = problem.path.join('.') felder[feld] ??= problem.message } return felder } async function akteur() { return pruefeRolle('BENUTZER') } export async function projektAnlegen(daten: FormData): Promise { let ich try { ich = await akteur() } catch (fehler) { if (fehler instanceof NichtErlaubt) return { ok: false, fehler: fehler.message } throw fehler } const gelesen = projektSchema.safeParse(Object.fromEntries(daten.entries())) if (!gelesen.success) { return { ok: false, fehler: 'Bitte die markierten Felder prüfen.', felder: feldFehler(gelesen.error), } } const vorhanden = await db.projekt.findUnique({ where: { name: gelesen.data.name }, select: { id: true }, }) if (vorhanden) { return { ok: false, fehler: 'Diesen Namen gibt es schon.', felder: { name: 'Diesen Namen gibt es schon.' }, } } const projekt = await db.projekt.create({ data: gelesen.data, select: { id: true, name: true }, }) await auditSchreiben({ akteur: { id: ich.id, email: ich.email }, aktion: 'projekt.angelegt', objektTyp: 'Projekt', objektId: projekt.id, details: { name: projekt.name }, }) revalidatePath('/projekte') return { ok: true, id: projekt.id, meldung: `Projekt „${projekt.name}" angelegt.` } } export async function projektAendern(id: string, daten: FormData): Promise { let ich try { ich = await akteur() } catch (fehler) { if (fehler instanceof NichtErlaubt) return { ok: false, fehler: fehler.message } throw fehler } const gelesen = projektSchema.safeParse(Object.fromEntries(daten.entries())) if (!gelesen.success) { return { ok: false, fehler: 'Bitte die markierten Felder prüfen.', felder: feldFehler(gelesen.error), } } const vorher = await db.projekt.findUnique({ where: { id }, select: { id: true, name: true, status: true }, }) if (!vorher) return { ok: false, fehler: 'Dieses Projekt gibt es nicht mehr.' } const dublette = await db.projekt.findFirst({ where: { name: gelesen.data.name, id: { not: id } }, select: { id: true }, }) if (dublette) { return { ok: false, fehler: 'Diesen Namen gibt es schon.', felder: { name: 'Diesen Namen gibt es schon.' }, } } await db.projekt.update({ where: { id }, data: gelesen.data }) await auditSchreiben({ akteur: { id: ich.id, email: ich.email }, aktion: 'projekt.geaendert', objektTyp: 'Projekt', objektId: id, details: { vorher: vorher.name, nachher: gelesen.data.name, statusVorher: vorher.status, statusNachher: gelesen.data.status, }, }) revalidatePath('/projekte') revalidatePath(`/projekte/${id}`) return { ok: true, id, meldung: `Projekt „${gelesen.data.name}" gespeichert.` } } /** * Löscht ein Projekt. Die enthaltenen Aufgaben bleiben bestehen und verlieren * nur ihre Zuordnung — sonst verschwände mit dem Projekt auch die Arbeit. */ export async function projektLoeschen(daten: FormData): Promise { let ich try { ich = await akteur() } catch (fehler) { if (fehler instanceof NichtErlaubt) return { ok: false, fehler: fehler.message } throw fehler } const id = daten.get('id') if (typeof id !== 'string' || id.length === 0) { return { ok: false, fehler: 'Ungültige Anfrage.' } } const projekt = await db.projekt.findUnique({ where: { id }, select: { id: true, name: true, _count: { select: { aufgaben: true } } }, }) if (!projekt) return { ok: false, fehler: 'Dieses Projekt gibt es nicht mehr.' } await db.projekt.delete({ where: { id: projekt.id } }) await auditSchreiben({ akteur: { id: ich.id, email: ich.email }, aktion: 'projekt.geloescht', objektTyp: 'Projekt', objektId: projekt.id, details: { name: projekt.name, aufgabenGeloest: projekt._count.aufgaben }, }) revalidatePath('/projekte') revalidatePath('/aufgaben') return { ok: true, meldung: projekt._count.aufgaben > 0 ? `Projekt „${projekt.name}" gelöscht. ${projekt._count.aufgaben} Aufgabe(n) bleiben erhalten.` : `Projekt „${projekt.name}" gelöscht.`, } }