170 lines
5.4 KiB
TypeScript
170 lines
5.4 KiB
TypeScript
import Link from "next/link";
|
|
import { TerminStatus, TerminType, UserRole } from "@prisma/client";
|
|
import { ArrowRight, ShieldAlert } from "lucide-react";
|
|
import { Manrope } from "next/font/google";
|
|
|
|
import { requireRole } from "@/lib/auth-utils";
|
|
import { prisma } from "@/lib/prisma";
|
|
import { formatDateKey, getTargetMonthData } from "@/lib/date-utils";
|
|
import { Button } from "@/components/ui/button";
|
|
import { NotdienstEntry } from "./notdienst-entry";
|
|
|
|
export const metadata = { title: "Notdienst · Kita-Planer" };
|
|
|
|
const manrope = Manrope({
|
|
subsets: ["latin"],
|
|
weight: ["400", "500", "600", "700"],
|
|
});
|
|
|
|
export default async function NotdienstPage() {
|
|
// ELTERN können eintragen; Admins/Koordinatoren haben hier eigentlich
|
|
// andere Ansichten (Plan-Generierung), aber wir lassen sie zur Demonstration
|
|
// ebenfalls durch, falls sie selbst Kinder haben.
|
|
const session = await requireRole([
|
|
UserRole.ELTERN,
|
|
UserRole.ADMIN,
|
|
UserRole.KOORDINATOR,
|
|
]);
|
|
|
|
const kitaId = session.user.kitaId;
|
|
if (!kitaId) {
|
|
return <div className="p-8">Kein Mandant zugeordnet.</div>;
|
|
}
|
|
|
|
// Kita-Einstellungen laden
|
|
const kita = await prisma.kita.findUniqueOrThrow({
|
|
where: { id: kitaId },
|
|
select: { notdienstMinPerChildPerMonth: true },
|
|
});
|
|
|
|
// Nur aktive Kinder des eigenen Haushalts laden.
|
|
const children = session.user.familyId
|
|
? await prisma.child.findMany({
|
|
where: {
|
|
kitaId,
|
|
familyId: session.user.familyId,
|
|
active: true,
|
|
},
|
|
select: { id: true, firstName: true },
|
|
})
|
|
: [];
|
|
|
|
const targetData = getTargetMonthData();
|
|
const { targetYear, targetMonth, isLocked } = targetData;
|
|
const targetMonthStart = new Date(Date.UTC(targetYear, targetMonth - 1, 1));
|
|
const targetMonthEnd = new Date(Date.UTC(targetYear, targetMonth, 1));
|
|
|
|
const requiredDaysTotal = children.length * kita.notdienstMinPerChildPerMonth;
|
|
const childIds = children.map((child) => child.id);
|
|
|
|
// Bisherige Einträge für den Zielmonat laden, haushaltsweit über die Kinder.
|
|
const [availabilities, termine] =
|
|
children.length > 0
|
|
? await Promise.all([
|
|
prisma.notdienstAvailability.findMany({
|
|
where: {
|
|
kitaId,
|
|
childId: { in: childIds },
|
|
date: {
|
|
gte: targetMonthStart,
|
|
lt: targetMonthEnd,
|
|
},
|
|
},
|
|
select: { date: true },
|
|
}),
|
|
prisma.termin.findMany({
|
|
where: {
|
|
kitaId,
|
|
status: TerminStatus.CONFIRMED,
|
|
startDate: { lt: targetMonthEnd },
|
|
endDate: { gte: targetMonthStart },
|
|
},
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
description: true,
|
|
type: true,
|
|
startDate: true,
|
|
endDate: true,
|
|
allDay: true,
|
|
},
|
|
orderBy: { startDate: "asc" },
|
|
}),
|
|
])
|
|
: [[], []];
|
|
|
|
const selectedDates = availabilities.map((a) => formatDateKey(a.date));
|
|
|
|
return (
|
|
<div className={`${manrope.className} mx-auto max-w-7xl px-4 py-8 sm:px-8`}>
|
|
{children.length === 0 ? (
|
|
<NotdienstLockout />
|
|
) : (
|
|
<NotdienstEntry
|
|
targetYear={targetYear}
|
|
targetMonth={targetMonth}
|
|
isLocked={isLocked}
|
|
requiredDaysTotal={requiredDaysTotal}
|
|
initialSelectedDates={selectedDates}
|
|
childrenIds={childIds}
|
|
calendarItems={termine.map((termin) => ({
|
|
id: termin.id,
|
|
title: termin.title,
|
|
category: getTerminCategory(termin.type),
|
|
startDate: termin.startDate,
|
|
endDate: termin.endDate,
|
|
allDay: termin.allDay,
|
|
note: termin.description,
|
|
}))}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function getTerminCategory(type: TerminType) {
|
|
switch (type) {
|
|
case TerminType.SCHLIESSTAG:
|
|
return "closure" as const;
|
|
case TerminType.TEAMTAG:
|
|
return "team" as const;
|
|
case TerminType.KITA_FEST:
|
|
case TerminType.MITMACH_TAG:
|
|
return "social" as const;
|
|
case TerminType.ELTERNABEND:
|
|
case TerminType.MITGLIEDERVERSAMMLUNG:
|
|
case TerminType.ELTERNCAFE:
|
|
return "parents" as const;
|
|
case TerminType.GEBURTSTAG_INTERN:
|
|
case TerminType.GEBURTSTAG_EXTERN:
|
|
return "birthday" as const;
|
|
default:
|
|
return "social" as const;
|
|
}
|
|
}
|
|
|
|
function NotdienstLockout() {
|
|
return (
|
|
<div className="flex min-h-[420px] items-center justify-center rounded-lg border border-dashed border-[var(--hairline)] bg-[var(--surface-2)] p-8 text-center">
|
|
<div className="mx-auto max-w-lg">
|
|
<div className="mx-auto flex h-20 w-20 items-center justify-center rounded-2xl bg-[var(--accent-soft)] text-[var(--accent-deep)]">
|
|
<ShieldAlert className="h-10 w-10 [stroke-width:1.7]" />
|
|
</div>
|
|
<h2 className="mt-6 text-2xl font-medium tracking-[-0.02em] text-[var(--ink)]">
|
|
Erst Kinder hinterlegen
|
|
</h2>
|
|
<p className="mt-3 text-sm leading-6 text-[var(--ink-soft)]">
|
|
Bitte lege zuerst deine Kinder im Profil an, bevor du Notdienste
|
|
übernimmst.
|
|
</p>
|
|
<Button asChild className="mt-6">
|
|
<Link href="/dashboard/profil">
|
|
Zum Profil
|
|
<ArrowRight className="h-4 w-4" />
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|