"use client"; import type { Appointment } from "@/lib/types"; import { format, startOfMonth, endOfMonth, startOfWeek, endOfWeek, eachDayOfInterval, isSameMonth, isToday, parseISO, addMonths, subMonths, } from "date-fns"; import { de } from "date-fns/locale"; import { ChevronLeft, ChevronRight } from "lucide-react"; import { useState, useMemo } from "react"; const TYPE_DOT_COLORS: Record = { hearing: "bg-blue-500", meeting: "bg-violet-500", consultation: "bg-emerald-500", deadline_hearing: "bg-amber-500", other: "bg-neutral-400", }; interface AppointmentCalendarProps { appointments: Appointment[]; onDayClick?: (date: string) => void; onAppointmentClick?: (appointment: Appointment) => void; } export function AppointmentCalendar({ appointments, onDayClick, onAppointmentClick, }: AppointmentCalendarProps) { const [currentMonth, setCurrentMonth] = useState(new Date()); const monthStart = startOfMonth(currentMonth); const monthEnd = endOfMonth(currentMonth); const calStart = startOfWeek(monthStart, { weekStartsOn: 1 }); const calEnd = endOfWeek(monthEnd, { weekStartsOn: 1 }); const days = eachDayOfInterval({ start: calStart, end: calEnd }); const appointmentsByDay = useMemo(() => { const map = new Map(); for (const a of appointments) { const key = a.start_at.slice(0, 10); const existing = map.get(key) || []; existing.push(a); map.set(key, existing); } return map; }, [appointments]); const weekDays = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]; return (
{/* Header */}
{format(currentMonth, "MMMM yyyy", { locale: de })}
{/* Weekday labels */}
{weekDays.map((d) => (
{d}
))}
{/* Days grid */}
{days.map((day, i) => { const key = format(day, "yyyy-MM-dd"); const dayAppointments = appointmentsByDay.get(key) || []; const inMonth = isSameMonth(day, currentMonth); const today = isToday(day); return (
onDayClick?.(key)} className={`min-h-[5rem] cursor-pointer border-b border-r border-neutral-100 p-1.5 transition-colors hover:bg-neutral-50 ${ !inMonth ? "bg-neutral-50/50" : "" }`} >
{today ? ( {format(day, "d")} ) : ( format(day, "d") )}
{dayAppointments.slice(0, 3).map((appt) => { const dotColor = TYPE_DOT_COLORS[appt.appointment_type ?? "other"] ?? TYPE_DOT_COLORS.other; return (
{ e.stopPropagation(); onAppointmentClick?.(appt); }} className="flex items-center gap-1 truncate rounded px-0.5 hover:bg-neutral-100" title={`${format(parseISO(appt.start_at), "HH:mm")} ${appt.title}`} >
{format(parseISO(appt.start_at), "HH:mm")} {" "} {appt.title}
); })} {dayAppointments.length > 3 && (
+{dayAppointments.length - 3} mehr
)}
); })}
); }