Skip to content

Frontend

Daniel Truong edited this page Sep 22, 2026 · 10 revisions

Frontend

React app (Vite) in frontend/ (repo eagle-demi), built to frontend/dist. Reference for the screens, their routes, and what each talks to.

Sign-in gate

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 staff role.
  • 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.

Shell

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).

Screens

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.

Overview screen

/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 queries data 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".

Map Explorer selection

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.

Styling

@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.

Build and test

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 references

design/README.md and design/handoffs/2026-08-28-revision/ in the eagle-demi repo hold the design handoff material for this shell.

DEMI Wiki

Home

Current system

Azure

Domain

ADRs

History

Related

Clone this wiki locally