Add 8 proposed changes and old trails analysis

Proposed changes (all with proposal, design, specs, tasks):
- app-navigation (9 tasks) — nav bars for both apps
- planner-landing-page (7 tasks) — standalone landing page
- planner-multiplayer-awareness (13 tasks) — participants, cursors, names
- changelog (13 tasks) — public changelog with "what's new"
- transactional-emails (15 tasks) — magic link + welcome emails
- planner-features (18 tasks) — no-go areas, notes, recovery, rate limits
- komoot-import (23 tasks) — Komoot tour import
- route-features (37 tasks) — sharing, multi-day, spatial, photos

Also adds docs/old-trails-analysis.md with feature analysis from the
older trails project.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-03-25 03:57:20 +01:00
parent 57141cdfab
commit c7c1c275df
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
55 changed files with 1704 additions and 0 deletions

View file

@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-25

View file

@ -0,0 +1,71 @@
## Context
The architecture doc defines a rich route model with permissions, multi-day
support, spatial queries, and photos. Currently routes are owner-only, flat
(no days), and have no photos or spatial search. This change adds the route
and activity features needed before federation makes them visible to others.
## Goals / Non-Goals
**Goals:**
- Route visibility (private/public) and sharing with specific users
- Fork public routes
- Multi-day route support with day-break waypoints
- Map-based route discovery via PostGIS
- Photo attachments on activities
- Contributor tracking on route versions
**Non-Goals:**
- Federation of routes (separate change)
- Real-time collaborative permission changes
- Photo editing or filters
- Route recommendations based on preferences
- Activity collections (multi-day trip grouping — future)
## Decisions
### D1: Visibility as enum column on routes and activities
Add `visibility` column (`public`, `private`, default `private`) to both
routes and activities. `followers_only` added later when following exists.
Public routes appear in spatial search. Private routes are owner-only.
### D2: Route shares table for explicit sharing
```
journal.route_shares (routeId, userId, permission: 'view' | 'edit')
```
Owner can share with specific users. Shared users see the route in their
"Shared with me" section. Edit permission allows starting Planner sessions.
### D3: Day breaks as waypoint property
The Planner's Yjs waypoint Y.Map gets an optional `isDayBreak: true` field.
The sidebar and elevation chart show day boundaries. GPX export uses one
track segment per day. The Journal stores day-break indices in route metadata.
### D4: PostGIS spatial search
Routes already store geometry as PostGIS LineString. Add a `/routes/explore`
page with a map — as the user pans/zooms, query public routes within the
bounding box using `ST_Intersects`. Show route previews on the map.
### D5: Photo storage via S3 (Garage)
Use the Garage S3-compatible storage already in the architecture. Upload flow:
server generates presigned PUT URL → client uploads directly to S3 → server
stores the S3 key in a `journal.activity_photos` table. Serve via presigned
GET URLs or a CDN path.
### D6: Contributor tracking via array column
Add `contributors` text array to `journal.route_versions`. When a Planner
session saves back via callback, the JWT identifies the actor. The version
records the contributor's user ID (or ActivityPub URI for future federation).
## Risks / Trade-offs
- **S3 setup required** → Garage needs to be enabled in docker-compose (currently commented out). Adds operational complexity.
- **Spatial search performance** → PostGIS bounding box queries are fast with spatial indexes. Already have the geometry column.
- **Multi-day in Planner requires Yjs changes** → Adding `isDayBreak` to waypoints is backward-compatible (optional field). Existing sessions unaffected.

View file

