Files
kita-planer/src/app/dashboard/kalender/page.tsx
T
2026-05-15 21:28:36 +02:00

266 lines
7.0 KiB
TypeScript

import {
DutyAssignmentStatus,
EventParticipationStatus,
TerminStatus,
UserRole,
} from "@prisma/client";
import { requireKitaSession } from "@/lib/auth-utils";
import { prisma } from "@/lib/prisma";
import { AdminTerminModal } from "./_components/admin-termin-modal";
import { PendingAnfragen, type PendingTerminDto } from "./_components/pending-anfragen";
import { TerminList, type CalendarListItemDto } from "./_components/termin-list";
import { TerminRequestModal } from "./_components/termin-request-modal";
export default async function KalenderPage({
searchParams,
}: {
searchParams: { tab?: string };
}) {
const session = await requireKitaSession();
const isAdmin =
session.user.role === UserRole.ADMIN ||
session.user.role === UserRole.KOORDINATOR;
const familyIdForParticipation = session.user.familyId ?? "__no_family__";
const currentTab = searchParams.tab || "übersicht";
const confirmedTermineRows = await prisma.termin.findMany({
where: {
kitaId: session.user.kitaId,
status: TerminStatus.CONFIRMED,
},
select: {
id: true,
title: true,
description: true,
type: true,
status: true,
startDate: true,
endDate: true,
allDay: true,
mitbringselListEnabled: true,
requiresRsvp: true,
participantInfo: true,
mitbringselItems: {
select: {
id: true,
userId: true,
content: true,
user: {
select: {
firstName: true,
lastName: true,
},
},
},
},
participations: {
where: {
child: {
familyId: familyIdForParticipation,
},
},
select: {
childId: true,
status: true,
},
},
},
orderBy: { startDate: "asc" },
});
const myPendingTermineRows = await prisma.termin.findMany({
where: {
kitaId: session.user.kitaId,
status: TerminStatus.PENDING,
createdById: session.user.id,
},
select: {
id: true,
title: true,
description: true,
type: true,
status: true,
startDate: true,
endDate: true,
allDay: true,
mitbringselListEnabled: true,
requiresRsvp: true,
participantInfo: true,
mitbringselItems: {
select: {
id: true,
userId: true,
content: true,
user: {
select: {
firstName: true,
lastName: true,
},
},
},
},
participations: {
where: {
child: {
familyId: familyIdForParticipation,
},
},
select: {
childId: true,
status: true,
},
},
},
orderBy: { startDate: "asc" },
});
const dutyAssignmentRows = await prisma.dutyAssignment.findMany({
where: {
kitaId: session.user.kitaId,
status: DutyAssignmentStatus.PLANNED,
endDate: { gte: new Date() },
},
select: {
id: true,
startDate: true,
endDate: true,
status: true,
dutyType: {
select: {
name: true,
},
},
family: {
select: {
name: true,
},
},
},
orderBy: { startDate: "asc" },
});
const allUserTermine: CalendarListItemDto[] = [
...confirmedTermineRows.map((termin) => ({
...termin,
kind: "termin" as const,
showParticipantInfo:
isAdmin ||
termin.participations.some(
(participation) =>
participation.status === EventParticipationStatus.ATTENDING,
),
})),
...myPendingTermineRows.map((termin) => ({
...termin,
kind: "termin" as const,
showParticipantInfo:
isAdmin ||
termin.participations.some(
(participation) =>
participation.status === EventParticipationStatus.ATTENDING,
),
})),
...dutyAssignmentRows.map((assignment) => ({
kind: "duty" as const,
id: assignment.id,
title: assignment.dutyType.name,
familyName: assignment.family.name,
status: assignment.status,
startDate: assignment.startDate,
endDate: assignment.endDate,
})),
].sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
let allPendingTermine: PendingTerminDto[] = [];
if (isAdmin) {
allPendingTermine = await prisma.termin.findMany({
where: {
kitaId: session.user.kitaId,
status: TerminStatus.PENDING,
},
select: {
id: true,
title: true,
startDate: true,
allDay: true,
createdBy: {
select: {
firstName: true,
lastName: true,
},
},
},
orderBy: { createdAt: "desc" },
});
}
return (
<div className="flex h-full flex-col gap-6 p-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold tracking-tight">Terminkalender</h1>
<p className="text-muted-foreground">
Alle anstehenden Termine, Feste und Schließtage im Überblick.
</p>
</div>
<div className="flex gap-2">
<TerminRequestModal />
{isAdmin && <AdminTerminModal />}
</div>
</div>
{isAdmin ? (
<div className="flex flex-col gap-4">
<div className="flex gap-4 border-b">
<a
href="/dashboard/kalender?tab=übersicht"
className={`pb-2 text-sm font-medium transition-colors hover:text-primary ${
currentTab === "übersicht"
? "border-b-2 border-primary text-primary"
: "text-muted-foreground"
}`}
>
Übersicht
</a>
<a
href="/dashboard/kalender?tab=anfragen"
className={`flex items-center gap-2 pb-2 text-sm font-medium transition-colors hover:text-primary ${
currentTab === "anfragen"
? "border-b-2 border-primary text-primary"
: "text-muted-foreground"
}`}
>
Ausstehende Anfragen
{allPendingTermine.length > 0 && (
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-primary text-[10px] text-primary-foreground">
{allPendingTermine.length}
</span>
)}
</a>
</div>
<div className="pt-2">
{currentTab === "übersicht" ? (
<TerminList
termine={allUserTermine}
userId={session.user.id}
isAdmin={isAdmin}
/>
) : (
<PendingAnfragen termine={allPendingTermine} />
)}
</div>
</div>
) : (
<TerminList
termine={allUserTermine}
userId={session.user.id}
isAdmin={isAdmin}
/>
)}
</div>
);
}