Files
kita-planer/src/actions/rsvp.ts
T

194 lines
5.6 KiB
TypeScript

"use server";
import { revalidatePath } from "next/cache";
import {
EventParticipationStatus,
RsvpMode,
TerminStatus,
UserRole,
} from "@prisma/client";
import { rsvpSubmissionSchema } from "@/lib/rsvp";
import { lockEvent } from "@/lib/event-lock";
import { requireKitaSession, requireRole } from "@/lib/auth-utils";
import { prisma } from "@/lib/prisma";
export type EventParticipantDto = {
childId: string;
childName: string;
familyName: string;
status: EventParticipationStatus | null;
};
export async function submitEventRsvp(
rawPayload: unknown,
): Promise<{ error?: string; success?: boolean }> {
const session = await requireKitaSession();
const parsed = rsvpSubmissionSchema.safeParse(rawPayload);
if (!parsed.success)
return {
error:
"Bitte alle Antworten und bei Familienzusagen die Anzahl Erwachsener angeben.",
};
if (!session.user.familyId || session.user.role === UserRole.ERZIEHER) {
return {
error: "Für Rückmeldungen ist ein Elternkonto mit Haushalt erforderlich.",
};
}
const kitaId = session.user.kitaId;
const familyId = session.user.familyId;
const payload = parsed.data;
try {
const result = await prisma.$transaction(async (tx) => {
await lockEvent(tx, payload.eventId, kitaId);
const event = await tx.termin.findFirst({
where: { id: payload.eventId, kitaId },
select: {
status: true,
requiresRsvp: true,
rsvpMode: true,
rsvpDeadline: true,
startDate: true,
kita: { select: { terminModuleEnabled: true } },
},
});
if (
!event?.kita.terminModuleEnabled ||
!event.requiresRsvp ||
event.status !== TerminStatus.CONFIRMED
) {
return { error: "Für diesen Termin ist keine Rückmeldung aktiv." };
}
if (event.rsvpMode !== payload.mode)
return {
error: "Die Antwortart wurde geändert. Bitte lade den Termin erneut.",
};
if (payload.mode === RsvpMode.FAMILY) {
const family = await tx.family.findFirst({
where: { id: familyId, kitaId },
select: { id: true },
});
if (!family) return { error: "Haushalt wurde nicht gefunden." };
if (
(event.rsvpDeadline ?? event.startDate) <= new Date() ||
event.startDate <= new Date()
) {
return { error: "Die Rückmeldefrist ist bereits abgelaufen." };
}
await tx.familyEventParticipation.upsert({
where: { eventId_familyId: { eventId: payload.eventId, familyId } },
create: {
kitaId,
eventId: payload.eventId,
familyId,
status: payload.status,
adultCount: payload.adultCount,
},
update: { status: payload.status, adultCount: payload.adultCount },
});
} else {
const children = await tx.child.findMany({
where: { kitaId, familyId, active: true },
select: { id: true },
});
const ids = new Set(children.map((child) => child.id));
if (
payload.responses.length !== ids.size ||
payload.responses.some((row) => !ids.has(row.childId))
) {
return {
error:
"Bitte antworte für alle eigenen aktiven Kinder. Lade den Termin bei Änderungen erneut.",
};
}
if (
(event.rsvpDeadline ?? event.startDate) <= new Date() ||
event.startDate <= new Date()
) {
return { error: "Die Rückmeldefrist ist bereits abgelaufen." };
}
for (const response of payload.responses) {
await tx.eventParticipation.upsert({
where: {
eventId_childId: {
eventId: payload.eventId,
childId: response.childId,
},
},
create: { kitaId, eventId: payload.eventId, ...response },
update: { status: response.status },
});
}
}
return { success: true };
});
if (result.error) return result;
revalidatePath("/dashboard");
revalidatePath("/dashboard/kalender");
revalidatePath("/dashboard/admin/kalender");
return result;
} catch (error) {
console.error("Rückmeldung konnte nicht gespeichert werden:", error);
return {
error:
"Rückmeldung konnte nicht gespeichert werden. Bitte versuche es erneut.",
};
}
}
export async function getEventParticipants(
eventId: string,
): Promise<{ participants?: EventParticipantDto[]; error?: string }> {
const session = await requireRole([UserRole.ADMIN, UserRole.KOORDINATOR]);
const kitaId = session.user.kitaId;
if (!kitaId) {
return { error: "Kein Mandant zugeordnet." };
}
const event = await prisma.termin.findFirst({
where: { id: eventId, kitaId },
select: { id: true },
});
if (!event) {
return { error: "Termin wurde nicht gefunden." };
}
const children = await prisma.child.findMany({
where: {
kitaId,
active: true,
},
select: {
id: true,
firstName: true,
lastName: true,
family: {
select: {
name: true,
},
},
eventParticipations: {
where: { eventId },
select: { status: true },
take: 1,
},
},
orderBy: [
{ family: { name: "asc" } },
{ lastName: "asc" },
{ firstName: "asc" },
],
});
return {
participants: children.map((child) => ({
childId: child.id,
childName: `${child.firstName} ${child.lastName}`,
familyName: child.family.name,
status: child.eventParticipations[0]?.status ?? null,
})),
};
}