import React, { useState, useEffect } from 'react'; import { format, startOfMonth, endOfMonth, eachDayOfInterval, isSameMonth, isToday, startOfWeek, endOfWeek, } from 'date-fns'; import { useTranslation } from 'react-i18next'; import { getFirstDayOfWeek, getLocaleFirstDayOfWeek, } from '../../utils/profileService'; interface CalendarEvent { id: string; title: string; start: Date; end: Date; type: 'task' | 'event'; color?: string; } interface CalendarMonthViewProps { currentDate: Date; events: CalendarEvent[]; onDateClick?: (date: Date) => void; onEventClick?: (event: CalendarEvent) => void; onEventDrop?: (eventId: string, newDate: Date) => void; } const CalendarMonthView: React.FC = ({ currentDate, events, onDateClick, onEventClick, onEventDrop, }) => { const { t } = useTranslation(); const [firstDayOfWeek, setFirstDayOfWeek] = useState(1); // Default to Monday const [draggedEventId, setDraggedEventId] = useState(null); // Load first day of week setting useEffect(() => { const loadFirstDayOfWeek = async () => { try { const firstDay = await getFirstDayOfWeek(); setFirstDayOfWeek(firstDay); } catch { const fallbackFirstDay = getLocaleFirstDayOfWeek( navigator.language ); setFirstDayOfWeek(fallbackFirstDay); } }; loadFirstDayOfWeek(); }, []); const monthStart = startOfMonth(currentDate); const monthEnd = endOfMonth(currentDate); const calendarStart = startOfWeek(monthStart, { weekStartsOn: firstDayOfWeek as 0 | 1 | 2 | 3 | 4 | 5 | 6, }); const calendarEnd = endOfWeek(monthEnd, { weekStartsOn: firstDayOfWeek as 0 | 1 | 2 | 3 | 4 | 5 | 6, }); const days = eachDayOfInterval({ start: calendarStart, end: calendarEnd, }); // Generate weekdays array based on first day of week setting const getAllWeekDays = () => [ t('weekdays.sunday', 'Sun'), t('weekdays.monday', 'Mon'), t('weekdays.tuesday', 'Tue'), t('weekdays.wednesday', 'Wed'), t('weekdays.thursday', 'Thu'), t('weekdays.friday', 'Fri'), t('weekdays.saturday', 'Sat'), ]; const getWeekDays = () => { const allDays = getAllWeekDays(); return [ ...allDays.slice(firstDayOfWeek), ...allDays.slice(0, firstDayOfWeek), ]; }; const weekDays = getWeekDays(); const handleDateClick = (date: Date) => { if (onDateClick) { onDateClick(date); } }; const handleEventClick = (event: CalendarEvent, e: React.MouseEvent) => { e.stopPropagation(); if (onEventClick) { onEventClick(event); } }; const handleDragStart = (event: CalendarEvent, e: React.DragEvent) => { e.stopPropagation(); setDraggedEventId(event.id); e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', event.id); }; const handleDragEnd = () => { setDraggedEventId(null); }; const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; }; const handleDrop = (day: Date, e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); const eventId = e.dataTransfer.getData('text/plain'); if (eventId && onEventDrop) { onEventDrop(eventId, day); } setDraggedEventId(null); }; return (
{/* Week days header */}
{weekDays.map((day) => (
{day}
))}
{/* Calendar grid */}
{days.map((day) => { const dayEvents = events.filter( (event) => format(event.start, 'yyyy-MM-dd') === format(day, 'yyyy-MM-dd') ); const isCurrentMonth = isSameMonth(day, currentDate); const isTodayDate = isToday(day); return (
handleDateClick(day)} onDragOver={handleDragOver} onDrop={(e) => handleDrop(day, e)} className={`p-2.5 border-b border-gray-200 dark:border-gray-700 cursor-pointer transition-all duration-200 flex flex-col min-h-[100px] ${ !isCurrentMonth ? 'bg-gray-100/50 dark:bg-gray-800/30' : 'bg-white dark:bg-gray-900 hover:bg-blue-50/50 dark:hover:bg-blue-900/10' } ${isTodayDate ? 'bg-gradient-to-br from-blue-50 to-blue-100/50 dark:from-blue-900/30 dark:to-blue-800/20 ring-2 ring-inset ring-blue-400 dark:ring-blue-500' : ''}`} >
{isTodayDate ? ( {format(day, 'd')} ) : ( {format(day, 'd')} )}
{/* Events */}
{dayEvents.slice(0, 3).map((event) => (
handleDragStart(event, e) } onDragEnd={handleDragEnd} onClick={(e) => handleEventClick(event, e) } className={`text-xs px-2 py-1.5 rounded-md text-white truncate transition-all duration-200 font-medium ${ event.type === 'task' ? 'border-l-3 border-l-white/60 cursor-move hover:scale-[1.02] hover:shadow-md' : 'cursor-pointer' } ${draggedEventId === event.id ? 'opacity-50' : ''}`} style={{ backgroundColor: event.color || '#3b82f6', boxShadow: '0 1px 3px rgba(0,0,0,0.1)', }} title={event.title} > {event.title}
))} {dayEvents.length > 3 && (
+{dayEvents.length - 3} more
)}
); })}
); }; export default CalendarMonthView;