Skip to content

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 mode from the URL; component renders the new topbar and, depending on mode, 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 / delete topbar actions.
  • Any change to Breadcrumb usage on other routes.
  • Any change to data/projects.json or projects.server.ts.

Why this shape

  • Reading mode in the loader (rather than useState + useSearchParams on 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 with onClick/useNavigate means 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 mode from new 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/tileSet is unchanged and still checked before the mode is resolved.
  • Loader data includes mode: "view" | "edit" | "settings" alongside the existing project, map, source fields.
  • pnpm typecheck passes.

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 current mode.
  • 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 current mode is visually distinguished (e.g. a different Button variant) from the other two.
  • project-map.tsx renders this component in place of the previous ← {project.name} / {map.name} header block; that block is removed.
  • pnpm typecheck passes.
  • 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 typecheck passes.
  • Manual check: pnpm dev, visit ?mode=edit and confirm the three placeholder panels appear around a still-functional map; visit ?mode=settings and confirm the map is replaced by the placeholder text; an invalid ?mode=foo falls back to the plain view layout.

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.