import { useState } from 'react'; import type { Task, User } from './data'; import { PRIORITY_COLORS } from './data'; import { Avatar } from './Shared'; interface CalendarProps { tasks: Task[]; currentUser: User; calMonth: { year: number; month: number }; calView: string; onMonthChange: (m: { year: number; month: number }) => void; onViewChange: (v: string) => void; onTaskClick: (t: Task) => void; onDayClick: (date: string, el: HTMLElement) => void; filterUser: string | null; searchQuery: string; users: User[]; } const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const DAYS = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']; function filterTasks(tasks: Task[], user: User, filterUser: string | null, search: string) { let t = tasks; if (user.role === 'employee') t = t.filter(x => x.assignee === user.id); if (filterUser) t = t.filter(x => x.assignee === filterUser); if (search) t = t.filter(x => x.title.toLowerCase().includes(search.toLowerCase())); return t; } function getMonthDays(year: number, month: number) { const first = new Date(year, month, 1); const startDay = first.getDay(); const daysInMonth = new Date(year, month + 1, 0).getDate(); const prevMonthDays = new Date(year, month, 0).getDate(); const cells: { date: Date; isCurrentMonth: boolean }[] = []; for (let i = startDay - 1; i >= 0; i--) cells.push({ date: new Date(year, month - 1, prevMonthDays - i), isCurrentMonth: false }); for (let i = 1; i <= daysInMonth; i++) cells.push({ date: new Date(year, month, i), isCurrentMonth: true }); const rem = 42 - cells.length; for (let i = 1; i <= rem; i++) cells.push({ date: new Date(year, month + 1, i), isCurrentMonth: false }); return cells; } function getWeekDays(_year: number, _month: number) { const today = new Date(); const dayOfWeek = today.getDay(); const start = new Date(today); start.setDate(today.getDate() - dayOfWeek); const cells: Date[] = []; for (let i = 0; i < 7; i++) { const d = new Date(start); d.setDate(start.getDate() + i); cells.push(d); } return cells; } function dateStr(d: Date) { return d.toISOString().split('T')[0]; } function isToday(d: Date) { const t = new Date(); return d.getDate() === t.getDate() && d.getMonth() === t.getMonth() && d.getFullYear() === t.getFullYear(); } function TaskChip({ task, onClick, users }: { task: Task; onClick: () => void; users: User[] }) { const p = PRIORITY_COLORS[task.priority]; return (
{ e.stopPropagation(); onClick(); }}> {task.title}
); } function MorePopover({ tasks, onTaskClick, onClose, users }: { tasks: Task[]; onTaskClick: (t: Task) => void; onClose: () => void; users: User[] }) { return (
e.stopPropagation()}>
{tasks.length} tasks
{tasks.map(t => { onTaskClick(t); onClose(); }} users={users} />)}
); } export function QuickAddPanel({ date, onAdd, onOpenFull, onClose, users }: { date: string; onAdd: (t: Partial) => void; onOpenFull: () => void; onClose: () => void; users: User[] }) { const [title, setTitle] = useState(''); const [assignee, setAssignee] = useState(users[0]?.id || ''); const [priority, setPriority] = useState<'medium' | 'low' | 'high' | 'critical'>('medium'); const submit = () => { if (!title.trim()) return; onAdd({ title, assignee, priority, dueDate: date, status: 'todo', description: '', tags: [], subtasks: [], comments: [], activity: [] }); setTitle(''); }; return (
e.stopPropagation()}>
📅 Add Task — {new Date(date + 'T00:00:00').toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })}
setTitle(e.target.value)} onKeyDown={e => e.key === 'Enter' && submit()} />
); } export function CalendarView({ tasks, currentUser, calMonth, calView, onMonthChange, onViewChange, onTaskClick, onDayClick, filterUser, searchQuery, users }: CalendarProps) { const [morePopover, setMorePopover] = useState<{ date: string; tasks: Task[] } | null>(null); const filtered = filterTasks(tasks, currentUser, filterUser, searchQuery); const prevMonth = () => { if (calView === 'week') { const d = new Date(); d.setDate(d.getDate() - 7); onMonthChange({ year: d.getFullYear(), month: d.getMonth() }); return; } const m = calMonth.month === 0 ? 11 : calMonth.month - 1; const y = calMonth.month === 0 ? calMonth.year - 1 : calMonth.year; onMonthChange({ year: y, month: m }); }; const nextMonth = () => { if (calView === 'week') { const d = new Date(); d.setDate(d.getDate() + 7); onMonthChange({ year: d.getFullYear(), month: d.getMonth() }); return; } const m = calMonth.month === 11 ? 0 : calMonth.month + 1; const y = calMonth.month === 11 ? calMonth.year + 1 : calMonth.year; onMonthChange({ year: y, month: m }); }; const goToday = () => { const n = new Date(); onMonthChange({ year: n.getFullYear(), month: n.getMonth() }); }; return (
{MONTHS[calMonth.month]} {calMonth.year}
{calView === 'month' ? (
{DAYS.map(d =>
{d}
)} {getMonthDays(calMonth.year, calMonth.month).map((cell, i) => { const ds = dateStr(cell.date); const dayTasks = filtered.filter(t => t.dueDate === ds); const show = dayTasks.slice(0, 3); const extra = dayTasks.length - 3; return (
{ if (!(e.target as HTMLElement).closest('.task-chip,.more-tasks-link,.more-popover')) onDayClick(ds, e.currentTarget); }}>
{cell.date.getDate()}
{show.map(t => onTaskClick(t)} users={users} />)} {extra > 0 && ( { e.stopPropagation(); setMorePopover({ date: ds, tasks: dayTasks }); }}> +{extra} more )}
{morePopover?.date === ds && setMorePopover(null)} users={users} />}
); })}
) : (
{getWeekDays(calMonth.year, calMonth.month).map((d, i) => (
{DAYS[d.getDay()]} {d.getDate()}
))} {getWeekDays(calMonth.year, calMonth.month).map((d, i) => { const ds = dateStr(d); const dayTasks = filtered.filter(t => t.dueDate === ds); return (
{ if (!(e.target as HTMLElement).closest('.task-chip,.week-chip')) onDayClick(ds, e.currentTarget); }}> {dayTasks.map(t => { const p = PRIORITY_COLORS[t.priority]; return (
{ e.stopPropagation(); onTaskClick(t); }}> {t.title}
); })}
); })}
)}
); }