"use client"; import { useMemo, useState } from "react"; import type { CalendarEvent, CalendarSource } from "@/types/dashboard"; type CalendarWidgetProps = { widgetId: string; editMode: boolean; events: CalendarEvent[]; error: string | null; sources: CalendarSource[]; selectedSourceIds: string[]; nextEventsCount: number; onToggleSource: (widgetId: string, sourceId: string) => void; onChangeEventsCount: (widgetId: string, count: number) => void; }; type CalendarDay = { key: string; date: Date; inCurrentMonth: boolean; isToday: boolean; events: CalendarEvent[]; }; const weekdayLabels = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]; function dateKey(date: Date): string { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } function formatEventDate(value: string): string { return new Intl.DateTimeFormat("de-DE", { weekday: "short", day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" }).format(new Date(value)); } function formatEventTime(value: string): string { return new Intl.DateTimeFormat("de-DE", { hour: "2-digit", minute: "2-digit" }).format(new Date(value)); } function formatMonthLabel(value: Date): string { return new Intl.DateTimeFormat("de-DE", { month: "long", year: "numeric" }).format(value); } function buildCalendarDays(monthDate: Date, eventsByDate: Map): CalendarDay[] { const year = monthDate.getFullYear(); const month = monthDate.getMonth(); const firstDayOfMonth = new Date(year, month, 1); const mondayBasedStartOffset = (firstDayOfMonth.getDay() + 6) % 7; const gridStart = new Date(firstDayOfMonth); gridStart.setDate(firstDayOfMonth.getDate() - mondayBasedStartOffset); gridStart.setHours(0, 0, 0, 0); const today = dateKey(new Date()); const days: CalendarDay[] = []; for (let index = 0; index < 42; index += 1) { const date = new Date(gridStart); date.setDate(gridStart.getDate() + index); const key = dateKey(date); days.push({ key, date, inCurrentMonth: date.getMonth() === month, isToday: key === today, events: eventsByDate.get(key) ?? [] }); } return days; } function groupEventsByDate(events: CalendarEvent[]): Map { const grouped = new Map(); events.forEach((event) => { const key = dateKey(new Date(event.start)); const existing = grouped.get(key) ?? []; grouped.set(key, [...existing, event]); }); return grouped; } export default function CalendarWidget({ widgetId, editMode, events, error, sources, selectedSourceIds, nextEventsCount, onToggleSource, onChangeEventsCount }: CalendarWidgetProps) { const [calendarMonth, setCalendarMonth] = useState(() => new Date()); const eventsByDate = useMemo(() => groupEventsByDate(events), [events]); const calendarDays = useMemo(() => buildCalendarDays(calendarMonth, eventsByDate), [calendarMonth, eventsByDate]); const clampedEventsCount = Math.max(0, Math.min(10, nextEventsCount)); const nextEvents = useMemo( () => [...events] .sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime()) .slice(0, clampedEventsCount), [events, clampedEventsCount] ); return ( <> {editMode ? (
Kalenderauswahl
{sources.length === 0 ? (

Keine Kalenderquellen angelegt. Lege Kalender in den Benutzereinstellungen an.

) : null} {sources.map((source) => ( ))}
) : null}
{selectedSourceIds.length === 0 ?

Keine Kalenderquelle ausgewählt.

: null}
{weekdayLabels.map((label) => (
{label}
))}
{calendarDays.map((day) => (
0 ? "calendarDayWithEvents" : "" ] .filter(Boolean) .join(" ")} key={day.key} > {day.date.getDate()} {day.events.length > 0 ? {day.events.length} : null} {day.events.length > 0 ? (
{day.events.slice(0, 5).map((event) => (
{formatEventTime(event.start)} {event.title}
))} {day.events.length > 5 ?
Weitere Termine vorhanden
: null}
) : null}
))}
{error ?

{error}

: null} {clampedEventsCount > 0 ? (

Nächste Termine

{nextEvents.length === 0 ?

Keine nächsten Termine.

: null}
{nextEvents.map((event) => (
{formatEventDate(event.start)}
{event.title}
{event.location ?
{event.location}
: null}
))}
) : null} ); }