feat: implement emergency duty (Notdienst) swapping for parents
This commit is contained in:
@@ -1,10 +1,22 @@
|
||||
"use server";
|
||||
|
||||
import { createElement } from "react";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { TerminStatus, TerminType, UserRole } from "@prisma/client";
|
||||
import {
|
||||
NotdienstAlertStatus,
|
||||
NotdienstPlanStatus,
|
||||
NotdienstSwapStatus,
|
||||
TerminStatus,
|
||||
TerminType,
|
||||
UserRole,
|
||||
} from "@prisma/client";
|
||||
import { format } from "date-fns";
|
||||
import { de } from "date-fns/locale";
|
||||
import { z } from "zod";
|
||||
|
||||
import { NotdienstSwapEmail } from "@/emails/NotdienstSwapEmail";
|
||||
import { requireKitaSession, requireRole } from "@/lib/auth-utils";
|
||||
import { sendAppEmail } from "@/lib/mail";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// =====================================================================
|
||||
@@ -24,6 +36,223 @@ const terminSchema = z.object({
|
||||
participantInfo: z.string().trim().max(1000).optional(),
|
||||
});
|
||||
|
||||
const swapRequestSchema = z.object({
|
||||
targetAssignmentId: z.string().min(1),
|
||||
offeredAssignmentId: z.string().min(1),
|
||||
message: z.string().trim().max(500).optional(),
|
||||
});
|
||||
|
||||
const swapIdSchema = z.string().min(1);
|
||||
|
||||
const activeNotdienstAlertStatuses = [
|
||||
NotdienstAlertStatus.PENDING,
|
||||
NotdienstAlertStatus.CONFIRMED,
|
||||
];
|
||||
|
||||
function getTodayUtcDateOnly() {
|
||||
const today = new Date();
|
||||
return new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate()));
|
||||
}
|
||||
|
||||
function getBaseUrl() {
|
||||
return process.env.NEXTAUTH_URL ?? process.env.AUTH_URL ?? "http://localhost:3000";
|
||||
}
|
||||
|
||||
function formatNotdienstDate(date: Date) {
|
||||
return format(date, "EEEE, dd. MMMM yyyy", { locale: de });
|
||||
}
|
||||
|
||||
function revalidateCalendarAndNotdienst() {
|
||||
revalidatePath("/dashboard/kalender");
|
||||
revalidatePath("/dashboard/notdienst/plan");
|
||||
revalidatePath("/dashboard/notdienst");
|
||||
revalidatePath("/dashboard");
|
||||
}
|
||||
|
||||
type SwapAssignmentWithFamily = {
|
||||
id: string;
|
||||
childId: string;
|
||||
date: Date;
|
||||
plan: {
|
||||
status: NotdienstPlanStatus;
|
||||
};
|
||||
child: {
|
||||
familyId: string;
|
||||
family: {
|
||||
name: string;
|
||||
users: {
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
alerts: {
|
||||
id: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
async function getSwapAssignment(
|
||||
assignmentId: string,
|
||||
kitaId: string,
|
||||
): Promise<SwapAssignmentWithFamily | null> {
|
||||
return prisma.notdienstAssignment.findFirst({
|
||||
where: { id: assignmentId, kitaId },
|
||||
select: {
|
||||
id: true,
|
||||
childId: true,
|
||||
date: true,
|
||||
plan: { select: { status: true } },
|
||||
child: {
|
||||
select: {
|
||||
familyId: true,
|
||||
family: {
|
||||
select: {
|
||||
name: true,
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN },
|
||||
select: {
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
alerts: {
|
||||
where: { status: { in: activeNotdienstAlertStatuses } },
|
||||
select: { id: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function validateSwapAssignmentPair({
|
||||
targetAssignment,
|
||||
offeredAssignment,
|
||||
requesterFamilyId,
|
||||
today,
|
||||
}: {
|
||||
targetAssignment: SwapAssignmentWithFamily | null;
|
||||
offeredAssignment: SwapAssignmentWithFamily | null;
|
||||
requesterFamilyId: string;
|
||||
today: Date;
|
||||
}) {
|
||||
if (!targetAssignment || !offeredAssignment) {
|
||||
return "Notdienst nicht gefunden.";
|
||||
}
|
||||
|
||||
if (targetAssignment.id === offeredAssignment.id) {
|
||||
return "Bitte wähle zwei unterschiedliche Notdienste aus.";
|
||||
}
|
||||
|
||||
if (
|
||||
targetAssignment.plan.status !== NotdienstPlanStatus.PUBLISHED ||
|
||||
offeredAssignment.plan.status !== NotdienstPlanStatus.PUBLISHED
|
||||
) {
|
||||
return "Es können nur veröffentlichte Notdienste getauscht werden.";
|
||||
}
|
||||
|
||||
if (targetAssignment.date < today || offeredAssignment.date < today) {
|
||||
return "Vergangene Notdienste können nicht getauscht werden.";
|
||||
}
|
||||
|
||||
if (offeredAssignment.child.familyId !== requesterFamilyId) {
|
||||
return "Du kannst nur eigene Notdienste als Tausch anbieten.";
|
||||
}
|
||||
|
||||
if (targetAssignment.child.familyId === requesterFamilyId) {
|
||||
return "Du kannst keinen Tausch mit deinem eigenen Haushalt anfragen.";
|
||||
}
|
||||
|
||||
if (targetAssignment.alerts.length > 0 || offeredAssignment.alerts.length > 0) {
|
||||
return "Notdienste mit aktiver Alarmierung können nicht getauscht werden.";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function sendSwapMail({
|
||||
to,
|
||||
parentName,
|
||||
subject,
|
||||
heading,
|
||||
preview,
|
||||
kitaName,
|
||||
body,
|
||||
targetDate,
|
||||
offeredDate,
|
||||
}: {
|
||||
to: string;
|
||||
parentName: string;
|
||||
subject: string;
|
||||
heading: string;
|
||||
preview: string;
|
||||
kitaName: string;
|
||||
body: string;
|
||||
targetDate: Date;
|
||||
offeredDate: Date;
|
||||
}) {
|
||||
const result = await sendAppEmail({
|
||||
to,
|
||||
subject,
|
||||
react: createElement(NotdienstSwapEmail, {
|
||||
parentName,
|
||||
heading,
|
||||
preview,
|
||||
kitaName,
|
||||
body,
|
||||
targetDateLabel: formatNotdienstDate(targetDate),
|
||||
offeredDateLabel: formatNotdienstDate(offeredDate),
|
||||
link: `${getBaseUrl()}/dashboard/kalender`,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
console.error("Notdienst-Tausch-Mail konnte nicht versendet werden:", result.error);
|
||||
}
|
||||
}
|
||||
|
||||
async function notifyFamilyAboutSwap({
|
||||
family,
|
||||
subject,
|
||||
heading,
|
||||
preview,
|
||||
kitaName,
|
||||
body,
|
||||
targetDate,
|
||||
offeredDate,
|
||||
}: {
|
||||
family: SwapAssignmentWithFamily["child"]["family"];
|
||||
subject: string;
|
||||
heading: string;
|
||||
preview: string;
|
||||
kitaName: string;
|
||||
body: string;
|
||||
targetDate: Date;
|
||||
offeredDate: Date;
|
||||
}) {
|
||||
await Promise.all(
|
||||
family.users
|
||||
.filter((parent) => !!parent.email)
|
||||
.map((parent) =>
|
||||
sendSwapMail({
|
||||
to: parent.email,
|
||||
parentName: `${parent.firstName} ${parent.lastName}`,
|
||||
subject,
|
||||
heading,
|
||||
preview,
|
||||
kitaName,
|
||||
body,
|
||||
targetDate,
|
||||
offeredDate,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function buildTerminData(data: z.infer<typeof terminSchema>) {
|
||||
const startDate = new Date(data.startDate);
|
||||
const endDate = new Date(data.endDate);
|
||||
@@ -60,6 +289,424 @@ function buildTerminData(data: z.infer<typeof terminSchema>) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function createNotdienstSwapRequest(rawPayload: unknown) {
|
||||
const session = await requireKitaSession();
|
||||
if (session.user.role !== UserRole.ELTERN) {
|
||||
return { error: "Nur Eltern können Notdienst-Tausche anfragen." };
|
||||
}
|
||||
if (!session.user.familyId) {
|
||||
return { error: "Dein Account ist noch keinem Haushalt zugeordnet." };
|
||||
}
|
||||
|
||||
const parsed = swapRequestSchema.safeParse(rawPayload);
|
||||
if (!parsed.success) {
|
||||
return { error: "Ungültige Eingabedaten." };
|
||||
}
|
||||
|
||||
const { targetAssignmentId, offeredAssignmentId, message } = parsed.data;
|
||||
const today = getTodayUtcDateOnly();
|
||||
|
||||
try {
|
||||
const [targetAssignment, offeredAssignment, kita, existingRequest] =
|
||||
await Promise.all([
|
||||
getSwapAssignment(targetAssignmentId, session.user.kitaId),
|
||||
getSwapAssignment(offeredAssignmentId, session.user.kitaId),
|
||||
prisma.kita.findUnique({
|
||||
where: { id: session.user.kitaId },
|
||||
select: { name: true },
|
||||
}),
|
||||
prisma.notdienstSwapRequest.findFirst({
|
||||
where: {
|
||||
kitaId: session.user.kitaId,
|
||||
status: NotdienstSwapStatus.PENDING,
|
||||
OR: [
|
||||
{ targetAssignmentId },
|
||||
{ offeredAssignmentId },
|
||||
{ targetAssignmentId: offeredAssignmentId },
|
||||
{ offeredAssignmentId: targetAssignmentId },
|
||||
],
|
||||
},
|
||||
select: { id: true },
|
||||
}),
|
||||
]);
|
||||
|
||||
const validationError = validateSwapAssignmentPair({
|
||||
targetAssignment,
|
||||
offeredAssignment,
|
||||
requesterFamilyId: session.user.familyId,
|
||||
today,
|
||||
});
|
||||
if (validationError) {
|
||||
return { error: validationError };
|
||||
}
|
||||
if (!targetAssignment || !offeredAssignment || !kita) {
|
||||
return { error: "Notdienst nicht gefunden." };
|
||||
}
|
||||
if (existingRequest) {
|
||||
return { error: "Für einen dieser Notdienste gibt es bereits eine offene Tauschanfrage." };
|
||||
}
|
||||
|
||||
await prisma.notdienstSwapRequest.create({
|
||||
data: {
|
||||
kitaId: session.user.kitaId,
|
||||
requesterUserId: session.user.id,
|
||||
requesterFamilyId: session.user.familyId,
|
||||
targetAssignmentId,
|
||||
offeredAssignmentId,
|
||||
message: message || null,
|
||||
},
|
||||
});
|
||||
|
||||
await notifyFamilyAboutSwap({
|
||||
family: targetAssignment.child.family,
|
||||
subject: `Notdienst-Tauschanfrage für ${formatNotdienstDate(targetAssignment.date)}`,
|
||||
heading: "Neue Tauschanfrage",
|
||||
preview: "Du hast eine neue Notdienst-Tauschanfrage.",
|
||||
kitaName: kita.name,
|
||||
body: `Ein Elternteil aus einem anderen Haushalt möchte einen Notdienst mit dir tauschen.${message ? ` Nachricht: ${message}` : ""}`,
|
||||
targetDate: targetAssignment.date,
|
||||
offeredDate: offeredAssignment.date,
|
||||
});
|
||||
|
||||
revalidateCalendarAndNotdienst();
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Erstellen der Notdienst-Tauschanfrage:", error);
|
||||
return { error: "Tauschanfrage konnte nicht erstellt werden." };
|
||||
}
|
||||
}
|
||||
|
||||
export async function acceptNotdienstSwapRequest(requestIdRaw: unknown) {
|
||||
const session = await requireKitaSession();
|
||||
if (!session.user.familyId) {
|
||||
return { error: "Dein Account ist noch keinem Haushalt zugeordnet." };
|
||||
}
|
||||
|
||||
const parsed = swapIdSchema.safeParse(requestIdRaw);
|
||||
if (!parsed.success) {
|
||||
return { error: "Ungültige Eingabedaten." };
|
||||
}
|
||||
|
||||
const today = getTodayUtcDateOnly();
|
||||
|
||||
try {
|
||||
const result = await prisma.$transaction(async (tx) => {
|
||||
const request = await tx.notdienstSwapRequest.findFirst({
|
||||
where: {
|
||||
id: parsed.data,
|
||||
kitaId: session.user.kitaId,
|
||||
status: NotdienstSwapStatus.PENDING,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
requesterFamilyId: true,
|
||||
targetAssignmentId: true,
|
||||
offeredAssignmentId: true,
|
||||
targetAssignment: {
|
||||
select: {
|
||||
id: true,
|
||||
childId: true,
|
||||
date: true,
|
||||
plan: { select: { status: true } },
|
||||
child: {
|
||||
select: {
|
||||
familyId: true,
|
||||
family: {
|
||||
select: {
|
||||
name: true,
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN },
|
||||
select: { email: true, firstName: true, lastName: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
alerts: {
|
||||
where: { status: { in: activeNotdienstAlertStatuses } },
|
||||
select: { id: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
offeredAssignment: {
|
||||
select: {
|
||||
id: true,
|
||||
childId: true,
|
||||
date: true,
|
||||
plan: { select: { status: true } },
|
||||
child: {
|
||||
select: {
|
||||
familyId: true,
|
||||
family: {
|
||||
select: {
|
||||
name: true,
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN },
|
||||
select: { email: true, firstName: true, lastName: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
alerts: {
|
||||
where: { status: { in: activeNotdienstAlertStatuses } },
|
||||
select: { id: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!request) {
|
||||
throw new Error("Tauschanfrage nicht gefunden oder nicht mehr offen.");
|
||||
}
|
||||
|
||||
if (request.targetAssignment.child.familyId !== session.user.familyId) {
|
||||
throw new Error("Nur der angefragte Haushalt kann diese Anfrage annehmen.");
|
||||
}
|
||||
|
||||
const validationError = validateSwapAssignmentPair({
|
||||
targetAssignment: request.targetAssignment,
|
||||
offeredAssignment: request.offeredAssignment,
|
||||
requesterFamilyId: request.requesterFamilyId,
|
||||
today,
|
||||
});
|
||||
if (validationError) {
|
||||
throw new Error(validationError);
|
||||
}
|
||||
|
||||
await tx.notdienstAssignment.update({
|
||||
where: { id: request.targetAssignmentId },
|
||||
data: { childId: request.offeredAssignment.childId },
|
||||
});
|
||||
await tx.notdienstAssignment.update({
|
||||
where: { id: request.offeredAssignmentId },
|
||||
data: { childId: request.targetAssignment.childId },
|
||||
});
|
||||
await tx.notdienstSwapRequest.update({
|
||||
where: { id: request.id },
|
||||
data: {
|
||||
status: NotdienstSwapStatus.ACCEPTED,
|
||||
respondedById: session.user.id,
|
||||
respondedAt: new Date(),
|
||||
},
|
||||
});
|
||||
await tx.notdienstSwapRequest.updateMany({
|
||||
where: {
|
||||
id: { not: request.id },
|
||||
kitaId: session.user.kitaId,
|
||||
status: NotdienstSwapStatus.PENDING,
|
||||
OR: [
|
||||
{ targetAssignmentId: { in: [request.targetAssignmentId, request.offeredAssignmentId] } },
|
||||
{ offeredAssignmentId: { in: [request.targetAssignmentId, request.offeredAssignmentId] } },
|
||||
],
|
||||
},
|
||||
data: {
|
||||
status: NotdienstSwapStatus.CANCELLED,
|
||||
respondedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
targetAssignment: request.targetAssignment,
|
||||
offeredAssignment: request.offeredAssignment,
|
||||
};
|
||||
});
|
||||
|
||||
const kita = await prisma.kita.findUnique({
|
||||
where: { id: session.user.kitaId },
|
||||
select: { name: true },
|
||||
});
|
||||
if (kita) {
|
||||
await Promise.all([
|
||||
notifyFamilyAboutSwap({
|
||||
family: result.targetAssignment.child.family,
|
||||
subject: "Notdienst-Tausch bestätigt",
|
||||
heading: "Tausch bestätigt",
|
||||
preview: "Ein Notdienst-Tausch wurde bestätigt.",
|
||||
kitaName: kita.name,
|
||||
body: "die Tauschanfrage wurde angenommen. Der Notdienst-Plan wurde automatisch aktualisiert.",
|
||||
targetDate: result.targetAssignment.date,
|
||||
offeredDate: result.offeredAssignment.date,
|
||||
}),
|
||||
notifyFamilyAboutSwap({
|
||||
family: result.offeredAssignment.child.family,
|
||||
subject: "Notdienst-Tausch bestätigt",
|
||||
heading: "Tausch bestätigt",
|
||||
preview: "Ein Notdienst-Tausch wurde bestätigt.",
|
||||
kitaName: kita.name,
|
||||
body: "deine Tauschanfrage wurde angenommen. Der Notdienst-Plan wurde automatisch aktualisiert.",
|
||||
targetDate: result.targetAssignment.date,
|
||||
offeredDate: result.offeredAssignment.date,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
revalidateCalendarAndNotdienst();
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
return {
|
||||
error: error instanceof Error ? error.message : "Tauschanfrage konnte nicht angenommen werden.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function declineNotdienstSwapRequest(requestIdRaw: unknown) {
|
||||
const session = await requireKitaSession();
|
||||
if (!session.user.familyId) {
|
||||
return { error: "Dein Account ist noch keinem Haushalt zugeordnet." };
|
||||
}
|
||||
|
||||
const parsed = swapIdSchema.safeParse(requestIdRaw);
|
||||
if (!parsed.success) {
|
||||
return { error: "Ungültige Eingabedaten." };
|
||||
}
|
||||
|
||||
try {
|
||||
const request = await prisma.notdienstSwapRequest.findFirst({
|
||||
where: {
|
||||
id: parsed.data,
|
||||
kitaId: session.user.kitaId,
|
||||
status: NotdienstSwapStatus.PENDING,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
targetAssignment: {
|
||||
select: {
|
||||
date: true,
|
||||
child: {
|
||||
select: {
|
||||
familyId: true,
|
||||
family: {
|
||||
select: {
|
||||
name: true,
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN },
|
||||
select: { email: true, firstName: true, lastName: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
offeredAssignment: {
|
||||
select: {
|
||||
date: true,
|
||||
child: {
|
||||
select: {
|
||||
family: {
|
||||
select: {
|
||||
name: true,
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN },
|
||||
select: { email: true, firstName: true, lastName: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!request) {
|
||||
return { error: "Tauschanfrage nicht gefunden oder nicht mehr offen." };
|
||||
}
|
||||
if (request.targetAssignment.child.familyId !== session.user.familyId) {
|
||||
return { error: "Nur der angefragte Haushalt kann diese Anfrage ablehnen." };
|
||||
}
|
||||
|
||||
await prisma.notdienstSwapRequest.update({
|
||||
where: { id: request.id },
|
||||
data: {
|
||||
status: NotdienstSwapStatus.DECLINED,
|
||||
respondedById: session.user.id,
|
||||
respondedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
const kita = await prisma.kita.findUnique({
|
||||
where: { id: session.user.kitaId },
|
||||
select: { name: true },
|
||||
});
|
||||
if (kita) {
|
||||
await Promise.all([
|
||||
notifyFamilyAboutSwap({
|
||||
family: request.targetAssignment.child.family,
|
||||
subject: "Notdienst-Tauschanfrage abgelehnt",
|
||||
heading: "Tauschanfrage abgelehnt",
|
||||
preview: "Eine Notdienst-Tauschanfrage wurde abgelehnt.",
|
||||
kitaName: kita.name,
|
||||
body: "du hast die Tauschanfrage abgelehnt. Der Notdienst-Plan bleibt unverändert.",
|
||||
targetDate: request.targetAssignment.date,
|
||||
offeredDate: request.offeredAssignment.date,
|
||||
}),
|
||||
notifyFamilyAboutSwap({
|
||||
family: request.offeredAssignment.child.family,
|
||||
subject: "Notdienst-Tauschanfrage abgelehnt",
|
||||
heading: "Tauschanfrage abgelehnt",
|
||||
preview: "Eine Notdienst-Tauschanfrage wurde abgelehnt.",
|
||||
kitaName: kita.name,
|
||||
body: "deine Tauschanfrage wurde abgelehnt. Der Notdienst-Plan bleibt unverändert.",
|
||||
targetDate: request.targetAssignment.date,
|
||||
offeredDate: request.offeredAssignment.date,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
revalidateCalendarAndNotdienst();
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Ablehnen der Notdienst-Tauschanfrage:", error);
|
||||
return { error: "Tauschanfrage konnte nicht abgelehnt werden." };
|
||||
}
|
||||
}
|
||||
|
||||
export async function cancelNotdienstSwapRequest(requestIdRaw: unknown) {
|
||||
const session = await requireKitaSession();
|
||||
if (!session.user.familyId) {
|
||||
return { error: "Dein Account ist noch keinem Haushalt zugeordnet." };
|
||||
}
|
||||
|
||||
const parsed = swapIdSchema.safeParse(requestIdRaw);
|
||||
if (!parsed.success) {
|
||||
return { error: "Ungültige Eingabedaten." };
|
||||
}
|
||||
|
||||
try {
|
||||
const request = await prisma.notdienstSwapRequest.findFirst({
|
||||
where: {
|
||||
id: parsed.data,
|
||||
kitaId: session.user.kitaId,
|
||||
requesterFamilyId: session.user.familyId,
|
||||
status: NotdienstSwapStatus.PENDING,
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!request) {
|
||||
return { error: "Tauschanfrage nicht gefunden oder nicht mehr offen." };
|
||||
}
|
||||
|
||||
await prisma.notdienstSwapRequest.update({
|
||||
where: { id: request.id },
|
||||
data: {
|
||||
status: NotdienstSwapStatus.CANCELLED,
|
||||
respondedById: session.user.id,
|
||||
respondedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
revalidateCalendarAndNotdienst();
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Zurückziehen der Notdienst-Tauschanfrage:", error);
|
||||
return { error: "Tauschanfrage konnte nicht zurückgezogen werden." };
|
||||
}
|
||||
}
|
||||
|
||||
export async function createTerminRequest(rawPayload: unknown) {
|
||||
const session = await requireKitaSession();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user