import { InputControl } from '../ui/input-control'; import { Flex, FlexItem, FlexBlock } from '../ui/flex'; import { Text } from '../ui/text'; import { PlusCircle, XCircle } from 'lucide-react'; import { Controller, useFormContext, useWatch } from 'react-hook-form'; interface FhirPeriodProps { label: string; context: string; type?: 'date' | 'dateTime'; disabled?: boolean; readonly?: boolean; alwaysExpanded?: boolean; } const FhirPeriod = ({ label, context, type = 'date', disabled = false, readonly = false, alwaysExpanded = false }: FhirPeriodProps) => { const { control, setValue, getValues } = useFormContext(); const periodValue = useWatch({ control, name: context, defaultValue: undefined, }); const startValue = useWatch({ control, name: `${context}.start`, defaultValue: '', }); const hasPeriod = !!periodValue; const hasStart = periodValue?.start !== undefined; const hasEnd = periodValue?.end !== undefined; const getCurrentValue = () => { const now = new Date(); if (type === 'dateTime') { return now.toISOString(); } return now.toISOString().split('T')[0]; }; const formatValueForInput = (value: string) => { if (!value) return ''; if (type === 'dateTime') { const d = new Date(value); const year = d.getFullYear(); const month = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); const hours = String(d.getHours()).padStart(2, '0'); const minutes = String(d.getMinutes()).padStart(2, '0'); return `${year}-${month}-${day}T${hours}:${minutes}`; } return new Date(value).toISOString().split('T')[0]; }; const formatValueForStorage = (value: string | undefined) => { if (!value) return ''; if (type === 'dateTime') { return new Date(value).toISOString(); } return new Date(value).toISOString().split('T')[0]; }; const startMin = startValue ? formatValueForInput(startValue) : undefined; const addPeriod = () => { const currentValue = getCurrentValue(); setValue(context, { start: currentValue }, { shouldDirty: true, shouldTouch: true, shouldValidate: true }); }; const addEndPeriod = () => { if (hasPeriod && !hasEnd) { const currentValue = getCurrentValue(); const existingValue = getValues(context) || {}; setValue(context, { ...existingValue, end: currentValue }, { shouldDirty: true, shouldTouch: true, shouldValidate: true }); } }; const removePeriod = () => { setValue(context, undefined, { shouldDirty: true, shouldTouch: true, shouldValidate: true }); }; const inputType = type === 'dateTime' ? 'datetime-local' : 'date'; const startLabel = type === 'dateTime' ? 'Start Date & Time' : 'Start'; const endLabel = type === 'dateTime' ? 'End Date & Time' : 'End'; if (alwaysExpanded) { return ( ( field.onChange(formatValueForStorage(value))}/> )}/> ( field.onChange(formatValueForStorage(value))}/> )}/> ); } return ( <>
{label} {!hasPeriod && !disabled && !readonly && ( )} {/* THE TWIN OF THE ADD ABOVE, AND IT CARRIES THE SAME COLOUR FOR THAT REASON. This was the destructive red, and `removePeriod` destroys nothing: it sets the form field to `undefined` in a form that has not been saved, which is the exact inverse of the `Add period` sitting in the same slot a moment earlier. Nothing leaves the server, and pressing it again restores the pair of date inputs. The colour is the purpose, so red on a control that clears a field teaches a reader that red means nothing in particular - and this component renders on dozens of clinical forms, so it taught it dozens of times. The one red that belongs on this modal is the block's own trash, which deletes a saved schedule period and confirms before it does. */} {hasPeriod && !disabled && !readonly && ( )}
{hasPeriod && ( {hasStart && ( ( { const formattedValue = formatValueForStorage(value); field.onChange(formattedValue); }}/> )}/> )} {hasPeriod && !hasEnd ? ( {/* The inline style below is LAYOUT (full width, top offset to align with the adjacent field), not a colour override, so it is preserved — unlike the hand-rolled colour styles removed elsewhere in this sweep. NOTE: this comment sits ABOVE the `&&` guard on purpose. A JSX comment cannot be the first thing inside `{cond && (` — that slot takes an expression, so it parses as an object literal and breaks the file. */} {!readonly && ( )} ) : ( ( { const formattedValue = formatValueForStorage(value); field.onChange(formattedValue); }}/> )}/> )} )} ); }; export default FhirPeriod;