// @ts-nocheck 'use client'; import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { format, startOfMonth, endOfMonth, startOfWeek, endOfWeek, addDays, addMonths, isSameDay, isSameMonth, isToday as isDateToday, parseISO, differenceInCalendarDays, } from 'date-fns'; import { de as deLocale, enUS as enLocale } from 'date-fns/locale'; import { ChevronLeft, ChevronRight, Plus, Edit, Trash2, Copy, Eye, GripVertical, X, Clock, MapPin, Users, AlertCircle, Check, MoreVertical, } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, ContextMenuSeparator, } from '@/components/ui/context-menu'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator, } from '@/components/ui/dropdown-menu'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { CalendarManager, CalendarEvent, CalendarEventType } from '@/lib/calendar-manager'; import { useApiGet } from '@/lib/hooks/useApi'; import { toast } from 'sonner'; import { cn } from '@/lib/utils'; interface ProfessionalCalendarProps { initialEvents?: CalendarEvent[]; onEventCreate?: (event: CalendarEvent) => Promise; onEventUpdate?: (event: CalendarEvent) => Promise; onEventDelete?: (eventId: string) => Promise; variant?: 'month' | 'week' | 'day' | 'agenda'; locale?: 'de' | 'en'; className?: string; } export function ProfessionalCalendar({ initialEvents = [], onEventCreate, onEventUpdate, onEventDelete, variant = 'month', locale = 'de', className, }: ProfessionalCalendarProps) { const [currentDate, setCurrentDate] = useState(new Date()); const [events, setEvents] = useState(initialEvents); const [selectedEvent, setSelectedEvent] = useState(null); const [isCreating, setIsCreating] = useState(false); const [draggedEvent, setDraggedEvent] = useState(null); const [view, setView] = useState<'month' | 'week' | 'day' | 'agenda'>(variant); const [eventFilter, setEventFilter] = useState<'all' | 'exams' | 'events' | 'holidays'>('all'); const contextMenuRef = useRef<{ x: number; y: number } | null>(null); const dateLocale = locale === 'de' ? deLocale : enLocale; // Fetch events from backend const monthParam = format(currentDate, 'yyyy-MM'); const { data: backendEvents, isLoading } = useApiGet( `/api/calendar-events?month=${monthParam}`, { revalidateOnFocus: true } ); useEffect(() => { if (backendEvents) { setEvents(backendEvents); } }, [backendEvents]); // Calculate calendar grid const monthStart = startOfMonth(currentDate); const monthEnd = endOfMonth(monthStart); const calendarStart = startOfWeek(monthStart); const calendarEnd = endOfWeek(monthEnd); const days: Date[] = []; let day = new Date(calendarStart); while (day <= calendarEnd) { days.push(new Date(day)); day = addDays(day, 1); } // Get events for current month const monthEvents = useMemo(() => { return CalendarManager.getEventsInRange(events, monthStart, monthEnd, { showPast: true, }); }, [events, monthStart, monthEnd]); // Get events for a specific date // Filtered events based on the active filter tab const filteredEvents = useMemo(() => { if (eventFilter === 'all') return events; const filterMap: Record = { exams: ['EXAM', 'TEST', 'ASSESSMENT'], events: ['EVENT', 'ACTIVITY', 'MEETING', 'TRIP'], holidays: ['HOLIDAY', 'BREAK'], }; const types = filterMap[eventFilter] || []; return events.filter(e => types.includes(e.type?.toUpperCase() || '')); }, [events, eventFilter]); const getEventsForDate = useCallback((date: Date): CalendarEvent[] => { return CalendarManager.getEventsForDate(filteredEvents, date); }, [filteredEvents]); // Handle event creation const handleCreateEvent = useCallback( async (eventData: Omit) => { try { const newEvent: CalendarEvent = { ...eventData, id: `event-${Date.now()}`, createdAt: new Date(), }; setEvents((prev) => [...prev, newEvent]); if (onEventCreate) { await onEventCreate(newEvent); } toast.success('Ereignis erfolgreich erstellt'); setIsCreating(false); } catch (error) { toast.error('Failed to create event'); console.error(error); } }, [onEventCreate] ); // Handle event update const handleUpdateEvent = useCallback( async (event: CalendarEvent) => { try { setEvents((prev) => prev.map((e) => (e.id === event.id ? { ...event, updatedAt: new Date() } : e)) ); if (onEventUpdate) { await onEventUpdate(event); } toast.success('Ereignis aktualisiert successfully'); } catch (error) { toast.error('Failed to update event'); console.error(error); } }, [onEventUpdate] ); // Handle event deletion const handleDeleteEvent = useCallback( async (eventId: string) => { try { setEvents((prev) => prev.filter((e) => e.id !== eventId)); if (onEventDelete) { await onEventDelete(eventId); } setSelectedEvent(null); toast.success('Ereignis gelöscht successfully'); } catch (error) { toast.error('Failed to delete event'); console.error(error); } }, [onEventDelete] ); // Handle drag-and-drop const handleDragStart = (e: React.DragEvent, event: CalendarEvent) => { setDraggedEvent(event); e.dataTransfer.effectAllowed = 'move'; }; const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; }; const handleDrop = (e: React.DragEvent, targetDate: Date) => { e.preventDefault(); if (!draggedEvent) return; const newEvent = { ...draggedEvent, date: targetDate, updatedAt: new Date(), }; handleUpdateEvent(newEvent); setDraggedEvent(null); toast.success('Event moved successfully'); }; // Handle right-click context menu const handleContextMenu = ( e: React.MouseEvent, event: CalendarEvent ) => { e.preventDefault(); contextMenuRef.current = { x: e.clientX, y: e.clientY }; setSelectedEvent(event); }; // Render calendar cell const renderCalendarCell = (date: Date) => { const dateEvents = getEventsForDate(date); const isCurrentMonth = isSameMonth(date, currentDate); const isToday = isDateToday(date); return ( handleDrop(e, date)} className={cn( 'min-h-24 p-2 border rounded-lg transition-colors', isCurrentMonth ? 'bg-background' : 'bg-muted/30', isToday && 'ring-2 ring-primary border-primary', 'hover:bg-accent/50 cursor-pointer' )} > {/* Date header */}
{format(date, 'd')}
{/* Events */}
{dateEvents.slice(0, 2).map((event) => ( handleDragStart(e, event)} onContextMenu={(e) => handleContextMenu(e, event)} onClick={() => setSelectedEvent(event)} className={cn( 'text-xs p-1 rounded cursor-grab active:cursor-grabbing', 'truncate hover:opacity-80 transition-opacity', CalendarManager.getColorForType(event.type) )} whileHover={{ scale: 1.05 }} whileDrag={{ scale: 1.1, opacity: 0.7 }} >
{CalendarManager.getIconForType(event.type)} {event.title}
))} {dateEvents.length > 2 && (
+{dateEvents.length - 2} more
)}
{/* Add event button */}
); }; return (
{/* Header */}

{format(currentDate, 'MMMM yyyy', { locale: dateLocale })}

{/* View tabs */}
setView(v as any)}> Monat Woche Agenda {/* Event filter tabs (replaces exam-calendar and school-events) */} setEventFilter(v as any)}> Alle Prüfungen Veranstaltungen Ferien
{/* Calendar grid */} {view === 'month' && (
{/* Weekday headers */}
{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map((day) => (
{day}
))}
{/* Calendar cells */}
{days.map((day) => (
{renderCalendarCell(day)}
))}
)} {/* Agenda view */} {view === 'agenda' && (
{monthEvents.map((event) => ( setSelectedEvent(event)} onContextMenu={(e) => handleContextMenu(e, event)} >
{CalendarManager.getIconForType(event.type)}

{event.title}

{event.type}

{format(event.date, 'PPP', { locale: dateLocale })} um {CalendarManager.formatTimeRange(event)}

{event.description && (

{event.description}

)}
setSelectedEvent(event)}> Edit handleDeleteEvent(event.id)}> Delete
))}
)} {/* Event detail dialog */} {selectedEvent && ( setSelectedEvent(null)} onUpdate={handleUpdateEvent} onDelete={handleDeleteEvent} /> )} {/* Create/Edit event dialog */} {isCreating && ( setIsCreating(false)} onCreate={handleCreateEvent} /> )}
); } // Event Detail Dialog Component function EventDetailDialog({ event, onClose, onUpdate, onDelete, }: { event: CalendarEvent; onClose: () => void; onUpdate: (event: CalendarEvent) => void; onDelete: (id: string) => void; }) { const [isEditing, setIsEditing] = useState(false); return ( {CalendarManager.getIconForType(event.type)} {event.title} {event.type}

{format(event.date, 'PPP')} um {CalendarManager.formatTimeRange(event)}

{event.location && (

{event.location}

)} {event.description && (

{event.description}

)} {event.attendees && event.attendees.length > 0 && (

{event.attendees.join(', ')}

)}
); } // Event Creator Dialog Component function EventCreatorDialog({ onClose, onCreate, }: { onClose: () => void; onCreate: (event: Omit) => Promise; }) { const [formData, setFormData] = useState({ title: '', type: 'custom' as CalendarEventType, date: new Date(), startTime: '09:00', endTime: '10:00', description: '', location: '', allDay: false, }); const handleSubmit = async () => { if (!formData.title.trim()) { toast.error('Please enter a title'); return; } await onCreate({ ...formData, date: formData.date, allDay: formData.allDay, }); onClose(); }; return ( Create Neues Ereignis
setFormData({ ...formData, title: e.target.value })} placeholder="Ereignistitel" />
setFormData({ ...formData, date: parseISO(e.target.value) })} />
{!formData.allDay && (
setFormData({ ...formData, startTime: e.target.value })} />
setFormData({ ...formData, endTime: e.target.value })} />
)}
setFormData({ ...formData, location: e.target.value })} placeholder="Ereignisort" />