Skip to content

feat(ui): SearchParameter viewer (#238) + Compartment viewer & route tester (#237) — read halves#252

Open
angela-helios wants to merge 5 commits into
mainfrom
feat/237-238-registry-compartment-ui
Open

feat(ui): SearchParameter viewer (#238) + Compartment viewer & route tester (#237) — read halves#252
angela-helios wants to merge 5 commits into
mainfrom
feat/237-238-registry-compartment-ui

Conversation

@angela-helios

Copy link
Copy Markdown
Contributor

Implements the read halves of #238 (SearchParameter viewer) and #237 (Compartment viewer + route tester) in helios-ui, following the interactive mockups from #247, the standalone hi-fi console design, and Brett's Figma file (CcLtq79cH2aHv4Ii9aNQTP). Stacked on #241 (feat/234-saved-queries); retarget/rebase once that merges.

Scope: the slice both issues said to ship first

GET /ui/search-parameters (#238)

  • Same snapshot the server searches with. The catalog seeds exactly like the storage backends: load_embedded() fallback first, then load_from_spec_file() from HFS_DATA_DIR (default ./data), first canonical URL wins — mirroring SearchParameterRegistry::register. A missing spec bundle renders a visible warning instead of silently under-reporting. Lazily loaded per FHIR version, cached for the process lifetime.
  • Resource Filter rail — search box, All types, Recently used, live counts per base type. The rail is a direct view of the registry's base → code → param outer map. Recents are a progressive enhancement (resource-filter.js, localStorage); everything else is links + a GET form, so the page works with JavaScript disabled.
  • Type and source facet rows scoped to the current base type, with live counts.
  • Pagination (50/page) rather than a render cap, per the acceptance criteria.
  • Detail panel for the selected row: canonical URL, type, bases, FHIRPath expression, targets, composite components, and the runtime status with the draft→active promotion hint (issue open question 1 — showing runtime status, with the promotion explained).
  • Registry-aware chips and notes, using the fix(search): resolve (base, code) shadowing by source precedence #242 semantics: a cross-source (base, code) shadow renders as informational overrides spec / shadowed; a same-source collision is the blocking conflict (DuplicateCode). Warnings for empty expressions (extractor indexes zero rows) and reference params without targets; informational note for choice-type expressions (rewrite_choice_types()).
  • Version pill re-keys the screen across whichever of R4/R4B/R5/R6 the build enables (helios-ui grew version features, forwarded from hfs via helios-ui?/…).

GET /ui/compartments (#237)

  • Rail of definitions with members / total counts (Patient 66/145 etc.), Definition tab (canonical URL, status, search/experimental, publisher, description — read-only, stated as such), Members tab with member/excluded chips, per-type linking params, and the Members · All types · Excluded filter.
  • Tester: pick id + target type (or *). Resolves through helios_fhir::compartment_params()the same codegen'd table the REST compartment handler consults — so the answer here is the answer the API gives:
    • member → the equivalent flat search (subject=Patient/example OR performer=Patient/example),
    • non-member → the 404 + OperationOutcome preview,
    • * → fan-out across member types, with the skipped-not-failed note matching the handler's filter_map,
    • {def} (e.g. Encounter in its own compartment) → explained as the compartment resource itself.
  • Definition metadata comes from the spec CompartmentDefinition JSONs vendored under crates/ui/data/compartments/ (the same files fhir-gen compiles; the crate can't include_str! across crate boundaries without breaking cargo package). A parity test asserts every (compartment, type) slot in the vendored copies matches the codegen table, for every enabled version — the vendored data cannot drift silently. The questionnaire.json oddity (no code, zero resources) is not vendored, per issue open question 4.

Design

Both pages implement the #247 mockups on the existing design system: app.css tokens, Figtree, the card/radius/shadow scale from the Figma Dashboard frames, and the Search screen's table styling. New semantic tokens --ok / --warn / --danger (+ soft variants) are held off the accent hue, defined for both themes. The pages reuse the crate's htmx-first conventions: server-rendered pages at real URLs, hx-boost for swaps, an hx-select live rail filter — no client-side state beyond the recents enhancement.

The sidebar's Compartments entry goes live, and a Search Parameters entry joins the Server section; strings added to en/es/de.

Tests

  • Parity: vendored compartment JSONs == get_compartment_params() output, all versions.
  • Tester membership/404/fan-out/{def} cases against real R4 data (Patient 66 members, subject OR performer, etc.).
  • SearchParameter snapshot: full 1,375-param bundle loads, base filter narrows with facet sums matching, pagination is stable and complete, missing spec dir degrades to the fallback with the warning flag set.
  • Chip semantics: cross-source mates → overrides/shadowed, same-source → conflict.
  • Router: both pages end-to-end through mount(), including the tester round-trip and detail-panel selection.

Notes / follow-ups

  • mount() now takes the data dir: helios_ui::mount(app, version, config.data_dir.clone()).
  • Custom parameter files (load_custom_from_directory, Config source) aren't in the snapshot yet — worth folding in together with Store SearchParameters in storage; make the in-memory registry a TTL cache over it #235's storage-backed registry, which is also when the editor's create/override/delete lands.
  • Compartment definitions ship version-scoped including R6's Group compartment.

Closes nothing yet — read halves only; #238 and #237 stay open to track the write paths.

Implements the read halves of #238 and #237 in helios-ui, following the
docs/237-238-mockups designs on the existing design system.

/ui/search-parameters (#238): read-only viewer over the same snapshot
the storage backends seed their registries from (embedded fallback +
the spec bundle in HFS_DATA_DIR, first canonical URL wins). Resource
Filter rail with live counts and a Recently-used group (localStorage
enhancement; everything else is links + GET forms, so the page works
without JavaScript), type/source facet rows scoped to the current base,
pagination instead of a render cap, and a detail panel with (base,
code) slot chips per the #242 precedence semantics - cross-source
shadows are informational overrides, same-source collisions are the
blocking DuplicateCode conflict - plus empty-expression / missing-
target warnings and the choice-type rewrite note. A missing spec
bundle renders a visible warning instead of under-reporting silently.
Write paths stay off pending #235, and the page says so.

/ui/compartments (#237): definition rail with member counts, read-only
Definition tab, Members tab with member/excluded chips and per-type
linking params, and the tester. Everything membership-shaped resolves
through helios_fhir::compartment_params() - the codegen'd table the
REST compartment handler consults - so the tester's member / 404 +
OperationOutcome / *-fan-out / {def} answers are the answers the API
gives. Definition metadata comes from the spec CompartmentDefinition
JSONs vendored under crates/ui/data/ (include_str! across crates would
break cargo package); a parity test asserts every (compartment, type)
slot matches the codegen table for every enabled version, so the
vendored copies cannot drift silently. The questionnaire.json oddity
(no code, zero resources) is not vendored, per issue open question 4.

helios-ui grows R4/R4B/R5/R6 features forwarded from hfs (helios-ui?/…)
so the version pill covers exactly the versions the server is built
with; mount() takes the data dir. Semantic --ok/--warn/--danger tokens
join app.css for both themes, held off the accent hue. Sidebar's
Compartments entry goes live and Search Parameters joins the Server
section; strings in en/es/de.

Verified end to end against a running hfs: both pages serve the real
R4 registry (1377 definitions; Patient 66/145 members), the tester
round-trips member, 404, and fan-out cases, and the ES locale renders.
…es) into feat/237-238-registry-compartment-ui

# Conflicts:
#	crates/ui/assets/app.css
#	locales/de/main.ftl
#	locales/en/main.ftl
#	locales/es/main.ftl
…results

Grows the queries page's Search Builder to the rest of the hi-fi console
design, on top of the featured GET URL:

- Visual rows in two-way sync with the URL: Conditions (parameter /
  modifier / value, add and remove), Includes (_include/_revinclude with
  :iterate), and Result controls (_count/_sort/_total/_summary/_elements).
  Colon modifiers rejoin onto the key, comparator prefixes (ge, le, ...)
  onto the value, matching how FHIR search actually spells them. Editing
  the URL re-derives the rows; editing a row rewrites the URL.
- Parameter suggestions come from the server's SearchParameter registry:
  GET /ui/queries/params?type=X returns a datalist fragment (options
  labeled with the parameter type) rendered from the same snapshot the
  SearchParameter viewer reads, swapped per resource type as the URL's
  type changes. Server-rendered hypermedia, not a UI-facing JSON API.
- Run now executes in-page against the FHIR REST API itself and renders
  the Bundle: total (with an included-resources count when _include is in
  play), a table whose columns honor _elements with compact formatting
  for common shapes (HumanName, CodeableConcept, Reference, Quantity),
  row links to the resource, OperationOutcome diagnostics on errors, and
  paging over Bundle.link. Explicit runs still record to recentSearches;
  paging does not. An Open-in-new-tab link keeps the old behavior a click
  away, and /ui/queries?url=... deep-links straight into a loaded builder
  and executed search.
- Global [hidden] { display: none !important } so script-toggled regions
  (pager buttons, builder sections, the rail's recents group) cannot be
  un-hidden by their class display values.

Verified against a running hfs with seeded Patients: the deep link
renders condition and control rows from the URL, the datalist swaps to
the Patient catalog (birthdate et al.), and the search renders '2
results' with name/gender/birthDate columns and formatted HumanNames.
@angela-helios

Copy link
Copy Markdown
Contributor Author

90b44da7 grows the queries page's builder to the rest of the hi-fi Search screen — it lives on this branch (not #241) because the parameter catalog is fed by #238's registry snapshot:

  • Visual rows ⇄ URL: Conditions (param / modifier / value), Includes, and Result controls stay in two-way sync with the featured GET URL. Colon modifiers rejoin the key, comparator prefixes (ge, le, …) rejoin the value.
  • Registry-fed suggestions: GET /ui/queries/params?type=X returns a server-rendered <datalist> fragment (options labeled with the param type) from the same snapshot as /ui/search-parameters, swapped as the URL's type changes. Hypermedia, not a UI JSON API.
  • In-page results: Run fetches the search from the FHIR API itself and renders Bundle.total, a table honoring _elements (compact HumanName/CodeableConcept/Reference/Quantity formatting), OperationOutcome diagnostics on errors, and paging over Bundle.link. Explicit runs record to recentSearches; paging doesn't. ?url= deep-links into a loaded builder + executed search.

Verified against a running hfs with seeded Patients (screenshots below reflect /ui/queries?url=/Patient?name=smith&_elements=name,gender,birthDate). The hi-fi's AI search panel is the remaining piece of that screen — needs an NL→FHIR-URL backend, so left as a follow-up.

Adds the hi-fi Search screen's remaining FHIR-applicable piece: a left
rail listing every resource type of the version, sourced from the spec
CompartmentDefinitions already vendored for the compartment viewer (each
enumerates the full set - 145 in R4), so no new dependency. Picking a
type re-keys the builder to GET /{Type} and runs the browse; a filter
box narrows the list client-side; the active type tracks the URL.

Counts hydrate client-side through the standard count-only search,
_summary=count, a few requests at a time so 145 types don't stampede
the server. _total=accurate rides along because this server only
computes Bundle.total when asked explicitly, even under _summary=count
- that gap is worth fixing server-side, at which point the extra
parameter is harmless.

The page goes content-wide with the rail | builder+results layout of
the design. The hi-fi's AI search panel is deliberately not included:
natural-language-to-URL translation is not a FHIR capability and needs
a backend HFS does not have; it stays a follow-up.

Verified against a running hfs with seeded data: counts render per type
(including real zeros), clicking a type browses it in-page, and the
deep-linked builder + results still work.
…-compartment-ui

# Conflicts:
#	Cargo.lock
#	crates/hfs/src/main.rs
#	crates/ui/src/lib.rs
#	crates/ui/templates/layouts/base.html
#	crates/ui/tests/router_http.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant