Replace blank home page with a landing page explaining collaborative
route planning. Hero section with "Start Planning" CTA, 5 feature cards
(collaboration, routing, elevation, GPX, no account), secondary CTA
linking to Journal, and footer with privacy/source/attribution links.
All strings in en + de via i18n.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Set Sentry user context (id, username) in Journal root for all routes
- Tag Planner errors with session_id
- Use reactRouterV7BrowserTracingIntegration for route-aware traces
- Hidden source maps (no sourceMappingURL in bundles, .map deleted after
upload to Sentry)
- Privacy manifest at /privacy documenting all data collection
- robots.txt blocking all bots on both apps
- Suppress i18next promotional console log
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Initialize i18n via initI18n() in both root.tsx files and replace all
hardcoded user-facing strings with useTranslation() calls. Adds missing
translation keys for planner (profiles, connection states, save flow)
and journal (auth pages, route management, passkey prompts).
Both English and German translations are complete.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix createRequestHandler → createRequestListener (@react-router/node
removed the old API)
- Move noEmit + allowImportingTsExtensions to tsconfig.base.json so all
packages and apps inherit them consistently
- Add explicit .ts extensions to all remaining relative imports across
packages and apps
Verified locally: node --experimental-strip-types server.ts starts and
responds 200.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
React Router's data() throw creates a DataWithResponseInit object
(type + data + init), not a Response or ErrorResponseImpl.
Check for type === "DataWithResponseInit" to re-throw correctly.
Verified: nonexistent session → 404, DB down → 503.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both apps now have proper ErrorBoundary exports in root.tsx:
- 404: "Page not found"
- 503: "Service temporarily unavailable"
- Other: shows error message
Simplified withDb re-throw check: anything with a "status" property
is treated as a React Router response (covers Response and
ErrorResponseImpl). DB errors throw a plain Response(503) that
the error boundary renders nicely.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
React Router's data() throws ErrorResponseImpl, not Response.
Check for objects with status + data properties to catch both
Response and ErrorResponseImpl, preventing withDb from
swallowing 404s as 503s.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous version checked for specific error messages that
didn't match Drizzle's actual error text ("Failed query").
Now any error from a withDb-wrapped handler returns 503.
React Router Response objects are still re-thrown.
Verified: DB down → 503 JSON, DB up → 201, home page always 200.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shared withDb() wrapper in @trails-cool/db catches database
connection errors and throws a 503 Response instead of crashing
the server. Re-throws React Router responses (redirects, data()).
Applied to Planner session routes — replaces manual try/catch.
Any route can use withDb(() => ...) for automatic 503 on DB failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three issues fixed:
1. GPX parser used browser DOMParser which doesn't exist in Node/Vite SSR.
Added async parseGpxAsync() using linkedom for server-side parsing.
2. Server-side session initialization stored waypoints in a Yjs doc
instance separate from the Vite WebSocket plugin's doc store.
Moved waypoint initialization to client-side: API returns parsed
waypoints, client adds them to Yjs after sync.
3. GPX was encoded in URL params causing HTTP 431. Now the Journal
creates a Planner session via API (POST body), and only passes
compact waypoint coordinates in URL params.
Verified: Journal route → Edit in Planner → 4 waypoints loaded,
route computed (79.3km), elevation profile, Save to Journal ready.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Passkey (WebAuthn) as primary auth for registration and login
- Magic link email as fallback for new devices
- Removed password_hash from users table
- Added credentials (WebAuthn) and magic_tokens tables
- Updated tasks (7.1-7.10)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Blue sliding progress bar appears at the top of the map while
BRouter is computing a route. Overlays on top of Leaflet map
with z-index 1000.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Vitest: unit tests for packages, components, and app logic (jsdom env,
@testing-library/react, co-located test files)
- Playwright: E2E browser tests per app (journal.test.ts, planner.test.ts),
auto-starts dev servers, scoped via testMatch
- Sample unit test for types package
- Smoke E2E tests for both apps
- Updated CLAUDE.md with test commands and strategy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>