feat: show family assignment counts in notdienst planning dropdown and support editing unassigned days in published plans
This commit is contained in:
@@ -11,6 +11,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { getTargetMonthData, getWorkingDaysOfMonth } from "@/lib/date-utils";
|
||||
import { getAppEmailConfigError, sendAppEmail } from "@/lib/mail";
|
||||
import { NotdienstAvailabilityReminderEmail } from "@/emails/NotdienstAvailabilityReminderEmail";
|
||||
import { NotdienstAssignmentEmail } from "@/emails/NotdienstAssignmentEmail";
|
||||
|
||||
// =====================================================================
|
||||
// /dashboard/notdienst/plan · Server Actions
|
||||
@@ -159,14 +160,40 @@ export async function updateAssignmentAction(
|
||||
const parsedDate = new Date(Date.UTC(yr, mo - 1, dy));
|
||||
|
||||
try {
|
||||
await prisma.$transaction(async (tx) => {
|
||||
// Prüfen, ob Plan noch DRAFT ist
|
||||
const { shouldSendEmails, assignedChildInfo } = await prisma.$transaction(async (tx) => {
|
||||
let txShouldSendEmails = false;
|
||||
let txAssignedChildInfo: {
|
||||
parentEmails: string[];
|
||||
parentNames: string[];
|
||||
dateLabel: string;
|
||||
} | null = null;
|
||||
|
||||
const plan = await tx.notdienstPlan.findUnique({
|
||||
where: { id: planId, kitaId },
|
||||
});
|
||||
|
||||
if (!plan || plan.status !== NotdienstPlanStatus.DRAFT) {
|
||||
throw new Error("Plan kann nicht mehr bearbeitet werden.");
|
||||
if (!plan) {
|
||||
throw new Error("Plan nicht gefunden.");
|
||||
}
|
||||
|
||||
const isDraft = plan.status === NotdienstPlanStatus.DRAFT;
|
||||
const isPublished = plan.status === NotdienstPlanStatus.PUBLISHED;
|
||||
|
||||
if (!isDraft && !isPublished) {
|
||||
throw new Error("Plan kann nicht bearbeitet werden.");
|
||||
}
|
||||
|
||||
// Wenn der Plan bereits veröffentlicht ist, dürfen nur unbesetzte Termine geändert werden.
|
||||
// Ein Termin gilt als "unbesetzt", wenn am Veröffentlichungstag kein Assignment existierte
|
||||
// bzw. das Assignment erst nach dem Veröffentlichungs-Zeitpunkt erstellt wurde.
|
||||
const existingAssignment = await tx.notdienstAssignment.findFirst({
|
||||
where: { planId, date: parsedDate },
|
||||
});
|
||||
|
||||
if (isPublished && existingAssignment) {
|
||||
if (!plan.publishedAt || existingAssignment.createdAt <= plan.publishedAt) {
|
||||
throw new Error("In einem bereits veröffentlichten Plan können nur unbesetzte Termine geändert werden.");
|
||||
}
|
||||
}
|
||||
|
||||
// Altes Assignment für diesen Tag löschen
|
||||
@@ -182,7 +209,16 @@ export async function updateAssignmentAction(
|
||||
kitaId,
|
||||
active: true,
|
||||
},
|
||||
select: { id: true },
|
||||
include: {
|
||||
family: {
|
||||
include: {
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN },
|
||||
select: { email: true, firstName: true, lastName: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!child) {
|
||||
@@ -197,9 +233,57 @@ export async function updateAssignmentAction(
|
||||
date: parsedDate,
|
||||
},
|
||||
});
|
||||
|
||||
// Wenn der Plan bereits veröffentlicht ist, bereiten wir E-Mail-Benachrichtigungen vor
|
||||
if (isPublished) {
|
||||
const parents = child.family.users.filter((u) => !!u.email);
|
||||
if (parents.length > 0) {
|
||||
const localFormatDate = new Date(yr, mo - 1, dy);
|
||||
txAssignedChildInfo = {
|
||||
parentEmails: parents.map((p) => p.email!),
|
||||
parentNames: parents.map((p) => `${p.firstName} ${p.lastName}`),
|
||||
dateLabel: format(localFormatDate, "EEEE, dd. MMMM yyyy", { locale: de }),
|
||||
};
|
||||
txShouldSendEmails = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
shouldSendEmails: txShouldSendEmails,
|
||||
assignedChildInfo: txAssignedChildInfo,
|
||||
};
|
||||
});
|
||||
|
||||
// E-Mails außerhalb der DB-Transaktion senden
|
||||
if (shouldSendEmails && assignedChildInfo) {
|
||||
const kita = await prisma.kita.findUnique({
|
||||
where: { id: kitaId },
|
||||
select: { name: true },
|
||||
});
|
||||
|
||||
if (kita) {
|
||||
const BASE_URL = process.env.NEXTAUTH_URL ?? process.env.AUTH_URL ?? "http://localhost:3000";
|
||||
const link = `${BASE_URL}/dashboard/notdienst`;
|
||||
|
||||
for (let i = 0; i < assignedChildInfo.parentEmails.length; i++) {
|
||||
const email = assignedChildInfo.parentEmails[i];
|
||||
const name = assignedChildInfo.parentNames[i];
|
||||
|
||||
await sendAppEmail({
|
||||
to: email,
|
||||
subject: `Notdienst-Einteilung am ${assignedChildInfo.dateLabel}`,
|
||||
react: createElement(NotdienstAssignmentEmail, {
|
||||
parentName: name,
|
||||
kitaName: kita.name,
|
||||
dateLabel: assignedChildInfo.dateLabel,
|
||||
link,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
revalidatePath("/dashboard/notdienst/plan");
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user