"use client"; import { Calendar as CalendarIcon, CircleXIcon } from "lucide-react"; import { useMemo, useState } from "react"; import { useI18nDateFnsLocale, useI18nLocale, useI18nTranslations } from "../../i18n"; import { Button, Calendar, Label, Popover, PopoverContent, PopoverTrigger, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../../shadcnui"; import { cn } from "../../utils"; import { FormFieldWrapper } from "./FormFieldWrapper"; export function FormDateTime({ form, id, name, minDate, onChange, allowEmpty, defaultMonth, placeholder, isRequired, description, }: { form: any; id: string; name?: string; placeholder?: string; minDate?: Date; onChange?: (date?: Date) => Promise; allowEmpty?: boolean; defaultMonth?: Date; isRequired?: boolean; description?: string; }) { const [open, setOpen] = useState(false); const t = useI18nTranslations(); const locale = useI18nLocale(); const dateFnsLocale = useI18nDateFnsLocale(); const [displayMonth, setDisplayMonth] = useState(() => { const currentValue = form.getValues(id); return currentValue || defaultMonth || new Date(); }); // Locale-aware date-time formatter const dateTimeFormatter = useMemo( () => new Intl.DateTimeFormat(locale, { year: "numeric", month: "long", day: "numeric", hour: "2-digit", minute: "2-digit", hour12: false, }), [locale], ); // Format date-time for display const formatDateTime = (date: Date): string => dateTimeFormatter.format(date); const [selectedHours, setSelectedHours] = useState(new Date().getHours()); const [selectedMinutes, setSelectedMinutes] = useState(roundToNearestFiveMinutes(new Date().getMinutes())); const hoursOptions = Array.from({ length: 24 }, (_, i) => { const hour = i; return { value: hour, label: hour.toString().padStart(2, "0"), }; }); const minutesOptions = Array.from({ length: 12 }, (_, i) => { const minute = i * 5; return { value: minute, label: minute.toString().padStart(2, "0"), }; }); const hoursItems = Object.fromEntries(hoursOptions.map((option) => [String(option.value), option.label])); const minutesItems = Object.fromEntries(minutesOptions.map((option) => [String(option.value), option.label])); function roundToNearestFiveMinutes(minutes: number): number { return (Math.round(minutes / 5) * 5) % 60; } const handleTimeChange = (hours: number, minutes: number) => { const currentDate = form.getValues(id); if (currentDate) { const updatedDate = new Date(currentDate); updatedDate.setHours(hours); updatedDate.setMinutes(minutes); form.setValue(id, updatedDate); if (onChange) onChange(updatedDate); } }; return (
{(field) => (
} > {field.value ? ( formatDateTime(field.value) ) : ( {placeholder ? placeholder : t(`common.pick_date_time`)} )} {field.value && allowEmpty !== false && ( { if (onChange) onChange(undefined); form.setValue(id, ""); }} /> )}
{ if (date) { // Preserve the current time when selecting a new date const newDate = new Date(date); if (field.value) { const currentDate = new Date(field.value); newDate.setHours(currentDate.getHours(), currentDate.getMinutes()); } else { newDate.setHours(selectedHours, selectedMinutes); } form.setValue(id, newDate); setDisplayMonth(newDate); if (onChange) onChange(newDate); // Update time state values setSelectedHours(newDate.getHours()); setSelectedMinutes(roundToNearestFiveMinutes(newDate.getMinutes())); } }} disabled={(date) => (minDate && date < minDate ? true : false)} locale={dateFnsLocale} weekStartsOn={1} startMonth={new Date(1900, 0)} endMonth={new Date(new Date().getFullYear() + 10, 11)} />
:
{allowEmpty !== false && ( )}
)}
); }