import React, { useState, useMemo } from 'react'; import { format, startOfMonth, endOfMonth, startOfWeek, endOfWeek, eachDayOfInterval, isSameMonth, isSameDay, addMonths, subMonths, isToday, parseISO, isWithinInterval, startOfDay, endOfDay } from 'date-fns'; import { ChevronLeft, ChevronRight, Calendar as CalendarIcon, CheckCircle2, Circle, Clock, Users } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import { Task } from '@/services/tasksApi'; import { Badge } from '@/components/ui/badge'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; interface Employee { id: number; firstName: string; lastName: string; profileImage?: string; } interface MonthlyTaskCalendarProps { tasks: Task[]; employees?: Employee[]; onSelectDate?: (date: Date) => void; isLoading?: boolean; } export const MonthlyTaskCalendar: React.FC = ({ tasks, employees = [], // Default to empty array if not provided onSelectDate, isLoading = false }) => { const [currentMonth, setCurrentMonth] = useState(new Date()); const nextMonth = () => setCurrentMonth(addMonths(currentMonth, 1)); const prevMonth = () => setCurrentMonth(subMonths(currentMonth, 1)); const goToToday = () => setCurrentMonth(new Date()); const monthStart = startOfMonth(currentMonth); const monthEnd = endOfMonth(monthStart); const startDate = startOfWeek(monthStart); const endDate = endOfWeek(monthEnd); const calendarDays = eachDayOfInterval({ start: startDate, end: endDate }); const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; // Memoize tasks for each day // Since tasks can span multiple days, we need to check intervals const getTasksForDate = (date: Date) => { return tasks.filter(task => { if (!task.stateDate) return false; const taskStart = startOfDay(parseISO(task.stateDate)); let taskEnd = task.endDate ? endOfDay(parseISO(task.endDate)) : taskStart; // Handle invalid date ranges where end date is before start date if (taskEnd < taskStart) { taskEnd = taskStart; } try { return isWithinInterval(date, { start: taskStart, end: taskEnd }); } catch (e) { console.error("Error checking date interval for task:", task.id, e); return false; } }); }; const getPriorityColor = (priority?: string) => { switch (priority?.toLowerCase()) { case 'high': return 'bg-red-100 text-red-700 border-red-200'; case 'medium': return 'bg-orange-100 text-orange-700 border-orange-200'; case 'low': return 'bg-green-100 text-green-700 border-green-200'; default: return 'bg-gray-100 text-gray-700 border-gray-200'; } }; const getStatusColor = (status: string) => { switch (status) { case "New": return "bg-blue-500"; case "In Progress": return "bg-orange-500"; case "In Review": return "bg-purple-500"; case "Completed": return "bg-green-500"; case "Closed": return "bg-gray-500"; default: return "bg-slate-500"; } }; const getEmployeeDetails = (id: number | null) => { if (!id) return null; return employees.find(e => e.id === id); }; if (isLoading) { return (

Loading calendar...

); } return (
{/* Calendar Header */}

{format(currentMonth, 'MMMM yyyy')}

Manage tasks schedule

{/* Week Days Header */}
{weekDays.map(day => (
{day}
))}
{/* Calendar Grid */}
{calendarDays.map((day, dayIdx) => { const dayTasks = getTasksForDate(day); const isCurrentMonth = isSameMonth(day, currentMonth); const isDayToday = isToday(day); return (
onSelectDate && onSelectDate(day)} >
{format(day, 'd')} {dayTasks.length > 0 && ( {dayTasks.length} )}
{dayTasks.map((task, idx) => { const assignee = getEmployeeDetails(task.assignedTo); return (
{task.title} {assignee && (
{assignee.firstName.charAt(0)}{assignee.lastName.charAt(0)}
)}
{task.type} Priority {task.status}

{task.title}

#{task.id}

{assignee && (
{assignee.firstName.charAt(0)}{assignee.lastName.charAt(0)}
{assignee.firstName} {assignee.lastName}
)}
Start {task.stateDate ? format(parseISO(task.stateDate), 'MMM d') : '-'}
Due {task.endDate ? format(parseISO(task.endDate), 'MMM d') : '-'}
) })}
); })}
); };