feat: add As-Of time-travel UI for viewing historical snapshots - #37
feat: add As-Of time-travel UI for viewing historical snapshots#37bhudevbhanpuriya wants to merge 4 commits into
Conversation
Update — follow-up commit (0b06278)Refines the As-Of chart behavior and fixes an existence-state bug found while Chart in snapshot mode
Empty-window feedback
Chart rendering
Existence-state fix (
|
0b06278 to
2729177
Compare
|
rebased on latest main, no conflicts, and moved all UI strings to i18n |
Introduces a full As-Of (time-travel) feature for the map/datastream
UI, letting users view the SensorThings graph as it existed at a past
point in time instead of live.
State & data layer:
- AsOfContext/AsOfProvider (context/AsOfContext.tsx): global
asOfDate/isSnapshot state, exposed via useAsOf().
- asOfAdapter.ts: single AsOfAdapter interface (resolveThing,
fetchCommits) with two implementations selected by
NEXT_PUBLIC_VERSIONING_ENABLED:
- mockAdapter: resolves state from local-dummy-things.ts, for
demo/dev use with no backend versioning support.
- apiAdapter: calls the real backend via
Things(id)?$as_of=<date> and Things(id)/Commit.
- useAsOfThing / useAsOfCommits: hooks wrapping the adapter, exposing
the resolved snapshot Thing, existence state
(exists/not-yet-created/deleted), and commit history for a Thing.
UI components (features/as-of/components/):
- AsOfNavbarButton + AsOfDropdown: navbar pill (Live / snapshot chip)
and a calendar+time picker with quick-range presets, with a
future-date guard.
- AsOfBanner: page-wide banner shown while in snapshot mode.
- TimelineScrubber: bottom-fixed slider across [earliest commit, now]
with clickable commit tick marks.
- AsOfExistenceDialog: dismissible floating alert with a "jump to
nearest valid date" action when asOfDate falls outside a Thing's
known lifetime.
Wiring into the app:
- layout.tsx: wraps the app in AsOfProvider, renders AsOfBanner.
- Navbar.tsx: adds AsOfNavbarButton, reworks the navbar into a
3-column grid to fit it.
- Home.tsx: resolves the selected Thing's snapshot via useAsOfThing
and feeds the resolved snapshotThing (not the live thing) into
DatastreamTable; renders TimelineScrubber and
AsOfExistenceDialog; re-arms the existence dialog only on a
genuine transition into an invalid state, not on every scrubber
tick.
- DatastreamTable.tsx: read-only banner and hides Edit/Delete/New/
Import actions while isSnapshot is true.
- LeafletMap.tsx / leafletDraw.ts / leafletCluster.ts: snapshot
markers get an amber date-pill divIcon (createThingMarkerDivIcon);
MapContextMenu hides "New"/"Import from file" during snapshot mode
so the map stays read-only like the table.
- ChartModal.tsx / ObservationGraph.tsx / observationGraphOptions.ts:
clamps the date-range picker to asOfDate, draws a dashed snapshot
marker line on the chart.
- observations.ts: appends $as_of to the observations request when
in snapshot mode.
- useChartState.ts: namespaces the observation cache by asOfDate so
live and snapshot results never collide.
- useDataSourcesSync.ts: injects the local dummy stations onto the
map only when NEXT_PUBLIC_VERSIONING_ENABLED is false (mock mode).
Follow-up to the As-Of time-travel UI: makes the observations chart react correctly to snapshot changes and corrects a wrong existence state when a snapshot predates a Thing's creation. Chart in snapshot mode (useChartState.ts): - Re-anchor the chart time range and refetch when asOfDate changes (scrubber, calendar, or "jump to nearest data"), instead of leaving a stale window from a previous snapshot. Debounced on the trailing edge so continuous scrubbing fires a single request. - Default window is strictly [asOfDate-7d, asOfDate]; clears the persisted range and cache on any snapshot/live context switch. - Refetch the whole current selection (all things / observed properties) and preserve an active comparison across snapshot changes, not just the primary datastream. Empty-window feedback (ChartModal.tsx): - Show an informational warning when the snapshot window has no data, describing where the data actually is (before/after the range), without ever mutating the as-of state. Chart rendering (ObservationGraph.tsx, observationGraphOptions.ts): - Pin the x-axis to the queried window in snapshot mode so sparse or empty data still renders the full range and the snapshot markLine can't stretch the axis. - Draw the snapshot markLine once (on the primary series) instead of once per series. Existence-state fix (asOfAdapter.ts): - Determine not-yet-created vs deleted from the Thing's commit history (transaction time, the axis $as_of filters on) instead of phenomenonTime, which can be years off from when the row was written. Fixes the dialog showing the wrong direction / jump target when a snapshot predates a Thing's creation. - fetchCommits now reads the plural /Things(id)/Commits collection for full history (falling back to the singular /Commit).
Restore data-sources.json authorizationEnabled to true and revert the tsconfig.tsbuildinfo build artifact; both were local testing conveniences and are not part of the As-Of feature.
All user-facing copy in the As-Of components was hardcoded English while the rest of the app goes through react-i18next. Move every label, button, message, tooltip and aria-label into a new as_of namespace and add the matching en/it translations. Calendar month and weekday names are resolved through i18n rather than dayjs locale data so the picker stays translated regardless of the active dayjs locale. Reuses general.apply for the Apply button. Also drops an unused asOfDate binding in AsOfExistenceDialog. Verified: tsc --noEmit clean; en/it key sets identical; every t() key used in the components resolves; no new eslint problems.
2729177 to
e59703a
Compare
|
Update - two new commits since this was last looked at. 1. Removed my local testing changes. 2. Added translations. What I checked: the TypeScript build passes, the English and Italian files have There are also 4 warnings in these files that the linter flags. They were there No conflicts with Next I'm starting the |
Introduces a full As-Of (time-travel) feature for the map/datastream UI, letting users view the SensorThings graph as it existed at a past point in time instead of live.
State & data layer:
UI components (features/as-of/components/):
Wiring into the app:
Known limitations
(pre-existing, see README.GSoC.md TODO) - the NEXT_PUBLIC_VERSIONING_ENABLED
fix in this PR only covers local dev (dev-docker-compose.yaml).
today (no plural /Commits endpoint exists yet), so the timeline scrubber's
tick marks are limited when running against a real backend.