import { useState, useMemo } from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; import type { Archive } from '../api/client'; import { api } from '../api/client'; import { parseUTCDate } from '../utils/date'; interface CalendarViewProps { archives: Archive[]; onArchiveClick?: (archive: Archive) => void; highlightedArchiveId?: number | null; } function getDaysInMonth(year: number, month: number): number { return new Date(year, month + 1, 0).getDate(); } function getFirstDayOfMonth(year: number, month: number): number { return new Date(year, month, 1).getDay(); } const MONTH_NAMES = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; const DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; export function CalendarView({ archives, onArchiveClick, highlightedArchiveId }: CalendarViewProps) { const today = new Date(); const [currentMonth, setCurrentMonth] = useState(today.getMonth()); const [currentYear, setCurrentYear] = useState(today.getFullYear()); const [selectedDate, setSelectedDate] = useState(null); const [selectedArchiveId, setSelectedArchiveId] = useState(null); // Group archives by date (using local timezone from UTC timestamps) const archivesByDate = useMemo(() => { const map = new Map(); archives.forEach(archive => { const date = parseUTCDate(archive.completed_at || archive.created_at) || new Date(); const key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; const existing = map.get(key) || []; existing.push(archive); map.set(key, existing); }); return map; }, [archives]); const daysInMonth = getDaysInMonth(currentYear, currentMonth); const firstDay = getFirstDayOfMonth(currentYear, currentMonth); const prevMonth = () => { if (currentMonth === 0) { setCurrentMonth(11); setCurrentYear(currentYear - 1); } else { setCurrentMonth(currentMonth - 1); } }; const nextMonth = () => { if (currentMonth === 11) { setCurrentMonth(0); setCurrentYear(currentYear + 1); } else { setCurrentMonth(currentMonth + 1); } }; const goToToday = () => { setCurrentMonth(today.getMonth()); setCurrentYear(today.getFullYear()); }; // Build calendar grid const calendarDays: (number | null)[] = []; for (let i = 0; i < firstDay; i++) { calendarDays.push(null); } for (let day = 1; day <= daysInMonth; day++) { calendarDays.push(day); } const selectedArchives = selectedDate ? archivesByDate.get(selectedDate) || [] : []; // Clear selected archive when date changes const handleDateSelect = (dateKey: string | null) => { if (dateKey !== selectedDate) { setSelectedArchiveId(null); } setSelectedDate(dateKey); }; return (
{/* Calendar */}
{/* Header */}

{MONTH_NAMES[currentMonth]} {currentYear}

{/* Day headers */}
{DAY_NAMES.map(day => (
{day}
))}
{/* Calendar grid */}
{calendarDays.map((day, index) => { if (day === null) { return
; } const dateKey = `${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; const dayArchives = archivesByDate.get(dateKey) || []; const hasArchives = dayArchives.length > 0; const isToday = day === today.getDate() && currentMonth === today.getMonth() && currentYear === today.getFullYear(); const isSelected = dateKey === selectedDate; const successCount = dayArchives.filter(a => a.status === 'completed').length; const failedCount = dayArchives.filter(a => a.status === 'failed').length; return ( ); })}
{/* Monthly stats */}
{archives.filter(a => { const d = parseUTCDate(a.completed_at || a.created_at) || new Date(); return d.getMonth() === currentMonth && d.getFullYear() === currentYear; }).length}
Prints this month
{archives.filter(a => { const d = parseUTCDate(a.completed_at || a.created_at) || new Date(); return d.getMonth() === currentMonth && d.getFullYear() === currentYear && a.status === 'completed'; }).length}
Successful
{archives.filter(a => { const d = parseUTCDate(a.completed_at || a.created_at) || new Date(); return d.getMonth() === currentMonth && d.getFullYear() === currentYear && a.status === 'failed'; }).length}
Failed
{/* Selected day details */}
{selectedDate ? ( <>

{new Date(selectedDate + 'T12:00:00').toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}

{selectedArchives.length > 0 ? (
{selectedArchives.map(archive => { const isHighlighted = archive.id === selectedArchiveId || archive.id === highlightedArchiveId; return ( ); })}
) : (

No prints on this day

)} ) : (

Select a day to see prints

)}
); }