Summary
The Guided form gives no help entering FHIR temporal primitives — date, dateTime, time, instant. Every one is a bare text box, so the user has to know the spec's format rules from memory and only finds out they got it wrong after a server round trip. Add authoring assistance for these types.
Spec reference: https://hl7.org/fhir/datatypes.html#primitive
Current behavior
crates/ui/templates/partials/editor-body.html:133-138 renders every primitive identically:
<input class="editor-row__input" type="text" value="{{ row.value }}" data-set="{{ row.path }}" …>
No format hint, no placeholder, no picker, no client-side check. A malformed birthDate is caught only on blur, when the value round-trips to /ui/editor/render and comes back as a red row error.
Two things already in place that this can build on
1. The row already knows its FHIR type. Row.type_label is populated from the schema's type_ (crates/ui/src/editor.rs:446-449), so at render time the server knows a field is dateTime rather than just "a string". The template currently only prints it as a hint chip (editor-body.html:98-99). No new plumbing is needed to branch on type.
2. The authoritative format regex is already loaded. The validator carries the FHIR spec pattern per primitive on schema.regex and matches against it (crates/fhir-validator/src/engine/primitives.rs:41-49), compiled anchored as ^(?:…)$. That same pattern can drive a live client-side check and a generated format hint, instead of hand-copying regexes into the UI.
The design constraint — don't just drop in native pickers
Native HTML date inputs cannot represent what FHIR permits, and would silently destroy valid data:
| Type |
FHIR permits |
Why the native control doesn't fit |
date |
YYYY, YYYY-MM, or YYYY-MM-DD — partial precision is legal |
<input type="date"> can only express a full day |
dateTime |
the three date forms, or a full timestamp; if a time is present a timezone offset is required |
<input type="datetime-local"> has no timezone, and can't do partials |
instant |
always full precision with an offset (YYYY-MM-DDThh:mm:ss.sss+zz:zz) |
no native control produces this |
time |
hh:mm:ss[.sss], no date, no timezone |
<input type="time"> commonly omits the seconds FHIR requires |
So the input must stay free-text and authoritative, with assistance layered on top. There's a precedent in this same editor: the live $expand picker enriches a plain text input with a datalist via data-vs-url (editor-body.html:136, resources.js:126-172) without taking over the field. The same shape fits here.
Suggested direction
- Per-type placeholder and format hint driven off
type_label, stating what's allowed including partial precision.
- Live format feedback using the schema regex already available server-side — flag a malformed value as you type rather than after a blur round trip.
- An optional assist affordance that writes a conformant string: a picker, plus shortcuts like "today" / "now". Precision must be an explicit choice for
date/dateTime, never silently upgraded from 2024 to 2024-01-01.
- Timezone handling for
dateTime and instant — default to the browser offset, allow Z or an explicit offset, and never emit a time without one.
- Keep the typed value the source of truth so an existing partial date survives a visit to the field untouched.
Scope
In scope: date, dateTime, time, instant.
Out of scope but worth noting: the complex types built from these — Period (start/end are dateTime), Timing (event, repeat.bounds[x]), Age/Duration — pick up the benefit for free once the primitive rows improve, since their children render as ordinary rows. Also, the same "no assistance" gap applies to boolean, integer, unsignedInt, and positiveInt, which are likewise plain text boxes; if the mechanism generalizes cheaply, say so, but the ask here is the temporal types.
Acceptance criteria
Pointers
Summary
The Guided form gives no help entering FHIR temporal primitives —
date,dateTime,time,instant. Every one is a bare text box, so the user has to know the spec's format rules from memory and only finds out they got it wrong after a server round trip. Add authoring assistance for these types.Spec reference: https://hl7.org/fhir/datatypes.html#primitive
Current behavior
crates/ui/templates/partials/editor-body.html:133-138renders every primitive identically:No format hint, no placeholder, no picker, no client-side check. A malformed
birthDateis caught only on blur, when the value round-trips to/ui/editor/renderand comes back as a red row error.Two things already in place that this can build on
1. The row already knows its FHIR type.
Row.type_labelis populated from the schema'stype_(crates/ui/src/editor.rs:446-449), so at render time the server knows a field isdateTimerather than just "a string". The template currently only prints it as a hint chip (editor-body.html:98-99). No new plumbing is needed to branch on type.2. The authoritative format regex is already loaded. The validator carries the FHIR spec pattern per primitive on
schema.regexand matches against it (crates/fhir-validator/src/engine/primitives.rs:41-49), compiled anchored as^(?:…)$. That same pattern can drive a live client-side check and a generated format hint, instead of hand-copying regexes into the UI.The design constraint — don't just drop in native pickers
Native HTML date inputs cannot represent what FHIR permits, and would silently destroy valid data:
dateYYYY,YYYY-MM, orYYYY-MM-DD— partial precision is legal<input type="date">can only express a full daydateTimedateforms, or a full timestamp; if a time is present a timezone offset is required<input type="datetime-local">has no timezone, and can't do partialsinstantYYYY-MM-DDThh:mm:ss.sss+zz:zz)timehh:mm:ss[.sss], no date, no timezone<input type="time">commonly omits the seconds FHIR requiresSo the input must stay free-text and authoritative, with assistance layered on top. There's a precedent in this same editor: the live
$expandpicker enriches a plain text input with a datalist viadata-vs-url(editor-body.html:136,resources.js:126-172) without taking over the field. The same shape fits here.Suggested direction
type_label, stating what's allowed including partial precision.date/dateTime, never silently upgraded from2024to2024-01-01.dateTimeandinstant— default to the browser offset, allowZor an explicit offset, and never emit a time without one.Scope
In scope:
date,dateTime,time,instant.Out of scope but worth noting: the complex types built from these —
Period(start/endaredateTime),Timing(event,repeat.bounds[x]),Age/Duration— pick up the benefit for free once the primitive rows improve, since their children render as ordinary rows. Also, the same "no assistance" gap applies toboolean,integer,unsignedInt, andpositiveInt, which are likewise plain text boxes; if the mechanism generalizes cheaply, say so, but the ask here is the temporal types.Acceptance criteria
date,dateTime,time, andinstantrows show what format is expected before the user types.dateanddateTimevalues can be entered and are preserved unchanged.dateTime/instantwith a time always carries a timezone offset.timevalues keep the required seconds component./ui/editorand in the Resources edit modal (two separate JS implementations — see UI editor: Guided form is not usable for authoring a new resource (empty tree, picker and focus lost on every edit) #547).a11y.spec.tsstill passes.locales/{en,es,de}/main.ftl).Pointers
crates/ui/templates/partials/editor-body.html:133-138— the single untyped inputcrates/ui/src/editor.rs:442-455—Rowconstruction,type_labelfromschema.type_crates/fhir-validator/src/engine/primitives.rs:1-50— the spec regex per primitivecrates/ui/assets/resources.js:126-172,crates/ui/assets/editor.js:318-360— the$expanddatalist pattern to model this on