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; } const CalendarMonthView: React.FC = ({ currentDate, events, onDateClick, onEventClick, }) => { const { t } = useTranslation(); const [firstDayOfWeek, setFirstDayOfWeek] = useState(1); // Default to Monday // 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); } }; 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)} className={`p-2 border-r border-b border-gray-100 dark:border-gray-800 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 flex flex-col ${ !isCurrentMonth ? 'bg-gray-50 dark:bg-gray-800' : 'bg-white dark:bg-gray-900' } ${isTodayDate ? 'bg-blue-50 dark:bg-blue-900/20 ring-2 ring-blue-300 dark:ring-blue-600' : ''}`} >
{isTodayDate && ( {format(day, 'd')} )} {!isTodayDate && format(day, 'd')}
{/* Events */}
{dayEvents.slice(0, 3).map((event) => (
handleEventClick(event, e) } className={`text-xs p-1 rounded text-white truncate cursor-pointer hover:opacity-80 transition-opacity ${ event.type === 'task' ? 'border-l-2 border-l-white/50' : '' }`} style={{ backgroundColor: event.color || '#3b82f6', }} title={`${event.type === 'task' ? '📋 ' : ''}${event.title}`} > {event.type === 'task' && '📋 '} {event.title}
))} {dayEvents.length > 3 && (
+{dayEvents.length - 3} more
)}
); })}
); }; export default CalendarMonthView;