"use client"; import * as React from "react"; import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover"; import { Button } from "@/components/ui/button"; import { Calendar } from "@/components/ui/calendar"; import { CalendarIcon } from "lucide-react"; import { cn } from "@/lib/utils"; import { Input } from "@/components/ui/input"; interface Props { value?: string; // "dd/mm/yyyy hh:mm:ss" onChange?: (value: string) => void; className?: string; } export function DateTimePicker({ value, onChange, className }: Props) { // --- store each part as string --- const [local, setLocal] = React.useState({ d: "", m: "", y: "", h: "", min: "", s: "", }); const [open, setOpen] = React.useState(false); // --- parse incoming value string --- React.useEffect(() => { if (!value) return; const dt = dateGetter(value); if (!dt) return; setLocal({ d: String(dt.getDate()), m: String(dt.getMonth() + 1), y: String(dt.getFullYear()), h: String(dt.getHours()), min: String(dt.getMinutes()), s: String(dt.getSeconds()), }); }, [value]); // --- emit combined string --- const emitChange = React.useCallback(() => { const dt = new Date( Number(local.y) || 0, Number(local.m) - 1 || 0, Number(local.d) || 1, Number(local.h) || 0, Number(local.min) || 0, Number(local.s) || 0 ); if (!isNaN(dt.getTime())) { onChange?.(dateSetter(dt)); } }, [local, onChange]); const handleInput = (field: keyof typeof local, val: string) => { if (/^\d*$/.test(val)) { setLocal((prev) => ({ ...prev, [field]: val })); } }; const handleBlur = (field: keyof typeof local) => { // Validate ranges let val = Number(local[field]); if (isNaN(val)) val = 0; switch (field) { case "d": val = Math.max(1, Math.min(31, val)); break; case "m": val = Math.max(1, Math.min(12, val)); break; case "y": val = Math.max(1900, Math.min(2100, val)); break; case "h": val = Math.max(0, Math.min(23, val)); break; case "min": case "s": val = Math.max(0, Math.min(59, val)); break; } setLocal((prev) => ({ ...prev, [field]: String(val) })); emitChange(); }; const currentDate = dateGetter(dateSetter(new Date( Number(local.y) || 0, Number(local.m) - 1 || 0, Number(local.d) || 1, Number(local.h) || 0, Number(local.min) || 0, Number(local.s) || 0 ))); const handleCalendarSelect = (d: Date | undefined) => { if (!d) return; const updated = new Date( d.getFullYear(), d.getMonth(), d.getDate(), Number(local.h) || 0, Number(local.min) || 0, Number(local.s) || 0 ); const str = dateSetter(updated); setLocal({ d: String(updated.getDate()), m: String(updated.getMonth() + 1), y: String(updated.getFullYear()), h: String(updated.getHours()), min: String(updated.getMinutes()), s: String(updated.getSeconds()), }); onChange?.(str); }; return ( {/* Calendar */} {/* Inputs */}
handleInput("d", e.target.value)} onBlur={() => handleBlur("d")} /> handleInput("m", e.target.value)} onBlur={() => handleBlur("m")} /> handleInput("y", e.target.value)} onBlur={() => handleBlur("y")} />
handleInput("h", e.target.value)} onBlur={() => handleBlur("h")} /> handleInput("min", e.target.value)} onBlur={() => handleBlur("min")} /> handleInput("s", e.target.value)} onBlur={() => handleBlur("s")} />
); } // --- helpers for external usage --- export const dateGetter = (str: string | undefined): Date | null => { if (!str) return null; const [d, m, yAndTime] = str.split("/"); if (!d || !m || !yAndTime) return null; const [y, time] = yAndTime.split(" "); if (!time) return null; const [h, min, s] = time.split(":").map(Number); return new Date(Number(y), Number(m) - 1, Number(d), h || 0, min || 0, s || 0); }; export const dateSetter = (date: Date | null): string => { if (!date) return ""; const fmt = (n: number) => String(n).padStart(2, "0"); return `${fmt(date.getDate())}/${fmt(date.getMonth() + 1)}/${date.getFullYear()} ${fmt( date.getHours() )}:${fmt(date.getMinutes())}:${fmt(date.getSeconds())}`; };