/** * 6-week (42-cell) calendar grid starting from Monday. Composes CalendarDay cells. * Ported from webapp/js/calendar.js renderCalendar. */ "use client"; import { useMemo } from "react"; import { firstDayOfMonth, getMonday, localDateString, } from "@/lib/date-utils"; import type { CalendarEvent, DutyWithUser } from "@/types"; import { dutiesByDate, calendarEventsByDate } from "@/lib/calendar-data"; import { cn } from "@/lib/utils"; import { CalendarDay } from "./CalendarDay"; export interface CalendarGridProps { /** Currently displayed month. */ currentMonth: Date; /** All duties for the visible range (will be grouped by date). */ duties: DutyWithUser[]; /** All calendar events for the visible range. */ calendarEvents: CalendarEvent[]; /** Called when a day cell is clicked (opens day detail). Receives date key and cell rect for popover. */ onDayClick: (dateKey: string, anchorRect: DOMRect) => void; className?: string; } const CELLS = 42; export function CalendarGrid({ currentMonth, duties, calendarEvents, onDayClick, className, }: CalendarGridProps) { const dutiesByDateMap = useMemo( () => dutiesByDate(duties), [duties] ); const calendarEventsByDateMap = useMemo( () => calendarEventsByDate(calendarEvents), [calendarEvents] ); const todayKey = localDateString(new Date()); const cells = useMemo(() => { const first = firstDayOfMonth(currentMonth); const start = getMonday(first); const result: { date: Date; key: string; month: number }[] = []; const d = new Date(start); for (let i = 0; i < CELLS; i++) { const key = localDateString(d); result.push({ date: new Date(d), key, month: d.getMonth() }); d.setDate(d.getDate() + 1); } return result; }, [currentMonth]); return (