Files
kita-planer/src/app/dashboard/kalender/actions.ts
T
2026-05-20 22:47:52 +02:00

338 lines
9.2 KiB
TypeScript

"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<typeof terminSchema>) {
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." };
}
}