194 lines
5.6 KiB
TypeScript
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,
|
|
})),
|
|
};
|
|
}
|