"use server"; import { revalidatePath } from "next/cache"; import { TerminStatus, TerminType, UserRole } from "@prisma/client"; import { z } from "zod"; import { requireKitaSession, requireRole } from "@/lib/auth-utils"; import { prisma } from "@/lib/prisma"; // ===================================================================== // /dashboard/kalender · Server Actions // ===================================================================== const terminSchema = z.object({ title: z.string().trim().min(2, "Titel muss mindestens 2 Zeichen lang sein"), description: z.string().trim().optional(), type: z.nativeEnum(TerminType), startDate: z.string().datetime(), endDate: z.string().datetime(), allDay: z.boolean().default(false), mitbringselListEnabled: z.boolean().default(false), requiresRsvp: z.boolean().default(false), rsvpDeadline: z.string().datetime().nullable().optional(), participantInfo: z.string().trim().max(1000).optional(), }); function buildTerminData(data: z.infer) { const startDate = new Date(data.startDate); const endDate = new Date(data.endDate); const now = new Date(); now.setSeconds(0, 0); if (startDate < now) { return { error: "Der Startzeitpunkt darf nicht in der Vergangenheit liegen." }; } if (endDate < startDate) { return { error: "Das Enddatum darf nicht vor dem Startdatum liegen." }; } return { data: { title: data.title, description: data.description || null, type: data.type, startDate, endDate, allDay: data.allDay, mitbringselListEnabled: data.mitbringselListEnabled, requiresRsvp: data.requiresRsvp, rsvpDeadline: data.requiresRsvp && data.rsvpDeadline ? new Date(data.rsvpDeadline) : null, participantInfo: data.requiresRsvp && data.participantInfo ? data.participantInfo : null, }, }; } export async function createTerminRequest(rawPayload: unknown) { const session = await requireKitaSession(); const parsed = terminSchema.safeParse(rawPayload); if (!parsed.success) { return { error: "Ungültige Eingabedaten." }; } const data = parsed.data; const terminData = buildTerminData(data); if ("error" in terminData) { return { error: terminData.error }; } try { await prisma.termin.create({ data: { kitaId: session.user.kitaId, createdById: session.user.id, title: terminData.data.title, description: terminData.data.description, type: terminData.data.type, startDate: terminData.data.startDate, endDate: terminData.data.endDate, allDay: terminData.data.allDay, status: TerminStatus.PENDING, }, }); revalidatePath("/dashboard/kalender"); return { success: true }; } catch (error) { console.error("Fehler beim Erstellen der Terminanfrage:", error); return { error: "Ein Fehler ist aufgetreten." }; } } export async function createTerminAdmin(rawPayload: unknown) { const session = await requireRole([UserRole.ADMIN, UserRole.KOORDINATOR]); const kitaId = session.user.kitaId; if (!kitaId) { return { error: "Kein Mandant zugeordnet." }; } const parsed = terminSchema.safeParse(rawPayload); if (!parsed.success) { return { error: "Ungültige Eingabedaten." }; } const data = parsed.data; const terminData = buildTerminData(data); if ("error" in terminData) { return { error: terminData.error }; } try { await prisma.termin.create({ data: { kitaId, createdById: session.user.id, ...terminData.data, status: TerminStatus.CONFIRMED, approvedById: session.user.id, approvedAt: new Date(), }, }); revalidatePath("/dashboard/kalender"); revalidatePath("/dashboard/admin/kalender"); return { success: true }; } catch (error) { console.error("Fehler beim Erstellen des Termins:", error); return { error: "Ein Fehler ist aufgetreten." }; } } export async function updateTerminAdmin(terminId: string, rawPayload: unknown) { const session = await requireRole([UserRole.ADMIN, UserRole.KOORDINATOR]); const kitaId = session.user.kitaId; if (!kitaId) { return { error: "Kein Mandant zugeordnet." }; } const parsed = terminSchema.safeParse(rawPayload); if (!parsed.success) { return { error: "Ungültige Eingabedaten." }; } const terminData = buildTerminData(parsed.data); if ("error" in terminData) { return { error: terminData.error }; } try { await prisma.termin.update({ where: { id: terminId, kitaId, }, data: terminData.data, }); revalidatePath("/dashboard/kalender"); revalidatePath("/dashboard/admin/kalender"); revalidatePath("/dashboard"); return { success: true }; } catch (error) { console.error("Fehler beim Aktualisieren des Termins:", error); return { error: "Termin konnte nicht aktualisiert werden." }; } } export async function approveTermin(terminId: string) { const session = await requireRole([UserRole.ADMIN, UserRole.KOORDINATOR]); const kitaId = session.user.kitaId; if (!kitaId) { return { error: "Kein Mandant zugeordnet." }; } try { await prisma.termin.update({ where: { id: terminId, kitaId, }, data: { status: TerminStatus.CONFIRMED, approvedById: session.user.id, approvedAt: new Date(), }, }); revalidatePath("/dashboard/kalender"); revalidatePath("/dashboard/admin/kalender"); return { success: true }; } catch (error) { console.error("Fehler beim Bestätigen des Termins:", error); return { error: "Ein Fehler ist aufgetreten." }; } } export async function rejectTermin(terminId: string, reason?: string) { const session = await requireRole([UserRole.ADMIN, UserRole.KOORDINATOR]); const kitaId = session.user.kitaId; if (!kitaId) { return { error: "Kein Mandant zugeordnet." }; } try { await prisma.termin.update({ where: { id: terminId, kitaId, }, data: { status: TerminStatus.REJECTED, rejectionReason: reason, approvedById: session.user.id, approvedAt: new Date(), }, }); revalidatePath("/dashboard/kalender"); revalidatePath("/dashboard/admin/kalender"); return { success: true }; } catch (error) { console.error("Fehler beim Ablehnen des Termins:", error); return { error: "Ein Fehler ist aufgetreten." }; } } export async function toggleMitbringselList(terminId: string, enabled: boolean) { const session = await requireRole([UserRole.ADMIN, UserRole.KOORDINATOR]); const kitaId = session.user.kitaId; if (!kitaId) { return { error: "Kein Mandant zugeordnet." }; } try { await prisma.termin.update({ where: { id: terminId, kitaId, }, data: { mitbringselListEnabled: enabled, }, }); revalidatePath("/dashboard/kalender"); revalidatePath("/dashboard/admin/kalender"); return { success: true }; } catch (error) { console.error("Fehler beim Umschalten der Mitbring-Liste:", error); return { error: "Ein Fehler ist aufgetreten." }; } } const mitbringselSchema = z.object({ terminId: z.string(), content: z.string().min(1, "Eintrag darf nicht leer sein"), }); export async function addMitbringsel(rawPayload: unknown) { const session = await requireKitaSession(); const parsed = mitbringselSchema.safeParse(rawPayload); if (!parsed.success) { return { error: "Ungültige Eingabedaten." }; } const { terminId, content } = parsed.data; try { // Verify termin is active and list is enabled const termin = await prisma.termin.findUnique({ where: { id: terminId, kitaId: session.user.kitaId }, }); if (!termin || termin.status !== TerminStatus.CONFIRMED || !termin.mitbringselListEnabled) { return { error: "Mitbring-Liste ist für diesen Termin nicht aktiv." }; } await prisma.mitbringselItem.create({ data: { kitaId: session.user.kitaId, terminId: terminId, userId: session.user.id, content: content, }, }); revalidatePath("/dashboard/kalender"); return { success: true }; } catch (error) { console.error("Fehler beim Hinzufügen des Eintrags:", error); return { error: "Ein Fehler ist aufgetreten." }; } } export async function deleteMitbringsel(itemId: string) { const session = await requireKitaSession(); try { const item = await prisma.mitbringselItem.findUnique({ where: { id: itemId, kitaId: session.user.kitaId }, }); if (!item) { return { error: "Eintrag nicht gefunden." }; } // Admins/Koords can delete any item. Users can only delete their own. if ( session.user.role !== UserRole.ADMIN && session.user.role !== UserRole.KOORDINATOR && item.userId !== session.user.id ) { return { error: "Keine Berechtigung diesen Eintrag zu löschen." }; } await prisma.mitbringselItem.delete({ where: { id: itemId }, }); revalidatePath("/dashboard/kalender"); return { success: true }; } catch (error) { console.error("Fehler beim Löschen des Eintrags:", error); return { error: "Ein Fehler ist aufgetreten." }; } }