feat(ui): History & Versions diff — the prototype (#236)#278
Open
angela-helios wants to merge 2 commits into
Open
feat(ui): History & Versions diff — the prototype (#236)#278angela-helios wants to merge 2 commits into
angela-helios wants to merge 2 commits into
Conversation
Steve asked on PR #246 to "implement a prototype and we can evaluate from there." This is it, built to the decision recorded in docs/history-diff-rendering.md. The diff between two versions is rendered server-side, in two layers, into Askama templates — no client-side diff library: - Semantic layer (json-patch): the field-level change list, the exact inverse of the RFC 6902 PATCH the server already applies. `replace /name/0/family Smith → Smythe`. json-patch was already in the workspace lock, so it adds no new transitive weight. - Textual layer (similar, +inline): the pretty-printed JSON of both versions, paired lines with +/- gutters and per-side line numbers, with word-level highlighting of the run that changed within a line (`"Jon"` → `"John"` marks only the inserted letter). similar has no HTML formatter, so its changes are iterated into template rows. meta.versionId / meta.lastUpdated move on every version, so they are filtered from the semantic view by default, behind a "show metadata" toggle that says how many ops it hid. Adjacent versions (n vs n-1) are the default comparison; two selects, htmx-swapped. A deleted version renders as a banner, not a diff against a tombstone. Identical versions say so. The screen follows Brett's "History & Versions" frame: the Instance / Type feed / System feed tabs, the version rail (newest first, each tagged with the interaction that produced it — create / update / patch), and the diff panel. Versions are read through the ordinary _history and vread FHIR API; the browser fetches them and posts them to /ui/history/diff, so the essence — the diff — is computed server-side while no new FHIR-facing endpoint is added. Verified end to end in a real browser against a Patient with three real versions (create → update → update). The version id and timestamp are read from the history Bundle's entry.response etag/lastModified, which is where this server carries them (resource.meta is a fallback) — a detail that only surfaced by driving it. 17 unit tests over the diff layer (including the exact array-churn scenario the decision doc characterizes) + 6 HTTP round-trip tests. UI strings in en/es/de (parity enforced). The nav's History & Versions entry is now a real link. fmt + clippy clean. Deliberately deferred, matching the decision doc: array-churn move detection (a front-insert still cascades — accepted for v1), and the Type feed / System feed tabs are present but not yet wired to /_history and /{type}/_history.
# Conflicts: # Cargo.lock # crates/ui/Cargo.toml # crates/ui/src/lib.rs # crates/ui/templates/layouts/base.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #236. This is the prototype you asked for on #246 ("implement a prototype and we can evaluate from there"), built to the decision recorded in
docs/history-diff-rendering.md.What it does
Compares two versions of a resource from
_history, rendering the diff server-side in the two layers the decision doc settled on — no client-side diff library, works as a plain page with JS off degrading to the base view.Semantic layer (
json-patch) — the field-level change list, the exact inverse of the RFC 6902 PATCH the server already applies:json-patchwas already in the workspace lock (a default dep of rest/persistence), so it adds zero new transitive weight.Textual layer (
similar,inline) — paired lines, +/- gutters, per-side line numbers, and word-level highlighting of the run that changed within a line. Selecting v1 → v3 shows it well:"Jon"→"John"highlights only the inserted letter.Following the decision doc
meta.versionId/lastUpdatedmove every version → filtered by default, behind a "show metadata" toggle that says how many ops it hid.helios-uihandler. No new FHIR-facing endpoint — the browser reads versions from the ordinary_history/vreadAPI and posts them to/ui/history/diff.Following Brett's design
The Figma "History & Versions" frame: the Instance / Type feed / System feed tabs, the version rail (newest first, each tagged create / update / patch), and the diff panel. The nav's History & Versions entry is now a real link.
Verified in a real browser
Driven end to end against a Patient with three real versions (create → update → update). One detail only surfaced by driving it: this server carries the version id and timestamp in the history Bundle's
entry.responseetag/lastModified, not inresource.meta— so the rail reads there, withresource.metaas a fallback for servers that stamp it onto the resource.Tests
17 unit tests over the diff layer — including the exact array-churn scenario the decision doc's spike characterizes — plus 6 HTTP round-trip tests, locale parity across en/es/de.
fmt+clippyclean.Deliberately deferred (matches the decision doc)
name[]still cascades acrossname/0..2instead of a singleadd /name/0. The doc explicitly accepts this for v1; move detection is a presentation post-process that can be added later without changing the architecture, and the textual layer already shows the insert legibly./{type}/_historyand/_history.metaonto the resource body — but the filter is implemented and unit-tested against resources that carrymeta(which most real resources do).