diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx index 900a69e4..bff47f58 100644 --- a/src/components/ui/calendar.tsx +++ b/src/components/ui/calendar.tsx @@ -11,6 +11,9 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C return ( ({ isValid: true, message: "" }); + + const parseDate = (input: string): Date | null => { + if (!input) return null; + + // Try ISO format: 2005-06-15 + let parsed = parse(input, "yyyy-MM-dd", new Date()); + if (isValid(parsed)) return parsed; + + // Try US format: 06/15/2005 + parsed = parse(input, "MM/dd/yyyy", new Date()); + if (isValid(parsed)) return parsed; + + // Try European format: 15/06/2005 + parsed = parse(input, "dd/MM/yyyy", new Date()); + if (isValid(parsed)) return parsed; + + // Try short format: 6/15/05 + parsed = parse(input, "M/d/yy", new Date()); + if (isValid(parsed)) return parsed; + + // Try short year format: 2005-6-15 + parsed = parse(input, "yyyy-M-d", new Date()); + if (isValid(parsed)) return parsed; + + return null; + }; + + const handleSelect = (date: Date | undefined) => { + if (date) { + setParseStatus({ + isValid: true, + message: `✓ ${format(date, "PPP")}`, + parsed: date + }); + } else { + setParseStatus({ isValid: true, message: "" }); + } + onSelect?.(date); + }; + + return ( +
+ + {showFeedback && parseStatus.message && ( +

+ {parseStatus.message} +

+ )} +
+ ); +} diff --git a/src/components/ui/date-picker.tsx b/src/components/ui/date-picker.tsx index f2a267c5..3354dc33 100644 --- a/src/components/ui/date-picker.tsx +++ b/src/components/ui/date-picker.tsx @@ -1,9 +1,10 @@ import * as React from "react"; -import { format } from "date-fns"; +import { format, parse, isValid } from "date-fns"; import { CalendarIcon } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; import { Calendar } from "@/components/ui/calendar"; import { Popover, @@ -21,6 +22,8 @@ export interface DatePickerProps { disablePast?: boolean; fromYear?: number; toYear?: number; + allowTextEntry?: boolean; + dateFormat?: string; } export function DatePicker({ @@ -33,12 +36,65 @@ export function DatePicker({ disablePast = false, fromYear, toYear, + allowTextEntry = false, + dateFormat = "yyyy-MM-dd", }: DatePickerProps) { const [open, setOpen] = React.useState(false); + const [textInput, setTextInput] = React.useState(""); + const [isTyping, setIsTyping] = React.useState(false); + + const parseDate = (input: string): Date | null => { + if (!input) return null; + + // Try ISO format: 2005-06-15 + let parsed = parse(input, "yyyy-MM-dd", new Date()); + if (isValid(parsed)) return parsed; + + // Try US format: 06/15/2005 + parsed = parse(input, "MM/dd/yyyy", new Date()); + if (isValid(parsed)) return parsed; + + // Try European format: 15/06/2005 + parsed = parse(input, "dd/MM/yyyy", new Date()); + if (isValid(parsed)) return parsed; + + // Try short format: 6/15/05 + parsed = parse(input, "M/d/yy", new Date()); + if (isValid(parsed)) return parsed; + + // Try short year format: 2005-6-15 + parsed = parse(input, "yyyy-M-d", new Date()); + if (isValid(parsed)) return parsed; + + return null; + }; + + const handleTextChange = (e: React.ChangeEvent) => { + const value = e.target.value; + setTextInput(value); + setIsTyping(true); + + const parsed = parseDate(value); + if (parsed) { + onSelect?.(parsed); + } + }; + + const handleBlur = () => { + setIsTyping(false); + if (date) { + setTextInput(format(date, dateFormat)); + } else { + setTextInput(""); + } + }; const handleSelect = (selectedDate: Date | undefined) => { onSelect?.(selectedDate); setOpen(false); + if (selectedDate) { + setTextInput(format(selectedDate, dateFormat)); + } }; const getDisabledDates = (date: Date) => { @@ -48,6 +104,48 @@ export function DatePicker({ return false; }; + if (allowTextEntry) { + return ( +
+ setIsTyping(true)} + onBlur={handleBlur} + placeholder={placeholder} + disabled={disabled} + className={cn("flex-1", className)} + /> + + + + + + + + +
+ ); + } + return ( @@ -59,6 +157,7 @@ export function DatePicker({ className )} disabled={disabled} + type="button" > diff --git a/src/components/ui/flexible-date-input.tsx b/src/components/ui/flexible-date-input.tsx index fd1c8e4d..7d82b1f7 100644 --- a/src/components/ui/flexible-date-input.tsx +++ b/src/components/ui/flexible-date-input.tsx @@ -128,6 +128,7 @@ export function FlexibleDateInput({ disablePast={disablePast} fromYear={fromYear} toYear={toYear} + allowTextEntry={true} /> )}