'use client' import { useState } from 'react' const DAYS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] const MONTHS = ['January','February','March','April','May','June', 'July','August','September','October','November','December'] interface Props { selected: string // YYYY-MM-DD or '' onSelect: (date: string) => void minDate: string // YYYY-MM-DD — dates before this are disabled maxDate: string // YYYY-MM-DD — dates after this are disabled disabledDates?: Set // fully blocked (strikethrough, not clickable) busyDates?: Set // has events but still selectable (orange dot) } function pad(n: number) { return String(n).padStart(2, '0') } export default function CalendarPicker({ selected, onSelect, minDate, maxDate, disabledDates, busyDates, }: Props) { const today = new Date().toISOString().slice(0, 10) const initRef = selected || minDate || today const [year, setYear] = useState(() => parseInt(initRef.slice(0, 4))) const [month, setMonth] = useState(() => parseInt(initRef.slice(5, 7)) - 1) const firstDow = new Date(year, month, 1).getDay() const daysInMonth = new Date(year, month + 1, 0).getDate() const curMonthStr = `${year}-${pad(month + 1)}` const canPrev = curMonthStr > minDate.slice(0, 7) const canNext = curMonthStr < maxDate.slice(0, 7) const prevMonth = () => { if (!canPrev) return if (month === 0) { setYear(y => y - 1); setMonth(11) } else setMonth(m => m - 1) } const nextMonth = () => { if (!canNext) return if (month === 11) { setYear(y => y + 1); setMonth(0) } else setMonth(m => m + 1) } // Empty cells before the 1st, then day numbers const cells: (number | null)[] = [ ...Array.from({ length: firstDow }, () => null), ...Array.from({ length: daysInMonth }, (_, i) => i + 1), ] return (
{/* Month navigation */}
{MONTHS[month]} {year}
{/* Day-of-week labels */}
{DAYS.map(d => (
{d}
))}
{/* Day cells */}
{cells.map((day, i) => { if (!day) return
const date = `${year}-${pad(month + 1)}-${pad(day)}` const isSelected = date === selected const isToday = date === today const outOfRange = date < minDate || date > maxDate const hardBlocked = disabledDates?.has(date) ?? false const isDisabled = outOfRange || hardBlocked const isBusy = !isDisabled && (busyDates?.has(date) ?? false) return ( ) })}
{/* Legend */} {busyDates && busyDates.size > 0 && (
May have limited availability
)}
) }