import * as React from "react"; import { format } from "date-fns"; import { CalendarIcon, Info } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { DatePicker } from "@/components/ui/date-picker"; import { MonthYearPicker } from "@/components/ui/month-year-picker"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { toDateOnly, toDateWithPrecision } from "@/lib/dateUtils"; export type DatePrecision = 'exact' | 'month' | 'year' | 'decade' | 'century' | 'approximate'; interface FlexibleDateInputProps { value?: Date; precision?: DatePrecision; onChange: (date: Date | undefined, precision: DatePrecision) => void; placeholder?: string; disabled?: boolean; className?: string; disableFuture?: boolean; disablePast?: boolean; fromYear?: number; toYear?: number; label?: string; } export function FlexibleDateInput({ value, precision = 'exact', onChange, placeholder = "Select date", disabled = false, className, disableFuture = false, disablePast = false, fromYear = 1800, toYear = new Date().getFullYear() + 10, label, }: FlexibleDateInputProps) { const [localPrecision, setLocalPrecision] = React.useState(precision); const [yearValue, setYearValue] = React.useState( value && precision === 'year' ? value.getFullYear().toString() : '' ); React.useEffect(() => { setLocalPrecision(precision); }, [precision]); React.useEffect(() => { if (value && precision === 'year') { setYearValue(value.getFullYear().toString()); } }, [value, precision]); const handlePrecisionChange = (newPrecision: DatePrecision) => { setLocalPrecision(newPrecision); // If we have a value, adjust it to the new precision if (value) { const year = value.getFullYear(); const month = value.getMonth(); let newDate: Date; switch (newPrecision) { case 'year': case 'decade': case 'century': case 'approximate': newDate = new Date(year, 0, 1); // January 1st (local timezone) setYearValue(year.toString()); break; case 'month': newDate = new Date(year, month, 1); // 1st of month (local timezone) break; case 'exact': default: newDate = value; // Keep existing date break; } onChange(newDate, newPrecision); } else { onChange(undefined, newPrecision); } }; const handleYearChange = (e: React.ChangeEvent) => { const yearStr = e.target.value; setYearValue(yearStr); const yearNum = parseInt(yearStr, 10); if (yearStr && !isNaN(yearNum) && yearNum >= fromYear && yearNum <= toYear) { const newDate = new Date(yearNum, 0, 1); onChange(newDate, 'year'); } else if (!yearStr) { onChange(undefined, 'year'); } }; const getPlaceholderText = () => { switch (localPrecision) { case 'year': case 'decade': case 'century': case 'approximate': return 'Enter year (e.g., 2005)'; case 'month': return 'Select month and year'; case 'exact': default: return placeholder; } }; const getPrecisionHelpText = () => { switch (localPrecision) { case 'exact': return 'Use when you know the specific day (e.g., June 15, 2010)'; case 'month': return 'Use when you only know the month (e.g., June 2010)'; case 'year': return 'Use when you only know the year (e.g., 2010)'; case 'decade': return 'Use for events in a general decade (e.g., 1980s). Enter any year from that decade.'; case 'century': return 'Use for very old dates spanning a century (e.g., 19th century). Enter any year from that century.'; case 'approximate': return 'Use when the date is uncertain or estimated (e.g., circa 2010)'; default: return ''; } }; return (
{label && }
{(localPrecision === 'exact') && ( onChange(date, 'exact')} placeholder={getPlaceholderText()} disabled={disabled} disableFuture={disableFuture} disablePast={disablePast} fromYear={fromYear} toYear={toYear} /> )} {localPrecision === 'month' && ( onChange(date, 'month')} placeholder={getPlaceholderText()} disabled={disabled} fromYear={fromYear} toYear={toYear} /> )} {(localPrecision === 'year' || localPrecision === 'decade' || localPrecision === 'century' || localPrecision === 'approximate') && ( )}

{getPrecisionHelpText()}

); }