// Year View Panel - KalNext-style 12-month yearly overview // Shows all months in a 4x3 grid with event density indicators import React, { useState, useMemo } from "react" import { useCalendarEvents, type DecryptedCalendarEvent } from "@/hooks/useCalendarEvents" interface YearViewPanelProps { onClose?: () => void onMonthSelect?: (year: number, month: number) => void shapeMode?: boolean initialYear?: number } // Helper functions const getDaysInMonth = (year: number, month: number) => { return new Date(year, month + 1, 0).getDate() } const getFirstDayOfMonth = (year: number, month: number) => { const day = new Date(year, month, 1).getDay() return day === 0 ? 6 : day - 1 // Monday-first } const isSameDay = (date1: Date, date2: Date) => { return ( date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate() ) } const MONTH_NAMES = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] const SHORT_MONTH_NAMES = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] export const YearViewPanel: React.FC = ({ onClose: _onClose, onMonthSelect, shapeMode: _shapeMode = false, initialYear, }) => { const [currentYear, setCurrentYear] = useState(initialYear || new Date().getFullYear()) // Detect dark mode const isDarkMode = typeof document !== "undefined" && document.documentElement.classList.contains("dark") // Fetch all events for the current year const yearStart = new Date(currentYear, 0, 1) const yearEnd = new Date(currentYear, 11, 31, 23, 59, 59) const { events, loading, getEventsForDate } = useCalendarEvents({ startDate: yearStart, endDate: yearEnd, }) // Colors const colors = isDarkMode ? { bg: "#1f2937", text: "#e4e4e7", textMuted: "#a1a1aa", border: "#374151", monthBg: "#252525", todayBg: "#22c55e30", todayBorder: "#22c55e", eventDot1: "#3b82f620", // 1 event eventDot2: "#3b82f640", // 2 events eventDot3: "#3b82f680", // 3+ events eventDotMax: "#3b82f6", // 5+ events headerBg: "#22c55e", } : { bg: "#f9fafb", text: "#1f2937", textMuted: "#6b7280", border: "#e5e7eb", monthBg: "#ffffff", todayBg: "#22c55e20", todayBorder: "#22c55e", eventDot1: "#3b82f620", eventDot2: "#3b82f640", eventDot3: "#3b82f680", eventDotMax: "#3b82f6", headerBg: "#22c55e", } // Get event count for a specific date const getEventCount = (date: Date) => { return getEventsForDate(date).length } // Get background color based on event density const getEventDensityColor = (count: number) => { if (count === 0) return "transparent" if (count === 1) return colors.eventDot1 if (count === 2) return colors.eventDot2 if (count <= 4) return colors.eventDot3 return colors.eventDotMax } // Navigation const goToPrevYear = () => setCurrentYear((y) => y - 1) const goToNextYear = () => setCurrentYear((y) => y + 1) const goToCurrentYear = () => setCurrentYear(new Date().getFullYear()) const today = new Date() // Generate mini calendar for a month const renderMiniMonth = (month: number) => { const daysInMonth = getDaysInMonth(currentYear, month) const firstDay = getFirstDayOfMonth(currentYear, month) const days: { day: number | null; date: Date | null }[] = [] // Leading empty cells for (let i = 0; i < firstDay; i++) { days.push({ day: null, date: null }) } // Days of month for (let i = 1; i <= daysInMonth; i++) { days.push({ day: i, date: new Date(currentYear, month, i) }) } // Trailing empty cells to complete grid (6 rows max) while (days.length < 42) { days.push({ day: null, date: null }) } return (
onMonthSelect?.(currentYear, month)} onPointerDown={(e) => e.stopPropagation()} > {/* Month name */}
{SHORT_MONTH_NAMES[month]}
{/* Day headers */}
{["M", "T", "W", "T", "F", "S", "S"].map((day, i) => (
{day}
))}
{/* Days grid */}
{days.slice(0, 42).map(({ day, date }, i) => { const isToday = date && isSameDay(date, today) const eventCount = date ? getEventCount(date) : 0 const densityColor = getEventDensityColor(eventCount) return (
{day}
) })}
) } return (
{/* Header with year navigation */}
{currentYear} {currentYear !== new Date().getFullYear() && ( )}
{/* 12-month grid (4x3 layout) */}
{loading ? (
Loading calendar data...
) : (
{Array.from({ length: 12 }, (_, month) => renderMiniMonth(month))}
)}
{/* Legend */}
Today
1 event
3+ events
5+ events
) }