Surface + waytype proportion bars on route detail, from the BRouter waytags the Planner already computes (and currently discards on save). Design records the data-source decision: persist a compact distance-weighted breakdown for Planner-created routes (accurate, no external call); Overpass map-matching for imports/uploads is parked as a future option. Validates with `openspec validate --strict`. Specs only; no code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1.8 KiB
1.8 KiB
1. Breakdown derivation (shared, pure)
- 1.1 Add a pure
surfaceBreakdown(coordinates, surfaces, highways)helper (in@trails-cool/map-coreor a planner lib): sum haversine segment lengths into per-surface and per-waytype buckets →{ surface: Record<string, number>, highway: Record<string, number> }(metres). Unit-test it. - 1.2 Define the shared wire shape (Zod) for the breakdown in
@trails-cool/api(SurfaceBreakdownSchema, all values ≥ 0).
2. Capture at save (Planner → Journal)
- 2.1 In
apps/planner/.../api.save-to-journal, read the sessionEnrichedRoute(coordinates + surfaces + highways), compute the breakdown, and addsurfaceBreakdownto the POST body. - 2.2 Journal callback (
api.routes.$id.callback) validates the optionalsurfaceBreakdownand persists it; re-save overwrites.
3. Schema
- 3.1 Add nullable
surfaceBreakdownjsonb tojournal.routes;pnpm db:push.
4. Render
- 4.1 Route detail loader exposes
surfaceBreakdown. - 4.2
SurfaceBreakdowncomponent: stacked proportion bar per dimension (surface, waytype), segments coloured viaSURFACE_COLORS/HIGHWAY_COLORS(DEFAULT for unknown), legend with category · km · % (largest first), unknown → "other"; hidden when empty. Mount onroutes.$id.tsx. - 4.3 i18n
journal.surface.*(label + category names + "other") en + de.
5. Tests & checks
- 5.1 Unit:
surfaceBreakdown(distance weighting, multiple surfaces, unknown bucket). - 5.2 Component (jsdom): bars render with correct proportions; "other" for unknowns; hidden when empty.
- 5.3 E2E: a Planner-saved route with waytags shows the breakdown bars on its detail page (or assert via a seeded route if the planner round-trip is too heavy for e2e).
- 5.4
pnpm typecheck && pnpm lint && pnpm test && pnpm test:e2egreen.