Map View: Mode Switching (shell)¶
Goal¶
Add a view / edit / settings mode switcher to the map view, driven by
a URL search param, replacing the current inline breadcrumb-style header
with a topbar that carries navigation back to the project plus the mode
switcher. edit mode reveals placeholder sidebar containers (left, bottom,
top); settings mode shows a "coming soon" placeholder. No layer/POI/route
editing, no map-level settings fields, and no save/delete actions are part
of this task.
Decisions¶
| Decision | Choice | Why |
|---|---|---|
| Plan file location | docs/v0.0.2/map-view-mode-switching.md |
Follows the existing per-feature file precedent in this folder (e.g. project-settings.md) |
| Scope | Shell only — mode switcher, topbar, placeholder sidebars/settings panel | Confirmed with user — layers/routes don't exist as features, POI editing is tracked separately, no functional tool goes into this task |
| Mode state | ?mode=view\|edit\|settings URL search param, read in the route loader |
Confirmed with user — linkable and survives a refresh; matches this app's ssr: true, no-client-fetch convention (loader owns derived state, not useState) |
Invalid/missing mode |
Falls back to "view" rather than 404ing |
Mode is a display concern, not a resource lookup — an unrecognized value shouldn't break the page |
| Mode switcher control | ButtonGroup of three Links (~/components/ui/button-group), each pointing at ?mode=<value> |
Existing primitive, already in the design system; renders as real links so it works without client JS, consistent with SSR-first routing here |
| Topbar scope | New component used only on project-map.tsx | Confirmed with user — Breadcrumb usage on home.tsx and project.tsx is untouched |
settings mode content |
Map and sidebars are not rendered; a single centered "Settings — coming soon" placeholder takes their place | There's nothing to configure yet (projects.server.ts has no map-update function); showing the map behind a placeholder would imply settings apply to it today |
edit mode layout |
Fixed-size placeholder panels: left column, bottom row, thin top row, each with a section heading only (Layers, Points of interest, Routes) |
Matches the backlog's "left, bottom, top" sidebar description without inventing tool content |
| Save / delete | Not added | Confirmed with user — nothing is persisted by edit/settings yet, so there's nothing for these actions to do |
Scope¶
In:
- app/routes/project-map.tsx — loader
reads and validates
modefrom the URL; component renders the new topbar and, depending onmode, either the plain map, the map with placeholder sidebars, or the map with settings placeholder. app/components/— one new topbar component (mode switcher + back link- map name).
Out:
- Any functional layer, points-of-interest, or route editing tool (POI is tracked separately as "Map View: Points of Interest" in backlog.md; layers/routes have no backlog item yet and aren't created here).
- Map-level settings fields (rename, change tile set/source) — no backend support exists for this.
save/deletetopbar actions.- Any change to
Breadcrumbusage on other routes. - Any change to
data/projects.jsonorprojects.server.ts.
Why this shape¶
- Reading
modein theloader(rather thanuseState+useSearchParamson the client) keeps this route consistent with the project's server-loader convention and means the correct mode is in the very first server-rendered HTML — no flash of the wrong mode. - Using real
Links for the mode switcher instead of buttons withonClick/useNavigatemeans mode-switching keeps working if JS hasn't loaded yet, and needs no new client state. - A dedicated topbar component (rather than growing the inline header in
project-map.tsx) keeps the mode-switcher UI reusable if a later task adds real edit/settings functionality without a rewrite. - Placeholder panels are deliberately inert (no
useState, no forms) so this task can't be mistaken for shipping any editing capability — that's explicitly deferred.
Tasks¶
Ordered so each task type-checks and is shippable on its own; later tasks depend on earlier ones.
T1 — Loader: read and validate mode¶
Goal: project-map.tsx's loader derives a validated mode from the
request URL and returns it as loader data.
Acceptance criteria:
- The loader reads
modefromnew URL(request.url).searchParams. - Valid values are exactly
"view","edit","settings"; any other value (including missing/empty) resolves to"view". - The existing 404 behavior for an unresolvable
project/map/tileSetis unchanged and still checked before the mode is resolved. - Loader data includes
mode: "view" | "edit" | "settings"alongside the existingproject,map,sourcefields. pnpm typecheckpasses.
T2 — Topbar component: back link, map name, mode switcher¶
Goal: A new topbar component replaces the current inline header,
showing project navigation, the map name, and a view/edit/settings
switcher built from ButtonGroup + Link.
Acceptance criteria:
- New component in
app/components/accepts the project (for the back link), the map name, and the currentmode. - Renders a link back to
/project/:projectId(same label/behavior as today:← {project.name}) and the map's name. - Renders three links, one per mode, each pointing at the current route
with
?mode=<value>set (preserving no other query params, since none exist on this route today); the link matching the currentmodeis visually distinguished (e.g. a differentButtonvariant) from the other two. project-map.tsxrenders this component in place of the previous← {project.name} / {map.name}header block; that block is removed.pnpm typecheckpasses.- Manual check:
pnpm dev, open a map, confirm the topbar renders and clicking each mode link updates the URL and the highlighted state.
T3 — Edit-mode sidebar shell and settings placeholder¶
Goal: project-map.tsx renders differently per mode: plain map for
view, map plus placeholder left/bottom/top panels for edit, and a
single "coming soon" placeholder (no map) for settings.
Acceptance criteria:
mode === "view"(default): renders exactly as today — topbar plus the full-size<Map>, no sidebars.mode === "edit": renders the topbar, then a layout with a left-column placeholder panel (heading "Layers"), a thin top-row placeholder panel above the map (heading "Routes"), a bottom-row placeholder panel below the map (heading "Points of interest"), and the<Map>still mounted and interactive in the remaining space. Panels show only their heading text — no forms, lists, or buttons.mode === "settings": renders the topbar, then a single centered placeholder panel with the text "Settings — coming soon" in place of the map;<Map>is not mounted in this mode.- Switching modes via the topbar re-renders the correct layout without a full page reload's worth of visible flicker (standard React Router client-side navigation is sufficient — no special-casing needed).
pnpm typecheckpasses.- Manual check:
pnpm dev, visit?mode=editand confirm the three placeholder panels appear around a still-functional map; visit?mode=settingsand confirm the map is replaced by the placeholder text; an invalid?mode=foofalls back to the plainviewlayout.
T4 — Backlog cleanup¶
Goal: Reflect that the mode-switching shell is planned/in progress.
Acceptance criteria:
- docs/backlog.md's "Map View: Mode Switching" entry is removed (or annotated as shell-complete, per whatever the team prefers at merge time) once T1–T3 ship.