Archive the monolithic route-features spec and replace with 9 focused OpenSpec changes: multi-day-routes, waypoint-notes (with POI snapping), undo-redo, local-dev-stack, route-sharing, route-discovery, activity-photos, osm-overlays, plus the existing changelog and komoot-import. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.3 KiB
3.3 KiB
1. Spatial Index & Database
- 1.1 Verify whether a GiST index exists on
journal.routes.geom-- if not, create one via raw SQL migration:CREATE INDEX IF NOT EXISTS idx_routes_geom ON journal.routes USING GIST (geom); - 1.2 Verify the
visibilitycolumn exists onjournal.routes(dependency on route-sharing) -- if not, note this as a blocker
2. Bounding Box API
- 2.1 Create
apps/journal/app/routes/api.routes.explore.tswith a GET loader that acceptssouth,west,north,eastquery params, validates bounds, and returns public routes within the bounding box usingST_Intersects+ST_Simplify(geom, 0.001), limited to 50 results - 2.2 Join route owner to include
usernameanddisplayNamein response - 2.3 Register the API route in
apps/journal/app/routes.ts:route("api/routes/explore", "routes/api.routes.explore.ts")
3. Explore Page
- 3.1 Create
apps/journal/app/routes/routes.explore.tsxwith a full-pageMapViewfrom@trails-cool/map, filling the viewport below the nav bar - 3.2 Register the page route in
apps/journal/app/routes.ts:route("routes/explore", "routes/routes.explore.tsx")(before theroutes/:idroute to avoid param conflict) - 3.3 Create
useExploreRouteshook: listens to Leafletmoveend, debounces 300ms, fetches/api/routes/explorewith current viewport bounds, returns{ routes, isLoading }. Use AbortController to cancel in-flight requests. - 3.4 Add simple bounds-based cache (Map with max 20 entries) to
useExploreRoutesto avoid re-fetching previously viewed areas - 3.5 Store and restore last map center/zoom in localStorage so the explore page remembers the user's last viewport
4. Route Rendering & Interaction
- 4.1 Create
ExploreRouteLayercomponent in@trails-cool/map: renders an array of route objects as Leaflet polylines with hover highlighting (opacity 0.6 -> 0.9, weight 3 -> 5) - 4.2 Add click handler to each polyline that opens a Leaflet popup with route name (linked to
/routes/:id), formatted distance, elevation gain, and author name (linked to/users/:username) - 4.3 Export
ExploreRouteLayerfrom@trails-cool/mappackage index
5. Navigation
- 5.1 Add "Explore" link to Journal navigation bar, pointing to
/routes/explore
6. Performance
- 6.1 Add loading indicator (small spinner in map corner) shown during API fetches, without clearing existing routes from the map
- 6.2 Verify query performance with
EXPLAIN ANALYZEon the bounding box query with the GiST index -- should use index scan, not sequential scan
7. i18n
- 7.1 Add translation keys (en + de) for: explore page title ("Explore Routes" / "Routen entdecken"), loading indicator, popup labels (distance, elevation, author), empty state ("No public routes in this area" / "Keine offentlichen Routen in diesem Bereich"), nav link
8. Testing
- 8.1 Unit test for bounding box API loader: mock database, verify correct SQL parameters, response format, 50-result limit, handling of missing/invalid bounds
- 8.2 Unit test for
useExploreRouteshook: verify debouncing, abort behavior, cache hit/miss - 8.3 E2E test: navigate to
/routes/explore, verify map renders, pan the map, verify routes appear as polylines (requires seeded public routes with geometry in test database)