90 lines
7.9 KiB
Markdown
90 lines
7.9 KiB
Markdown
# spa — Roadmap
|
|
|
|
A React + TypeScript single-page application for end-user operators of the TRM platform: race directors, marshals, timekeepers, and (post-Phase 4 of [[directus]]) public-facing participants. Talks to [[directus]] for REST/GraphQL + business-plane WebSocket and to [[processor]] for the live-position WebSocket firehose.
|
|
|
|
This file is the single navigation hub for all SPA implementation planning. Each phase has its own folder with a README and granular task files. Update statuses here as work lands.
|
|
|
|
## Status legend
|
|
|
|
| Symbol | Meaning |
|
|
| ------ | ----------------------------- |
|
|
| ⬜ | Not started |
|
|
| 🟦 | Planned (designed, not coded) |
|
|
| 🟨 | In progress |
|
|
| 🟩 | Done |
|
|
| ⏸ | Paused / blocked |
|
|
| ❄️ | Frozen / future / optional |
|
|
|
|
## Architectural anchors
|
|
|
|
The SPA is specified by the wiki at `../docs/wiki/`. Implementing agents should read these pages before starting any task:
|
|
|
|
- **This service** — `docs/wiki/entities/react-spa.md`
|
|
- **Map subsystem** — `docs/wiki/concepts/maps-architecture.md`, `docs/wiki/sources/traccar-maps-architecture.md`
|
|
- **Live channel** — `docs/wiki/concepts/live-channel-architecture.md`, `docs/wiki/synthesis/processor-ws-contract.md`
|
|
- **Auth + business plane** — `docs/wiki/entities/directus.md`, `docs/wiki/synthesis/directus-schema-draft.md`
|
|
- **System architecture** — `docs/wiki/sources/gps-tracking-architecture.md`, `docs/wiki/concepts/plane-separation.md`
|
|
|
|
## Non-negotiable design rules
|
|
|
|
These rules govern every task. Any deviation must be discussed and documented as a decision before code lands.
|
|
|
|
1. **Singleton MapLibre.** A single `maplibregl.Map` instance lives in a module-level variable, attached to a detached `<div>`. React mounts/unmounts the div via refs across page navigation. WebGL context never recreates. Pattern is documented in `docs/wiki/concepts/maps-architecture.md`.
|
|
2. **Side-effect-only `Map*` components.** Every map-participating component returns `null` and uses `useEffect` for setup + cleanup, with a separate effect for `setData` updates. No DOM marker components, no `react-map-gl`.
|
|
3. **rAF coalescer at the WS boundary.** Position messages buffer per-device; one `requestAnimationFrame` tick flushes the latest snapshot to the Zustand store. Per-message dispatch is the failure mode `traccar-web` exhibits — we don't replicate it.
|
|
4. **Same-origin everything.** SPA bundle, [[directus]] REST/WS, and [[processor]] WS all served under one origin via reverse proxy. Vite dev proxy in dev; Traefik in stage/prod. Cross-origin breaks the cookie auth flow.
|
|
5. **In-memory access token, httpOnly refresh cookie.** Access token never touches `localStorage`. Refresh cookie is `httpOnly + Secure + SameSite=Lax`. Refresh on 401 via Directus's `/auth/refresh`.
|
|
6. **Role-aware UI shape from day one.** Even though everyone's admin until [[directus]] Phase 4, the route guards and conditional rendering check `user.role` rather than hard-coding "everyone sees everything." Retrofitting is painful; baking it in costs nothing.
|
|
7. **Runtime config, not build-time.** Per-environment values (Directus URL, processor WS URL, optional Google Maps key) come from `/config.json` served by the proxy. One image, multiple environments. No rebuild to switch envs.
|
|
8. **Native PostGIS GeoJSON.** Geometry round-trips through `ST_AsGeoJSON(geometry)` on the server, not WKT. The SPA never imports `wellknown` or runs WKT parsing in the client.
|
|
|
|
## Phases
|
|
|
|
### Phase 1 — Foundation
|
|
|
|
**Status:** 🟨 In progress (1.1 done)
|
|
**Outcome:** A deployable empty-shell SPA: scaffold + stack rounded out + Directus cookie auth flow + protected routes + Gitea CI + compose deploy block. End state: an operator can browse to `https://stage.trmtracking.org`, log in with a Directus credential, see a placeholder home page, log out. No live map yet — that's Phase 2.
|
|
|
|
[**See `phase-1-foundation/README.md`**](./phase-1-foundation/README.md)
|
|
|
|
| # | Task | Status | Landed in |
|
|
| ---- | ------------------------------------------------------------------------------------------------------------ | ------ | --------- |
|
|
| 1.1 | Project scaffold (Vite + React + TS) | 🟩 | (manual) |
|
|
| 1.2 | [Stack rounding-out (Tailwind + shadcn/ui + deps + Prettier)](./phase-1-foundation/02-stack-rounding-out.md) | 🟩 | `9918418` |
|
|
| 1.3 | [Vite dev proxy + path aliases + tsconfig hardening](./phase-1-foundation/03-vite-dev-proxy.md) | 🟩 | `39b60c9` |
|
|
| 1.4 | [Runtime config endpoint](./phase-1-foundation/04-runtime-config.md) | 🟩 | `8e2151a` |
|
|
| 1.5 | [Directus auth client (cookie mode + refresh)](./phase-1-foundation/05-directus-auth-client.md) | 🟩 | `38fe2e3` |
|
|
| 1.6 | [Login page](./phase-1-foundation/06-login-page.md) | 🟩 | `7215cb5` |
|
|
| 1.7 | [Routing skeleton (TanStack Router + role-aware guards)](./phase-1-foundation/07-routing-skeleton.md) | 🟩 | `f4a5e5b` |
|
|
| 1.8 | [Logout flow](./phase-1-foundation/08-logout-flow.md) | 🟩 | `1ee339c` |
|
|
| 1.9 | [Gitea CI + Dockerfile + nginx static serve](./phase-1-foundation/09-gitea-ci-and-dockerfile.md) | ⬜ | — |
|
|
| 1.10 | [Compose service block in trm/deploy](./phase-1-foundation/10-deploy-compose-block.md) | ⬜ | — |
|
|
|
|
### Phase 2 — Live monitoring map
|
|
|
|
**Status:** ⬜ Not started — depends on [[processor]] Phase 1.5 landing
|
|
**Outcome:** The dogfood-day deliverable. MapLibre singleton + tile-source switcher + sprite preload + WS client with rAF coalescer + Zustand position store + `MapPositions` (clustered + selected) + `MapTrails` (bounded ring buffer) + event picker + camera control trio + connection-status indicator. Operators on race day open one page, pick the active event, and watch their field move in real time.
|
|
|
|
[**See `phase-2-live-map/README.md`**](./phase-2-live-map/README.md)
|
|
|
|
### Phase 3 — Dogfood readiness
|
|
|
|
**Status:** ⬜ Not started
|
|
**Outcome:** Operational polish a closed pilot needs but a demo doesn't: error boundaries that don't blank-screen the map, connection-state UI that tells the operator "WS reconnecting", mobile-responsive baseline (doesn't crash on a phone in the field), per-device detail panel, operator-friendly empty/loading states.
|
|
|
|
[**See `phase-3-dogfood-readiness/README.md`**](./phase-3-dogfood-readiness/README.md)
|
|
|
|
### Phase 4 — Future / optional
|
|
|
|
**Status:** ❄️ Not committed
|
|
[**See `phase-4-future/README.md`**](./phase-4-future/README.md)
|
|
|
|
Ideas on radar: geometry editor (depends on [[directus]] Phase 2 collections), replay mode, heatmaps / deck.gl, i18n (Albanian), dark mode, E2E tests.
|
|
|
|
## Operating model
|
|
|
|
- **Implementation agent contract.** Each task file is self-sufficient: goal, deliverables, specification, acceptance criteria. An agent should be able to complete one task without reading the whole wiki — but should skim the wiki references at the top of the task before starting.
|
|
- **Sequence within a phase.** Task numbering reflects intended order. Soft dependencies are explicit in each task's "Depends on" field. Tasks with no dependencies on each other can be done in parallel.
|
|
- **Status updates.** When a task is started, change its row in this ROADMAP to 🟨 and the task file's status badge accordingly. When done, 🟩 + a one-line note in the task file's "Done" section pointing at the merging commit/PR.
|
|
- **Drift control.** If implementation diverges from a task's spec, update the task file _before_ the diverging code lands, with a note explaining why. Do not let plans rot.
|