'use client'; import { useEffect, useId, useRef, useState } from 'react'; import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react'; import { addCalendarDays, compareLocalDayStart, startOfLocalDay, } from '@/lib/appointmentTime'; interface ScheduleDayPickerProps { value: Date; onChange: (day: Date) => void; /** Optional lower bound for day selection and previous-day navigation. */ minDate?: Date; label?: string; } const MONTH_LABELS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] as const; function daysInMonth(year: number, month: number): number { return new Date(year, month + 1, 0).getDate(); } function buildLocalDay(year: number, month: number, day: number): Date { return new Date(year, month, day, 0, 0, 0, 0); } function clampToValidDay( year: number, month: number, day: number, min?: Date, ): Date { const maxDay = daysInMonth(year, month); let next = buildLocalDay(year, month, Math.min(Math.max(1, day), maxDay)); if (min) { const floor = startOfLocalDay(min); if (compareLocalDayStart(next, floor) < 0) { next = floor; } } return next; } function yearRange(min?: Date, anchor?: Date): number[] { const now = new Date(); const startYear = min ? min.getFullYear() : now.getFullYear() - 5; const endYear = Math.max(now.getFullYear() + 2, anchor?.getFullYear() ?? now.getFullYear()); const years: number[] = []; for (let y = startYear; y <= endYear; y += 1) { years.push(y); } return years; } const selectClassName = ` w-full appearance-none rounded-[var(--radius-sm)] border border-border bg-background-card/90 text-text-primary text-sm pl-2 pr-7 py-1.5 focus:outline-none focus:ring-2 focus:ring-primary/35 focus:border-border-strong disabled:opacity-50 disabled:cursor-not-allowed `; export function ScheduleDayPicker({ value, onChange, minDate, label = 'Schedule date', }: ScheduleDayPickerProps) { const panelId = useId(); const rootRef = useRef(null); const [panelOpen, setPanelOpen] = useState(false); const normalizedValue = startOfLocalDay(value); const normalizedMin = minDate ? startOfLocalDay(minDate) : undefined; const labelText = normalizedValue.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', }); const previousDay = addCalendarDays(normalizedValue, -1); const canGoPrevious = !normalizedMin || compareLocalDayStart(previousDay, normalizedMin) >= 0; const years = yearRange(normalizedMin, normalizedValue); const selectedYear = normalizedValue.getFullYear(); const selectedMonth = normalizedValue.getMonth(); const selectedDay = normalizedValue.getDate(); const dayCount = daysInMonth(selectedYear, selectedMonth); function applyParts(year: number, month: number, day: number, closePanel = false) { onChange(clampToValidDay(year, month, day, normalizedMin)); if (closePanel) { setPanelOpen(false); } } function handlePreviousDay() { if (!canGoPrevious) return; onChange(previousDay); } useEffect(() => { if (!panelOpen) return; function onPointerDown(event: MouseEvent) { if (!rootRef.current?.contains(event.target as Node)) { setPanelOpen(false); } } function onKeyDown(event: KeyboardEvent) { if (event.key === 'Escape') { setPanelOpen(false); } } document.addEventListener('mousedown', onPointerDown); document.addEventListener('keydown', onKeyDown); return () => { document.removeEventListener('mousedown', onPointerDown); document.removeEventListener('keydown', onKeyDown); }; }, [panelOpen]); return (

{label}

{panelOpen && ( )}
); }