Add Send to Wahoo UI, i18n, and privacy disclosure

Route detail page now renders one of three states for the owner of a
route with a Wahoo connection:

- "Send to Wahoo" button + privacy tooltip when no successful push
  exists for the current version, plus the inline last-error blurb
  if the previous attempt failed
- "Sent to Wahoo on <date>" pill when sync_pushes has a pushedAt
  for the current version
- Banner row at the top mapping the ?push= and ?code= query params
  the action route appends to user-facing copy

i18n strings cover all 8 banner states (success, needs_permission,
no_connection, no_geometry, validation, rate_limit, token_expired,
generic) in English and German.

Privacy disclosure lives at /legal/privacy (apps/journal/app/routes/
legal.privacy.tsx), the in-app surface this repo uses instead of a
docs/privacy.md manifest. The new bullet declares that route
geometry, name, and description are transmitted to Wahoo on opt-in
via the Send to Wahoo button.

E2E tests for the push and re-auth flows (tasks 9.4/9.5) are
deferred — they need a server-side Wahoo mock harness that doesn't
exist yet. Slice-4 unit tests cover the pipeline against the
provider mock.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-04-30 22:44:50 +02:00
parent a9c0093877
commit 76c3e49de2
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
5 changed files with 168 additions and 13 deletions

View file

@ -204,6 +204,21 @@ export default {
importGpx: "GPX importieren",
exportGpx: "GPX exportieren",
delete: "Route löschen",
sendToWahoo: "An Wahoo senden",
sendToWahooHelp: "Sendet die Route an Wahoo, damit sie auf deinem Bike-Computer erscheint.",
sentToWahoo: "Am {{date}} an Wahoo gesendet",
sendToWahooFailed: "Letzter Versuch fehlgeschlagen: {{error}}",
sendToWahooSending: "Sende…",
sendToWahooBanner: {
success: "Route an Wahoo gesendet. Schau in der Wahoo-App oder auf deinem Bike-Computer nach.",
needsPermission: "Wir brauchen deine Erlaubnis, Routen an Wahoo zu senden. Bitte neu verbinden.",
noConnection: "Verbinde zuerst dein Wahoo-Konto in den Einstellungen.",
noGeometry: "Diese Route hat noch keine Geometrie — plane zuerst einen Weg.",
validation: "Senden fehlgeschlagen — versuche, Name oder Beschreibung anzupassen.",
rateLimit: "Zu viele Anfragen an Wahoo. Bitte in einer Minute erneut versuchen.",
tokenExpired: "Deine Wahoo-Verbindung ist abgelaufen. Bitte in den Einstellungen neu verbinden.",
generic: "Senden an Wahoo fehlgeschlagen. Bitte später erneut versuchen.",
},
distance: "Strecke",
elevationGain: "Höhenmeter",
dayBreakdown: "Tagesübersicht",

View file

@ -204,6 +204,21 @@ export default {
importGpx: "Import GPX",
exportGpx: "Export GPX",
delete: "Delete Route",
sendToWahoo: "Send to Wahoo",
sendToWahooHelp: "This sends your route to Wahoo so it appears on your bike computer.",
sentToWahoo: "Sent to Wahoo on {{date}}",
sendToWahooFailed: "Last attempt failed: {{error}}",
sendToWahooSending: "Sending…",
sendToWahooBanner: {
success: "Route sent to Wahoo. Check your Wahoo App or bike computer.",
needsPermission: "We need your permission to send routes to Wahoo. Please reconnect.",
noConnection: "Connect your Wahoo account in Settings first.",
noGeometry: "This route has no geometry yet — plan a path before sending.",
validation: "We couldn't send this route — try editing the name or description.",
rateLimit: "Too many requests to Wahoo. Try again in a minute.",
tokenExpired: "Your Wahoo connection expired. Please reconnect in Settings.",
generic: "Couldn't send to Wahoo. Try again later.",
},
distance: "Distance",
elevationGain: "Elevation Gain",
dayBreakdown: "Day Breakdown",