feat: complete notdienst planning, quick wins and notifications
This commit is contained in:
@@ -24,6 +24,7 @@ import { serializableTransaction } from "@/lib/transaction";
|
||||
import { rateLimit } from "@/lib/rate-limit";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { lockEvent } from "@/lib/event-lock";
|
||||
import { canRequestNotdienstSwap } from "@/lib/notdienst-swaps";
|
||||
|
||||
// =====================================================================
|
||||
// /dashboard/kalender · Server Actions
|
||||
@@ -127,7 +128,7 @@ async function getSwapAssignment(
|
||||
select: {
|
||||
name: true,
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN, emailVerifiedAt: { not: null }, disabledAt: null, privacyPolicyAcceptedAt: { not: null } },
|
||||
where: { role: { in: [UserRole.ELTERN, UserRole.ADMIN, UserRole.KOORDINATOR] }, emailVerifiedAt: { not: null }, disabledAt: null, privacyPolicyAcceptedAt: { not: null } },
|
||||
select: {
|
||||
email: true,
|
||||
firstName: true,
|
||||
@@ -335,11 +336,8 @@ function buildTerminData(
|
||||
export async function createNotdienstSwapRequest(rawPayload: unknown) {
|
||||
const session = await requireKitaSession();
|
||||
if (!(await rateLimit(`createNotdienstSwapRequest:${session.user.kitaId}:${session.user.id}`, 30, 3600)).allowed) return { error: "Zu viele Anfragen. Bitte versuche es später erneut." };
|
||||
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." };
|
||||
if (!canRequestNotdienstSwap(session.user.role, session.user.familyId)) {
|
||||
return { error: "Nur Eltern mit einem eigenen Haushalt können Notdienst-Tausche anfragen." };
|
||||
}
|
||||
|
||||
const parsed = swapRequestSchema.safeParse(rawPayload);
|
||||
@@ -467,7 +465,7 @@ export async function acceptNotdienstSwapRequest(requestIdRaw: unknown) {
|
||||
select: {
|
||||
name: true,
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN, emailVerifiedAt: { not: null }, disabledAt: null, privacyPolicyAcceptedAt: { not: null } },
|
||||
where: { role: { in: [UserRole.ELTERN, UserRole.ADMIN, UserRole.KOORDINATOR] }, emailVerifiedAt: { not: null }, disabledAt: null, privacyPolicyAcceptedAt: { not: null } },
|
||||
select: { email: true, firstName: true, lastName: true },
|
||||
},
|
||||
},
|
||||
@@ -493,7 +491,7 @@ export async function acceptNotdienstSwapRequest(requestIdRaw: unknown) {
|
||||
select: {
|
||||
name: true,
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN, emailVerifiedAt: { not: null }, disabledAt: null, privacyPolicyAcceptedAt: { not: null } },
|
||||
where: { role: { in: [UserRole.ELTERN, UserRole.ADMIN, UserRole.KOORDINATOR] }, emailVerifiedAt: { not: null }, disabledAt: null, privacyPolicyAcceptedAt: { not: null } },
|
||||
select: { email: true, firstName: true, lastName: true },
|
||||
},
|
||||
},
|
||||
@@ -636,7 +634,7 @@ export async function declineNotdienstSwapRequest(requestIdRaw: unknown) {
|
||||
select: {
|
||||
name: true,
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN, emailVerifiedAt: { not: null }, disabledAt: null, privacyPolicyAcceptedAt: { not: null } },
|
||||
where: { role: { in: [UserRole.ELTERN, UserRole.ADMIN, UserRole.KOORDINATOR] }, emailVerifiedAt: { not: null }, disabledAt: null, privacyPolicyAcceptedAt: { not: null } },
|
||||
select: { email: true, firstName: true, lastName: true },
|
||||
},
|
||||
},
|
||||
@@ -654,7 +652,7 @@ export async function declineNotdienstSwapRequest(requestIdRaw: unknown) {
|
||||
select: {
|
||||
name: true,
|
||||
users: {
|
||||
where: { role: UserRole.ELTERN, emailVerifiedAt: { not: null }, disabledAt: null, privacyPolicyAcceptedAt: { not: null } },
|
||||
where: { role: { in: [UserRole.ELTERN, UserRole.ADMIN, UserRole.KOORDINATOR] }, emailVerifiedAt: { not: null }, disabledAt: null, privacyPolicyAcceptedAt: { not: null } },
|
||||
select: { email: true, firstName: true, lastName: true },
|
||||
},
|
||||
},
|
||||
@@ -1059,6 +1057,44 @@ export async function addMitbringsel(rawPayload: unknown) {
|
||||
}
|
||||
}
|
||||
|
||||
const updateMitbringselSchema = z.object({
|
||||
itemId: z.string().min(1).max(100),
|
||||
content: mitbringselSchema.shape.content,
|
||||
});
|
||||
|
||||
export async function updateMitbringsel(rawPayload: unknown) {
|
||||
const session = await requireKitaSession();
|
||||
const parsed = updateMitbringselSchema.safeParse(rawPayload);
|
||||
if (!parsed.success) return { error: "Bitte einen Beitrag mit 1 bis 500 Zeichen eingeben." };
|
||||
if (!(await rateLimit(`updateMitbringsel:${session.user.kitaId}:${session.user.id}`, 30, 3600)).allowed) {
|
||||
return { error: "Zu viele Änderungen. Bitte versuche es später erneut." };
|
||||
}
|
||||
|
||||
try {
|
||||
await serializableTransaction(async (tx) => {
|
||||
const item = await tx.mitbringselItem.findFirst({
|
||||
where: { id: parsed.data.itemId, kitaId: session.user.kitaId, userId: session.user.id },
|
||||
select: { terminId: true },
|
||||
});
|
||||
if (!item) throw new UserFacingError("Du kannst nur eigene Mitbringbeiträge bearbeiten.");
|
||||
await lockEvent(tx, item.terminId, session.user.kitaId);
|
||||
const termin = await tx.termin.findFirst({
|
||||
where: { id: item.terminId, kitaId: session.user.kitaId, status: TerminStatus.CONFIRMED, mitbringselListEnabled: true },
|
||||
select: { id: true },
|
||||
});
|
||||
if (!termin) throw new UserFacingError("Die Mitbring-Liste ist für diesen Termin nicht mehr aktiv.");
|
||||
await tx.mitbringselItem.update({
|
||||
where: { id: parsed.data.itemId, kitaId: session.user.kitaId, userId: session.user.id },
|
||||
data: { content: parsed.data.content },
|
||||
});
|
||||
});
|
||||
revalidatePath("/dashboard/kalender");
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
return { error: publicActionError(error, "Der Beitrag konnte nicht gespeichert werden.") };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteMitbringsel(itemId: string) {
|
||||
const session = await requireKitaSession();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user