import type { AvailabilityConfig } from "@shared/api"; import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react"; import { startOfMonth, endOfMonth, startOfWeek, endOfWeek, eachDayOfInterval, isSameMonth, isSameDay, isToday, isBefore, addDays, addMonths, subMonths, format, startOfDay, getDay, } from "date-fns"; import { useMemo } from "react"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { cn } from "@/lib/utils"; interface DatePickerProps { selectedDate: Date | null; onSelect: (date: Date) => void; availability: AvailabilityConfig; availableDates: string[]; availabilityLoading?: boolean; viewMonth: Date; onViewMonthChange: (month: Date) => void; } const WEEKDAY_HEADERS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]; const DAY_MAP: Record = { 0: "sunday", 1: "monday", 2: "tuesday", 3: "wednesday", 4: "thursday", 5: "friday", 6: "saturday", }; export function DatePicker({ selectedDate, onSelect, availability, availableDates, availabilityLoading = false, viewMonth, onViewMonthChange, }: DatePickerProps) { const monthStart = startOfMonth(viewMonth); const monthEnd = endOfMonth(viewMonth); const calendarStart = startOfWeek(monthStart); const calendarEnd = endOfWeek(monthEnd); const days = eachDayOfInterval({ start: calendarStart, end: calendarEnd }); const availableDateSet = useMemo( () => new Set(availableDates), [availableDates], ); const today = startOfDay(new Date()); const maxDate = addDays(today, availability.maxAdvanceDays); function isDayDisabled(day: Date) { const dateKey = format(day, "yyyy-MM-dd"); if (isBefore(day, today)) return true; if (isBefore(maxDate, day)) return true; const dayName = DAY_MAP[getDay(day)]; if (!availability.weeklySchedule[dayName]?.enabled) return true; if (availabilityLoading) return true; if (!availableDateSet.has(dateKey)) return true; return false; } return (
{/* Month navigation */}
{format(viewMonth, "MMMM yyyy")}
{/* Weekday headers */}
{WEEKDAY_HEADERS.map((d) => (
{d}
))}
{/* Days */}
{days.map((day) => { const inMonth = isSameMonth(day, viewMonth); if (availabilityLoading) { return inMonth ? ( ) : (
); } const disabled = !inMonth || isDayDisabled(day); const selected = selectedDate && isSameDay(day, selectedDate); const todayMark = isToday(day); return ( ); })}
); }