@ -0,0 +1,43 @@
## Why
Routes are currently flat — no permissions beyond owner, no way to share with
specific people, no forking, no multi-day support, no spatial discovery, and
no contributor tracking. The architecture doc specifies all of these for Phase
2. Without them, routes are just personal GPX storage with no social or
collaborative dimension.
## What Changes
- **Route sharing permissions**: Private/public/shared visibility levels with
a view/edit permission matrix
- **Route forking**: Copy someone else's public route to your own collection
- **Multi-day routes**: Day-break waypoints that split a route into stages with
per-day stats and GPX segments
- **Spatial search**: "Routes near me" or "routes in this area" using PostGIS
- **Contributor tracking**: Record who contributed to each route version
- **Activity visibility levels**: Public/followers-only/private on activities
- **Photo attachments**: Upload photos to activities (requires S3/Garage setup)
## Capabilities
### New Capabilities
- `route-sharing`: Permission model (private/public/shared), view/edit access, route forking
- `spatial-search`: Map-based route discovery using PostGIS bounding box and proximity queries
- `multi-day-routes`: Day-break markers on waypoints, per-day stats, multi-segment GPX export
- `activity-photos`: Photo upload and display on activities via S3-compatible storage
### Modified Capabilities
- `route-management`: Add visibility, contributor tracking, forking
- `activity-feed`: Add visibility levels (public/followers-only/private)
- `planner-session`: Support day-break waypoint markers
- `infrastructure`: S3/Garage setup for photo storage
## Impact
- **Database**: New columns (visibility, contributors) on routes and activities, new route_shares table, photo storage metadata
- **Storage**: S3-compatible object storage (Garage) for photos
- **Files**: Route detail page, activity pages, search page, Planner waypoint UI, sharing UI
- **Dependencies**: S3 client library for photo uploads
- **Privacy**: Photo storage, route visibility controls documented in manifest

View file

@ -0,0 +1,12 @@
## MODIFIED Requirements
### Requirement: Activity visibility
Activities SHALL have visibility levels controlling who can see them.
#### Scenario: Private activity
- **WHEN** an activity's visibility is "private"
- **THEN** only the owner can view it
#### Scenario: Public activity
- **WHEN** an activity's visibility is "public"
- **THEN** anyone can view it on the owner's profile

View file

@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: Photo attachments on activities
Users SHALL be able to upload photos to their activities.
#### Scenario: Upload photo
- **WHEN** a user adds a photo to an activity
- **THEN** the photo is uploaded to S3 storage and linked to the activity
#### Scenario: View photos
- **WHEN** a user views an activity with photos
- **THEN** the photos are displayed in a gallery on the activity page
#### Scenario: Delete photo
- **WHEN** a user deletes a photo from their activity
- **THEN** the photo is removed from storage and unlinked

View file

@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: Day-break waypoints
Waypoints in the Planner SHALL support a day-break marker that splits the route into stages.
#### Scenario: Mark day break
- **WHEN** a user marks a waypoint as a day break in the Planner
- **THEN** the route is visually split into days at that point
#### Scenario: Per-day statistics
- **WHEN** a route has day-break markers
- **THEN** the sidebar shows distance and elevation per day/stage
#### Scenario: GPX export with day segments
- **WHEN** a multi-day route is exported as GPX
- **THEN** each day is a separate track segment in the GPX file

View file

@ -0,0 +1,12 @@
## MODIFIED Requirements
### Requirement: Route metadata
Routes SHALL track visibility, contributors, and support forking.
#### Scenario: Visibility on route creation
- **WHEN** a user creates a route
- **THEN** the route defaults to "private" visibility
#### Scenario: Contributor recorded on version
- **WHEN** a Planner session saves a new route version via callback
- **THEN** the version records the contributor who made the edit

View file

@ -0,0 +1,30 @@
## ADDED Requirements
### Requirement: Route visibility levels
Routes SHALL have a visibility setting controlling who can see them.
#### Scenario: Private route
- **WHEN** a route's visibility is "private"
- **THEN** only the owner can view it
#### Scenario: Public route
- **WHEN** a route's visibility is "public"
- **THEN** anyone can view it and export its GPX
### Requirement: Share routes with specific users
Route owners SHALL be able to share routes with specific users at view or edit permission levels.
#### Scenario: Share with view access
- **WHEN** owner shares a route with another user as "view"
- **THEN** that user can see the route and export GPX but cannot edit
#### Scenario: Share with edit access
- **WHEN** owner shares a route with another user as "edit"
- **THEN** that user can start Planner sessions and create new versions
### Requirement: Fork routes
Users SHALL be able to fork (copy) public routes to their own collection.
#### Scenario: Fork a public route
- **WHEN** a user clicks "Fork" on a public route
- **THEN** a copy is created in their collection with the original credited

View file

