Frontend - digitalspace/eagle-demi GitHub Wiki
React app (Vite) in frontend/ (repo eagle-demi), built to frontend/dist. Reference for the
screens, their routes, and what each talks to.
Every route requires a Keycloak session carrying staff, sysadmin, or demi-admin.
- Anonymous visitor: sees the sign-in screen (
frontend/src/shell/SignIn.tsx). - Authenticated account without one of those roles: Keycloak accepts the sign-in, DEMI rejects it
and shows a message asking for the
staffrole. - Authenticated staff account: sees the shell.
frontend/src/App.tsx renders <SignIn /> instead of the router until isStaff is true, so a
typed URL cannot bypass it. There is no per-route guard.
Header: environment pill, "How this is built" info modal, account menu (My account, Active
sessions, Sign out). The header's menu button only appears below 900px wide, where it opens the
sidebar as a drawer. At 900px and above the sidebar is a rail with its own collapse toggle;
collapsed, the rail is a 48px strip. Sign out sends client_id and post_logout_redirect_uri to
Keycloak, plus id_token_hint when a token is on hand; this Keycloak realm ignores
redirect_uri, so a stray copy of that parameter would leave the user on the Keycloak page.
Sidebar: grouped links from the screen registry, frontend/src/shell/screens.ts. Groups:
Discover (Map Explorer, Search, AI Summary), Account (Overview,
Active sessions), Operate (eagle-notify, Short URLs), Reference (Access model, API documentation,
API keys).
| Screen | Route | Group | Backend |
|---|---|---|---|
| Overview |
/workspace (/profile redirects here) |
Account | identity from Keycloak token claims, preferences, saved areas and saved queries through GET /me/data, PUT /me/prefs, /me/lassos, /me/queries — see User-Data
|
| Active sessions | /sessions |
Account | current session only; not wired to a revoke endpoint |
| Map Explorer |
/map, /boundaries
|
Discover |
GET /api/search, boundary GeoJSON containers, saved areas through /me/lassos — see User-Data
|
| Search |
/search (/index and /content redirect here) |
Discover |
GET /api/search, record-type tabs (Projects, Documents, Activities, Notifications); documents tab scoped "Names & details" or "Inside documents" against Azure AI Search; row click opens a record detail dialog; "Save this query" stores the URL through PUT /me/queries — see User-Data
|
| AI Summary | /summary |
Discover | GET /api/search/summary |
| Project summary | /projects/:id |
Discover | project record GET /api/projects/:id, document counts GET /api/search, stored summary GET /api/projects/:id/summary — see ADR-013-Stored-Project-Summaries
|
| eagle-notify | /notify |
Operate | eagle-notify API; only wired when NOTIFY_API_LOCATION is set |
| Short URLs | /links |
Operate |
/api/links, grouped into My links and Shared links by personal — see Short-Links
|
| Access model | /rbac |
Reference | client-side explainer of the RBAC ladder; calls GET /api/me
|
| API documentation |
/developers (/api-docs bounces to /api/api-docs, same origin) |
Reference | raw swagger.yaml, dev and test only |
| API keys | /keys |
Reference | /api/admin/api-keys |
Screen definitions and this table's routes come from frontend/src/routes.tsx and
frontend/src/shell/screens.ts.
/workspace is the account home, reached from the account menu and the /profile redirect. It
holds:
- Identity — name, email, IDIR, roles and groups, read-only from the Keycloak token. Roles are filtered to the ones DEMI checks.
- Preferences — landing screen and results-per-page, editable here; see User-Data for the localStorage-first, sync-when-signed-in write path.
-
Saved map areas — a selector list of the caller's saved lassos next to a live MapLibre
mini-map (
frontend/src/map/MiniMap.tsx, loaded lazily). Picking one previews it on the mini-map; "Apply on map" carries it into Map Explorer's own lasso filter; each row can be deleted. -
Saved search queries — listed below the saved areas: name, a summary (record type, keywords,
filter count, date), "Open in search" and "Delete". Backed by the same
queriesdata the Search screen's "Save this query" writes; see User-Data. - Short links — the links this account created, the same rows Short URLs lists under "My links".
Pins come from a clustered GeoJSON source (clusterRadius 60, clusterMaxZoom 9) drawn as HTML
markers, not map layers. Selecting a project adds demi-marker--selected to its marker and raises
it above unselected markers with a higher z-index (650 over 600) rather than moving it to a
separate pane. The selected marker carries a gold ring and a one-shot arrival pulse
(demi-marker--arriving). There is no spiderfy: a cluster does not expand to show the markers
inside it, and a project co-located with others is picked from the rail instead. Everything else
on the map — other markers, cluster bubbles, boundary polygons, wildfire pills — dims while a
selection is active. Selecting always centres the map on the marker: a fly-in (flyTo) from a
BC-wide view (zoom below 6), a pan (easeTo) otherwise.
Four doors reach the one deselect: the detail card's close button, Escape, a click on the map background, and re-clicking the selected rail row (a toggle). Clicking the selected marker itself also deselects.
A saved lasso's filter chip shows the lasso's name instead of a generic "Lasso area" label. A
saved area applied from the Overview screen draws on the map through the same effects that redraw
on selection and filter changes; those effects rerun once the map's onLoad has fired (the
loaded state in MapExplorer.tsx), so an area applied before the map has finished loading still
lands.
@bcgov/design-tokens and @bcgov/bc-sans supply tokens and the BC Sans typeface. No
FontAwesome, no Google Fonts.
frontend/src/styles/vendor/ holds CSS copied from eagle-design-kit and eagle-demi-admin.
frontend/scripts/sync-design-css.sh copies it; --check compares instead and exits non-zero on
drift, used in CI.
yarn dev for a local server, yarn lint, yarn test (Vitest). yarn build writes to
frontend/dist. node scripts/e2e-smoke.mjs smoke-tests a build against yarn preview: it needs
Playwright's Chromium, found on the module path or pointed to with PLAYWRIGHT_MODULE.
design/README.md and design/handoffs/2026-08-28-revision/ in the eagle-demi repo hold the
design handoff material for this shell.