"use client"; import { useMemo, useState, useTransition } from "react"; import { EventParticipationStatus, TerminStatus, TerminType, } from "@prisma/client"; import { format } from "date-fns"; import { de } from "date-fns/locale"; import { CalendarPlus, Check, Clock, HelpCircle, Pencil, Users, X, } from "lucide-react"; import { toast } from "sonner"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Textarea } from "@/components/ui/textarea"; import { createTerminAdmin, updateTerminAdmin, } from "../../kalender/actions"; type AdminTerminDto = { id: string; title: string; description: string | null; type: TerminType; status: TerminStatus; startDate: string; endDate: string; allDay: boolean; mitbringselListEnabled: boolean; requiresRsvp: boolean; rsvpDeadline: string | null; participantInfo: string | null; participations: { childId: string; status: EventParticipationStatus; }[]; }; type ChildDto = { id: string; name: string; familyName: string; }; export function AdminCalendarManager({ termine, childRows, }: { termine: AdminTerminDto[]; childRows: ChildDto[]; }) { const rsvpCount = termine.filter((termin) => termin.requiresRsvp).length; const pendingCount = termine.filter( (termin) => termin.status === TerminStatus.PENDING, ).length; return (

ADMIN · EVENT-ANMELDUNGEN

Termine{" "} koordinieren

Termine verwalten, RSVP aktivieren und den Rücklauf pro Kind verfolgen.

{termine.length} Termine {rsvpCount} RSVP {pendingCount > 0 && ( {pendingCount} ausstehend )}
{termine.length === 0 ? (

Noch keine Termine

Lege den ersten Termin an, um RSVP-Rückmeldungen einzusammeln.

) : (
{termine.map((termin) => ( ))}
)}
); } function AdminTerminCard({ termin, childRows, }: { termin: AdminTerminDto; childRows: ChildDto[]; }) { const stats = useMemo(() => getStats(termin, childRows), [termin, childRows]); return (
{getTypeLabel(termin.type)} {getStatusLabel(termin.status)} {termin.requiresRsvp && ( RSVP aktiv )}
{termin.title} {format(new Date(termin.startDate), "dd.MM.yyyy", { locale: de, })} {!termin.allDay && ( <> · {format(new Date(termin.startDate), "HH:mm", { locale: de, })}{" "} bis{" "} {format(new Date(termin.endDate), "HH:mm", { locale: de, })} )}
{termin.description && (

{termin.description}

)} {termin.requiresRsvp ? (

{stats.attending} Zugesagt / {stats.notAttending} Abgesagt /{" "} {stats.pending} Ausstehend

Frist:{" "} {termin.rsvpDeadline ? format(new Date(termin.rsvpDeadline), "dd.MM.yyyy HH:mm", { locale: de, }) : "bis Terminbeginn"}

{termin.participantInfo && (
Hinweis für Teilnehmer: {termin.participantInfo}
)} Kind Familie Status {childRows.map((child) => { const status = stats.statusByChildId.get(child.id) ?? null; return ( {child.name} {child.familyName} ); })}
) : (
Für diesen Termin ist keine Teilnahme-Abfrage aktiv.
)}
); } function TerminFormDialog({ mode, termin, }: { mode: "create" | "edit"; termin?: AdminTerminDto; }) { const [open, setOpen] = useState(false); const [requiresRsvp, setRequiresRsvp] = useState( termin?.requiresRsvp ?? false, ); const [isPending, startTransition] = useTransition(); function handleAction(formData: FormData) { const startDate = new Date(String(formData.get("startDate"))); const endDate = new Date(String(formData.get("endDate"))); const deadlineRaw = String(formData.get("rsvpDeadline") ?? ""); const hasRsvp = formData.get("requiresRsvp") === "on"; const payload = { title: String(formData.get("title") ?? ""), description: String(formData.get("description") ?? ""), type: formData.get("type") as TerminType, startDate: startDate.toISOString(), endDate: endDate.toISOString(), allDay: formData.get("allDay") === "on", mitbringselListEnabled: formData.get("mitbringselListEnabled") === "on", requiresRsvp: hasRsvp, rsvpDeadline: hasRsvp && deadlineRaw ? new Date(deadlineRaw).toISOString() : null, participantInfo: String(formData.get("participantInfo") ?? ""), }; startTransition(async () => { const result = mode === "edit" && termin ? await updateTerminAdmin(termin.id, payload) : await createTerminAdmin(payload); if (result.error) { toast.error(result.error); return; } toast.success( mode === "edit" ? "Termin aktualisiert." : "Termin angelegt.", ); setOpen(false); }); } return ( { setOpen(nextOpen); if (nextOpen) { setRequiresRsvp(termin?.requiresRsvp ?? false); } }} >
{mode === "edit" ? "Termin bearbeiten" : "Termin anlegen"} RSVP kann für Ausflüge und besondere Events aktiviert werden.