// DynamicDateField — modern date picker for declarative forms: a shadcn // Calendar inside a Popover, instead of the native `` whose // browser calendar looks dated and gets clipped inside a modal. // // Contract: the field VALUE stays an ISO date string ("YYYY-MM-DD") so the form // payload is unchanged (the kernel/handlers already expect that). We parse the // string to a Date for the Calendar and format back to ISO on select. The // Popover portals to the document body, so the calendar is never clipped by the // modal's overflow — fixing the "se corta" cut-off. // // Deliberately NOT reusing metacore-ui's `DatePicker` shared helper: that one // hard-disables future dates and pins a fixed 240px width, neither of which is // right for a generic declarative field (a journal entry can be post-dated). import { useState } from 'react' import { Button, Calendar, Popover, PopoverContent, PopoverTrigger, } from '@asteby/metacore-ui/primitives' import { CalendarIcon } from 'lucide-react' import type { ActionFieldDef } from './types' export interface DynamicDateFieldProps { field: ActionFieldDef value: any onChange: (v: any) => void } // Parse "YYYY-MM-DD" (or any Date-parseable string) into a local Date, or // undefined when empty/invalid. Uses noon to dodge timezone day-shift. function toDate(v: any): Date | undefined { if (!v) return undefined const s = String(v) const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(s) if (m) return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), 12) const d = new Date(s) return isNaN(d.getTime()) ? undefined : d } // Format a Date back to "YYYY-MM-DD" (local, no timezone shift). function toISO(d: Date): string { const y = d.getFullYear() const m = String(d.getMonth() + 1).padStart(2, '0') const day = String(d.getDate()).padStart(2, '0') return `${y}-${m}-${day}` } // Human label for the trigger. Locale-aware, falls back to the raw ISO. function label(d: Date | undefined): string { if (!d) return '' try { return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }) } catch { return toISO(d) } } export function DynamicDateField({ field, value, onChange }: DynamicDateFieldProps) { const [open, setOpen] = useState(false) const selected = toDate(value) return ( { onChange(d ? toISO(d) : '') setOpen(false) }} /> ) } export default DynamicDateField