'use client'; /** * Agenda Calendar Component * * FullCalendar wrapper with day/week/workweek views. */ import { useCallback, useRef, useEffect } from 'react'; import FullCalendar from '@fullcalendar/react'; import dayGridPlugin from '@fullcalendar/daygrid'; import timeGridPlugin from '@fullcalendar/timegrid'; import interactionPlugin from '@fullcalendar/interaction'; import type { EventClickArg, DateSelectArg, EventDropArg, CalendarApi } from '@fullcalendar/core'; import nlLocale from '@fullcalendar/core/locales/nl'; import type { CalendarEvent, CalendarView } from '../types'; interface AgendaCalendarProps { events: CalendarEvent[]; initialView?: CalendarView; currentView?: CalendarView; currentDate?: Date; onEventClick?: (event: CalendarEvent) => void; onDateSelect?: (start: Date, end: Date) => void; onEventDrop?: (eventId: string, newStart: Date, newEnd: Date | null) => void; onDateChange?: (start: Date, end: Date) => void; calendarRef?: React.RefObject; } export function AgendaCalendar({ events, initialView = 'timeGridWeek', currentView, currentDate, onEventClick, onDateSelect, onEventDrop, onDateChange, calendarRef: externalRef, }: AgendaCalendarProps) { const internalRef = useRef(null); const isChangingViewRef = useRef(false); // Sync view when currentView prop changes useEffect(() => { if (currentView && internalRef.current) { const api = internalRef.current.getApi(); if (api.view.type !== currentView) { isChangingViewRef.current = true; api.changeView(currentView); setTimeout(() => { isChangingViewRef.current = false; }, 100); } } }, [currentView]); // Sync date when currentDate prop changes useEffect(() => { if (currentDate && internalRef.current) { const api = internalRef.current.getApi(); if (api.getDate().toDateString() !== currentDate.toDateString()) { isChangingViewRef.current = true; api.gotoDate(currentDate); setTimeout(() => { isChangingViewRef.current = false; }, 100); } } }, [currentDate]); const handleEventClick = useCallback((info: EventClickArg) => { if (onEventClick) { const event = info.event; const calendarEvent: CalendarEvent = { id: event.id, title: event.title, start: event.start!, end: event.end || undefined, extendedProps: event.extendedProps as CalendarEvent['extendedProps'], backgroundColor: event.backgroundColor, borderColor: event.borderColor, }; onEventClick(calendarEvent); } }, [onEventClick]); const handleDateSelect = useCallback((info: DateSelectArg) => { if (onDateSelect) { onDateSelect(info.start, info.end); } }, [onDateSelect]); const handleEventDrop = useCallback((info: EventDropArg) => { if (onEventDrop) { onEventDrop( info.event.id, info.event.start!, info.event.end ); } }, [onEventDrop]); const handleDatesSet = useCallback((dateInfo: { start: Date; end: Date }) => { // Skip if we're in the middle of a programmatic view change if (isChangingViewRef.current) { return; } if (onDateChange) { onDateChange(dateInfo.start, dateInfo.end); } }, [onDateChange]); return (
(
{eventInfo.timeText}
{eventInfo.event.title}
{eventInfo.event.extendedProps?.encounter?.type_display && (
{eventInfo.event.extendedProps.encounter.type_display}
)}
)} />
); }