diff --git a/src/components/CalendarView.tsx b/src/components/CalendarView.tsx new file mode 100644 index 0000000..fb37b27 --- /dev/null +++ b/src/components/CalendarView.tsx @@ -0,0 +1,160 @@ +'use client'; + +import { useApp, type Task } from './AppProvider'; +import { useState, useMemo } from 'react'; +import { + startOfMonth, endOfMonth, startOfWeek, endOfWeek, addMonths, subMonths, + format, isSameDay, parseISO, isToday, isPast, isFuture, + eachDayOfInterval, +} from 'date-fns'; + +export default function CalendarView() { + const { tasks, projects, setSelectedTask, setShowEditTaskModal, setEditingTask } = useApp(); + const [currentDate, setCurrentDate] = useState(new Date()); + + const year = currentDate.getFullYear(); + const month = currentDate.getMonth(); + + const monthStart = startOfMonth(currentDate); + const monthEnd = endOfMonth(currentDate); + const calendarStart = startOfWeek(monthStart); + const calendarEnd = endOfWeek(monthEnd); + + const days = eachDayOfInterval({ start: calendarStart, end: calendarEnd }); + + const daysInMonth = days.filter(d => d.getMonth() === month); + + // Group tasks by date + const tasksByDate = useMemo(() => { + const map = new Map(); + tasks.forEach(task => { + if (!task.dueDate) return; + const date = format(parseISO(task.dueDate), 'yyyy-MM-dd'); + if (!map.has(date)) map.set(date, []); + map.get(date)!.push(task); + }); + return map; + }, [tasks]); + + const priorityColors = { + urgent: 'bg-[var(--priority-urgent)]', + high: 'bg-[var(--priority-high)]', + medium: 'bg-[var(--priority-medium)]', + low: 'bg-[var(--priority-low)]', + }; + + const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + + const monthName = format(currentDate, 'MMMM yyyy'); + + const handleDayClick = (date: Date) => { + // Could open a "new task" modal pre-filled with this date + console.log('Day clicked:', format(date, 'yyyy-MM-dd')); + }; + + const handleTaskClick = (task: Task) => { + setSelectedTask(task); + setEditingTask(task); + setShowEditTaskModal(true); + }; + + return ( +
+
+ {/* Month navigation */} +
+ +

{monthName}

+ +
+ + {/* Calendar grid */} +
+ {/* Week day headers */} +
+ {weekDays.map(day => ( +
+ {day} +
+ ))} +
+ + {/* Days */} +
+ {days.map((day, index) => { + const dateStr = format(day, 'yyyy-MM-dd'); + const isCurrentMonth = day.getMonth() === month; + const isTodayDate = isToday(day); + const dayTasks = tasksByDate.get(dateStr) || []; + const incompleteTasks = dayTasks.filter(t => !t.completed); + + return ( +
handleDayClick(day)} + className={`min-h-[80px] p-1.5 border-b border-r border-border cursor-pointer hover:bg-content/50 transition-colors ${ + !isCurrentMonth ? 'opacity-40' : '' + }`} + > + {/* Date number */} +
+ + {format(day, 'd')} + + {incompleteTasks.length > 0 && ( + {incompleteTasks.length} + )} +
+ + {/* Task indicators */} +
+ {dayTasks.slice(0, 3).map(task => ( + + ))} + {dayTasks.length > 3 && ( + + +{dayTasks.length - 3} more + + )} +
+
+ ); + })} +
+
+ + {/* Legend */} +
+ Priority: + Urgent + High + Medium + Low +
+
+
+ ); +}