"use client"; import { useMemo } from "react"; import { useTranslations, useFormatter } from "next-intl"; import { format, parseISO, isToday, isTomorrow } from "date-fns"; import { Calendar as CalendarIcon, MapPin } from "lucide-react"; import { cn } from "@/lib/utils"; import { parseDuration, getEventColor } from "./event-card"; import type { CalendarEvent, Calendar } from "@/lib/jmap/types"; interface CalendarAgendaViewProps { selectedDate: Date; events: CalendarEvent[]; calendars: Calendar[]; onSelectEvent: (event: CalendarEvent) => void; timeFormat?: "12h" | "24h"; } interface DayGroup { date: Date; dateKey: string; events: CalendarEvent[]; } function getEventEndDate(event: CalendarEvent): Date { const start = new Date(event.start); if (!event.duration) return start; const days = parseInt(event.duration.match(/(\d+)D/)?.[1] || "0"); const hours = parseInt(event.duration.match(/(\d+)H/)?.[1] || "0"); const minutes = parseInt(event.duration.match(/(\d+)M/)?.[1] || "0"); const weeks = parseInt(event.duration.match(/(\d+)W/)?.[1] || "0"); const totalMs = ((weeks * 7 + days) * 24 * 60 + hours * 60 + minutes) * 60000; return new Date(start.getTime() + totalMs); } export function CalendarAgendaView({ events, calendars, onSelectEvent, timeFormat = "24h", }: CalendarAgendaViewProps) { const t = useTranslations("calendar"); const intlFormatter = useFormatter(); const calendarMap = useMemo(() => { const map = new Map(); calendars.forEach((c) => map.set(c.id, c)); return map; }, [calendars]); const grouped = useMemo(() => { const sorted = [...events].sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime() ); const groups: DayGroup[] = []; const groupMap = new Map(); sorted.forEach((ev) => { try { const start = new Date(ev.start); const end = getEventEndDate(ev); const startKey = format(start, "yyyy-MM-dd"); const endKey = format(end, "yyyy-MM-dd"); if (startKey === endKey || ev.showWithoutTime) { let group = groupMap.get(startKey); if (!group) { group = { date: start, dateKey: startKey, events: [] }; groupMap.set(startKey, group); groups.push(group); } group.events.push(ev); } else { const cursor = new Date(start); cursor.setHours(0, 0, 0, 0); const endDay = new Date(end); endDay.setHours(0, 0, 0, 0); while (cursor <= endDay) { const key = format(cursor, "yyyy-MM-dd"); let group = groupMap.get(key); if (!group) { group = { date: new Date(cursor), dateKey: key, events: [] }; groupMap.set(key, group); groups.push(group); } group.events.push(ev); cursor.setDate(cursor.getDate() + 1); } } } catch { /* skip invalid dates */ } }); groups.sort((a, b) => a.date.getTime() - b.date.getTime()); return groups; }, [events]); const formatDateHeader = (date: Date): string => { if (isToday(date)) return t("events.today_header"); if (isTomorrow(date)) return t("events.tomorrow_header"); return intlFormatter.dateTime(date, { weekday: "long", month: "long", day: "numeric" }); }; const formatTime = (date: Date): string => { if (timeFormat === "12h") { return intlFormatter.dateTime(date, { hour: "numeric", minute: "2-digit", hour12: true }); } return format(date, "HH:mm"); }; if (grouped.length === 0) { return (

{t("events.no_events")}

); } return (
{grouped.map((group) => (
{formatDateHeader(group.date)} {intlFormatter.dateTime(group.date, { month: "short", day: "numeric", year: "numeric" })}
{group.events.map((ev) => { const calId = Object.keys(ev.calendarIds)[0]; const calendar = calendarMap.get(calId); const color = getEventColor(ev, calendar); const start = parseISO(ev.start); const durMin = parseDuration(ev.duration); const end = new Date(start.getTime() + durMin * 60000); const locationName = ev.locations ? Object.values(ev.locations)[0]?.name : null; return ( ); })}
))}
); }