@ -0,0 +1,12 @@
## ADDED Requirements
### Requirement: Map-based route discovery
Users SHALL be able to discover public routes by browsing a map.
#### Scenario: Browse routes on map
- **WHEN** a user visits the route explore page and pans/zooms the map
- **THEN** public routes within the visible area are shown as polylines on the map
#### Scenario: Route preview
- **WHEN** a user clicks a route on the explore map
- **THEN** a popup or sidebar shows the route name, distance, elevation, and a link to the detail page

View file

@ -0,0 +1,68 @@
## 1. Route Visibility & Sharing Schema
- [ ] 1.1 Add `visibility` enum column (private, public) to `journal.routes`, default private
- [ ] 1.2 Add `visibility` enum column (private, public) to `journal.activities`, default private
- [ ] 1.3 Create `journal.route_shares` table (routeId, userId, permission: view/edit)
- [ ] 1.4 Add `contributors` text array column to `journal.route_versions`
- [ ] 1.5 Add `forkedFromId` nullable column to `journal.routes`
- [ ] 1.6 Push schema and verify locally
## 2. Route Permissions Logic
- [ ] 2.1 Create `apps/journal/app/lib/permissions.server.ts` with canView(routeId, userId), canEdit(routeId, userId) functions
- [ ] 2.2 Update route detail loader to check visibility/permissions (show 404 for unauthorized)
- [ ] 2.3 Update route list to only show own + shared routes
- [ ] 2.4 Add visibility toggle (private/public) to route edit page
- [ ] 2.5 Add share dialog — search users, set view/edit permission
## 3. Route Forking
- [ ] 3.1 Add "Fork" button on public route detail pages (visible to logged-in users who aren't the owner)
- [ ] 3.2 Create fork API route — copies route + latest GPX to user's collection, sets forkedFromId
- [ ] 3.3 Show "Forked from [original]" link on forked routes
## 4. Spatial Search
- [ ] 4.1 Ensure PostGIS spatial index exists on routes.geometry column
- [ ] 4.2 Create `/routes/explore` route with a full-page map
- [ ] 4.3 Add API route to query public routes within bounding box (ST_Intersects)
- [ ] 4.4 Render matching routes as polylines on the explore map
- [ ] 4.5 Add route popup/sidebar with name, stats, link to detail
## 5. Multi-Day Routes
- [ ] 5.1 Add `isDayBreak` optional boolean support to Planner waypoint Y.Map
- [ ] 5.2 Add day-break toggle in Planner waypoint sidebar (click to mark/unmark)
- [ ] 5.3 Show day boundaries in elevation chart
- [ ] 5.4 Compute per-day distance and elevation in sidebar
- [ ] 5.5 Update GPX export to use one track segment per day
- [ ] 5.6 Store dayBreaks array in route metadata on save
## 6. Activity Photos
- [ ] 6.1 Enable Garage in docker-compose.yml, create bucket
- [ ] 6.2 Create S3 client utility (`apps/journal/app/lib/storage.server.ts`) with presigned URL generation
- [ ] 6.3 Create `journal.activity_photos` table (id, activityId, s3Key, altText, createdAt)
- [ ] 6.4 Add photo upload UI on activity detail/edit page
- [ ] 6.5 Display photo gallery on activity detail page
- [ ] 6.6 Add delete photo functionality
## 7. Contributor Tracking
- [ ] 7.1 Update Planner→Journal callback to include contributor info from JWT
- [ ] 7.2 Store contributor in route_versions on save
- [ ] 7.3 Display contributors on route detail page
## 8. Privacy & i18n
- [ ] 8.1 Update /privacy page: document photo storage, route visibility, sharing
- [ ] 8.2 Add i18n keys for all new UI strings (en + de)
## 9. Verify
- [ ] 9.1 Test route visibility: create private and public routes, verify access control
- [ ] 9.2 Test sharing: share route with another user, verify view/edit access
- [ ] 9.3 Test forking: fork a public route, verify copy created
- [ ] 9.4 Test spatial search: create public routes, verify they appear on explore map
- [ ] 9.5 Test multi-day: mark day breaks, verify per-day stats and GPX export
- [ ] 9.6 Test photos: upload, view, delete photos on activities