From 5b40bd9b0059790945fa1e17187a2a8f3346d0a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 19 Apr 2026 08:13:24 +0200 Subject: [PATCH] Show empty-state CTA on Route detail page when no waypoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Observed: both non-maintainer users (pistazie, nelli) created a route, never planned in it, and stopped. The Route detail page rendered as a dead end — name, a row of buttons, then blank. Changes: - Detect "empty" as no geometry AND no distance (the state every new route has before any planning happens). - When empty, hide the top-row button cluster (Edit in Planner / Edit / Export GPX) and render a centered dashed-border card in its place. - Card shows: large headline, short body (owner-specific vs viewer-only copy), primary "Open in Planner" button (same action as the existing top-row button, just visually prominent), and a secondary "or upload a GPX file" link to the edit page. - Bilingual copy added under routes.empty.*. No non-empty route loses any UI — those paths stay identical. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/journal/app/routes/routes.$id.tsx | 36 +++++++++++++++++++++++++- packages/i18n/src/locales/de.ts | 7 +++++ packages/i18n/src/locales/en.ts | 7 +++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/apps/journal/app/routes/routes.$id.tsx b/apps/journal/app/routes/routes.$id.tsx index 5623e31..821bc1b 100644 --- a/apps/journal/app/routes/routes.$id.tsx +++ b/apps/journal/app/routes/routes.$id.tsx @@ -99,6 +99,11 @@ export default function RouteDetailPage({ loaderData }: Route.ComponentProps) { const [editLoading, setEditLoading] = useState(false); const [highlightedDay, setHighlightedDay] = useState(null); + // A route is "empty" when it has no geometry and no computed distance — + // i.e. nobody has planned any waypoints yet. Surfaced as a dedicated + // empty-state below so the page isn't a dead end. + const isEmpty = !route.geojson && route.distance == null; + const handleEditInPlanner = useCallback(async () => { setEditLoading(true); try { @@ -121,7 +126,7 @@ export default function RouteDetailPage({ loaderData }: Route.ComponentProps) {

{route.description}

)} - {isOwner && ( + {isOwner && !isEmpty && (
+ + {t("routes.empty.uploadGpx")} + +
+ )} + + )} + {versions.length > 0 && (

Version History

diff --git a/packages/i18n/src/locales/de.ts b/packages/i18n/src/locales/de.ts index b4cb12c..87af7f4 100644 --- a/packages/i18n/src/locales/de.ts +++ b/packages/i18n/src/locales/de.ts @@ -179,6 +179,13 @@ export default { noRoutesYet: "Noch keine Routen. Erstelle deine erste Route!", noMapPreview: "Keine Kartenvorschau", saveChanges: "Änderungen speichern", + empty: { + heading: "Diese Route ist leer", + bodyOwner: "Du hast noch keine Wegpunkte geplant. Öffne sie im Planer, um eine Strecke zu skizzieren, oder lade eine GPX-Datei hoch.", + bodyViewer: "Diese Route enthält noch keine Wegpunkte.", + openInPlanner: "Im Planer öffnen", + uploadGpx: "oder eine GPX-Datei hochladen", + }, }, activities: { title: "Aktivitäten", diff --git a/packages/i18n/src/locales/en.ts b/packages/i18n/src/locales/en.ts index f41764d..1560842 100644 --- a/packages/i18n/src/locales/en.ts +++ b/packages/i18n/src/locales/en.ts @@ -179,6 +179,13 @@ export default { noRoutesYet: "No routes yet. Create your first route!", noMapPreview: "No map preview", saveChanges: "Save Changes", + empty: { + heading: "This route is empty", + bodyOwner: "You haven't planned any waypoints yet. Open it in the Planner to sketch a path, or upload a GPX file.", + bodyViewer: "This route doesn't have any waypoints yet.", + openInPlanner: "Open in Planner", + uploadGpx: "or upload a GPX file", + }, }, activities: { title: "Activities",