"use client"; import { useState, useTransition } from "react"; import { DutyAssignmentStatus, TerminStatus, TerminType } from "@prisma/client"; import { format } from "date-fns"; import { de } from "date-fns/locale"; import { AlertTriangle, Calendar, Clock, MapPin, MoreHorizontal, WashingMachine, Trash2, ShieldAlert, } from "lucide-react"; import { toast } from "sonner"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, DialogFooter, } from "@/components/ui/dialog"; import { AdminTerminModal } from "./admin-termin-modal"; import { TerminRequestModal } from "./termin-request-modal"; import { MitbringselList } from "./mitbringsel-list"; import { deleteTerminAdmin } from "../actions"; export type TerminListItemDto = { kind: "termin"; id: string; title: string; description: string | null; type: TerminType; status: TerminStatus; startDate: Date; endDate: Date; allDay: boolean; mitbringselListEnabled: boolean; requiresRsvp: boolean; participantInfo: string | null; showParticipantInfo: boolean; mitbringselItems?: { id: string; userId: string; content: string; user: { firstName: string; lastName: string }; }[]; }; export type DutyCalendarItemDto = { kind: "duty"; id: string; title: string; familyName: string; status: DutyAssignmentStatus; startDate: Date; endDate: Date; }; export type NotdienstCalendarItemDto = { kind: "notdienst"; id: string; title: string; familyName: string; childName: string; isOwn: boolean; startDate: Date; endDate: Date; }; export type CalendarListItemDto = TerminListItemDto | DutyCalendarItemDto | NotdienstCalendarItemDto; export function TerminList({ termine, userId, isAdmin, }: { termine: CalendarListItemDto[]; userId: string; isAdmin: boolean; }) { if (termine.length === 0) { return (

Keine Termine in diesem Zeitraum

Lege den ersten Termin an, damit Eltern planen können.

{isAdmin ? ( ) : ( )}
); } const groups = groupByMonth(termine); return (
{groups.map((group) => (

{group.label}

{group.items.length} {group.items.length === 1 ? "Termin" : "Termine"}
{group.items.map((item) => item.kind === "duty" ? ( ) : item.kind === "notdienst" ? ( ) : ( ), )}
))}
); } function DutyRow({ duty, isAdmin }: { duty: DutyCalendarItemDto; isAdmin: boolean }) { const color = "var(--good)"; const [menuOpen, setMenuOpen] = useState(false); return (

{duty.title}

{format(duty.startDate, "dd.MM.", { locale: de })} -{" "} {format(duty.endDate, "dd.MM.yyyy", { locale: de })} {duty.familyName}

Elterndienst {isAdmin ? (
{menuOpen && ( <> ) : (
)}
); } function TerminRow({ termin, userId, isAdmin, }: { termin: TerminListItemDto; userId: string; isAdmin: boolean; }) { const color = getTypeColor(termin.type); const [menuOpen, setMenuOpen] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false); const [isDeleting, startDeleteTransition] = useTransition(); const handleDeleteConfirm = () => { startDeleteTransition(async () => { const res = await deleteTerminAdmin(termin.id); if (res.error) { toast.error(res.error); } else { toast.success("Termin wurde erfolgreich gelöscht."); setDeleteOpen(false); } }); }; return ( <>

{termin.title}

{termin.status === TerminStatus.PENDING && ( Ausstehend )}

{formatTerminTime(termin)} Kita

{getTypeLabel(termin.type)} {isAdmin ? (
{menuOpen && ( <>
setMenuOpen(false)} />
)}
) : (
)}
{termin.title} {format(termin.startDate, "PP", { locale: de })} {!termin.allDay && ` · ${format(termin.startDate, "p", { locale: de })} - ${format( termin.endDate, "p", { locale: de }, )}`}
{getTypeLabel(termin.type)}
{termin.description ? (

{termin.description}

) : (

Keine Beschreibung hinterlegt.

)} {termin.participantInfo && termin.showParticipantInfo && (
Wichtiger Hinweis für Teilnehmer

{termin.participantInfo}

)} {termin.mitbringselListEnabled && (
)}
{/* Delete Confirmation Dialog */} Termin unwiderruflich löschen? Möchtest du den Termin “{termin.title}” wirklich löschen? Alle zugehörigen Teilnahmen und Mitbringsel-Einträge gehen verloren. ); } function DateTile({ date }: { date: Date }) { return (
{format(date, "d", { locale: de })} {format(date, "EEE", { locale: de })}
); } function CategoryPill({ color, children, }: { color: string; children: React.ReactNode; }) { return ( {children} ); } function groupByMonth(items: CalendarListItemDto[]) { const groups = new Map(); items.forEach((item) => { const key = format(item.startDate, "yyyy-MM"); const label = format(item.startDate, "MMMM yyyy", { locale: de }); const group = groups.get(key) ?? { key, label, items: [] }; group.items.push(item); groups.set(key, group); }); return Array.from(groups.values()); } function formatTerminTime(termin: TerminListItemDto) { if (termin.allDay) return "ganztägig"; return `${format(termin.startDate, "HH:mm", { locale: de })} - ${format( termin.endDate, "HH:mm", { locale: de }, )}`; } function getTypeColor(type: TerminType): string { switch (type) { case TerminType.KITA_FEST: case TerminType.MITMACH_TAG: return "var(--good)"; case TerminType.SCHLIESSTAG: case TerminType.TEAMTAG: return "var(--danger)"; case TerminType.GEBURTSTAG_INTERN: case TerminType.GEBURTSTAG_EXTERN: return "oklch(0.50 0.10 320)"; case TerminType.ELTERNABEND: case TerminType.MITGLIEDERVERSAMMLUNG: case TerminType.ELTERNCAFE: return "oklch(0.50 0.10 280)"; default: return "var(--ink-muted)"; } } function getTypeLabel(type: TerminType): string { switch (type) { case TerminType.KITA_FEST: return "Kita-Fest"; case TerminType.MITMACH_TAG: return "Mitmach-Tag"; case TerminType.SCHLIESSTAG: return "Schließtag"; case TerminType.TEAMTAG: return "Teamtag"; case TerminType.ELTERNABEND: return "Elternabend"; case TerminType.MITGLIEDERVERSAMMLUNG: return "Mitgliederversammlung"; case TerminType.ELTERNCAFE: return "Elterncafe"; case TerminType.GEBURTSTAG_INTERN: return "Geburtstag"; case TerminType.GEBURTSTAG_EXTERN: return "Raumanfrage"; default: return "Sonstiges"; } } function NotdienstRow({ notdienst, isAdmin }: { notdienst: NotdienstCalendarItemDto; isAdmin: boolean }) { const color = notdienst.isOwn ? "oklch(0.60 0.18 45)" : "oklch(0.75 0.10 50)"; const [menuOpen, setMenuOpen] = useState(false); return (

{notdienst.title}

{format(notdienst.startDate, "dd.MM.yyyy", { locale: de })} (ganztägig) Kind: {notdienst.childName}

{notdienst.isOwn ? "Mein Notdienst" : "Notdienst"} {isAdmin ? (
{menuOpen && ( <> ) : (
)}
); }