Add GPX file import to planner + archive change
Two import entry points: - Home page: "Import GPX" button next to "Start Planning" - In-session: drag-and-drop GPX onto the map (with confirmation) Parses GPX client-side, extracts waypoints (Douglas-Peucker) and no-go areas from extensions. Non-GPX files show error toast. Also: - Fix spec drift: non-GPX drop now shows error toast (was silent) - Add E2E tests for import button, invalid GPX, and session creation - Archive gpx-import-planner change, sync specs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
516b86aa3d
commit
a9f8ee61f0
16 changed files with 394 additions and 175 deletions
|
|
@ -0,0 +1,2 @@
|
|||
schema: spec-driven
|
||||
created: 2026-04-03
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
## Context
|
||||
|
||||
The planner currently receives GPX data only via URL parameters or the journal API. All GPX parsing infrastructure exists (`parseGpxAsync`, `extractWaypoints`, no-go area parsing) but there's no user-facing file upload. Users expect to open a local GPX file directly — standard in every route planning tool.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Let users import a GPX file from the planner home page to start a new session
|
||||
- Let users import a GPX file into an existing session (replacing current waypoints)
|
||||
- Support drag-and-drop onto the map as an alternative to the file picker
|
||||
- Reuse existing GPX parsing, waypoint extraction, and no-go area infrastructure
|
||||
|
||||
**Non-Goals:**
|
||||
- Importing non-GPX formats (KML, GeoJSON, FIT) — future work
|
||||
- Merging imported GPX with existing session data — import replaces
|
||||
- Server-side file storage — GPX is parsed client-side, only waypoints/no-go areas are stored in Yjs
|
||||
|
||||
## Decisions
|
||||
|
||||
**Client-side parsing:** Parse GPX in the browser using `parseGpxAsync` (which uses native `DOMParser`). No need to upload the file to the server. Extract waypoints and no-go areas, then initialize the Yjs session.
|
||||
|
||||
**Two entry points:**
|
||||
1. **Home page:** Upload button next to "Start Planning". Creates a new session with the imported data.
|
||||
2. **Session map:** Drag-and-drop onto the map. Replaces current waypoints and no-go areas after confirmation.
|
||||
|
||||
**Session creation flow (home page):** POST the parsed waypoints and no-go areas to `/api/sessions` (same as the journal handoff), then redirect to the new session URL with data in query params.
|
||||
|
||||
**In-session import (drag-and-drop):** Parse client-side, confirm replacement, then update Yjs arrays directly. No server round-trip needed.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Large GPX files:** Douglas-Peucker runs client-side. Files with 100K+ points may be slow. Acceptable for v1 — optimize later if needed.
|
||||
- **Replacing vs merging:** Import replaces all waypoints/no-go areas. Users might expect to add to existing data. A confirmation dialog mitigates accidental loss.
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
## Why
|
||||
|
||||
The planner has no UI for importing GPX files directly. Users can only get routes into the planner via URL parameters (`/new?gpx=...`) or the journal's "Edit in Planner" handoff. There's no way to open a local GPX file from the planner itself — a basic expectation for any route planning tool.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add a GPX file upload button to the planner home page and session header
|
||||
- When a GPX file is uploaded, create a new session with waypoints extracted from the track (via Douglas-Peucker) and no-go areas from extensions
|
||||
- Support drag-and-drop of GPX files onto the map
|
||||
- Reuse existing `parseGpxAsync`, `extractWaypoints`, and no-go area parsing infrastructure
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `gpx-import`: GPX file import UI in the planner (upload button, drag-and-drop, file parsing, session creation)
|
||||
|
||||
### Modified Capabilities
|
||||
- `planner-session`: Session can now be initialized from a GPX file upload (not just URL params)
|
||||
- `planner-journal-handoff`: The "Export Plan" → reimport flow is now a first-class UI action
|
||||
|
||||
## Impact
|
||||
|
||||
- `apps/planner/app/routes/home.tsx` — add upload button
|
||||
- `apps/planner/app/components/PlannerMap.tsx` — drag-and-drop zone
|
||||
- `apps/planner/app/routes/new.tsx` — handle file upload POST
|
||||
- `packages/i18n/src/locales/` — new translation keys
|
||||
- `e2e/planner.test.ts` — new E2E tests for file import
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Import GPX from home page
|
||||
Users SHALL be able to import a GPX file from the planner home page to start a new planning session.
|
||||
|
||||
#### Scenario: Upload GPX via file picker
|
||||
- **WHEN** a user clicks the "Import GPX" button on the home page and selects a GPX file
|
||||
- **THEN** the file is parsed client-side using `parseGpxAsync`
|
||||
- **AND** waypoints are extracted via `extractWaypoints` (Douglas-Peucker for single-segment tracks)
|
||||
- **AND** no-go areas are extracted from GPX extensions if present
|
||||
- **AND** a new session is created with the extracted data
|
||||
- **AND** the user is redirected to the new session
|
||||
|
||||
#### Scenario: Invalid GPX file
|
||||
- **WHEN** a user uploads a file that is not valid GPX
|
||||
- **THEN** an error message is shown
|
||||
- **AND** no session is created
|
||||
|
||||
### Requirement: Import GPX via drag-and-drop
|
||||
Users SHALL be able to drag a GPX file onto the map in an existing session.
|
||||
|
||||
#### Scenario: Drop GPX on map
|
||||
- **WHEN** a user drags a `.gpx` file onto the map area
|
||||
- **THEN** a visual drop zone indicator appears
|
||||
- **AND** on drop, the file is parsed client-side
|
||||
- **AND** a confirmation dialog asks whether to replace the current route
|
||||
- **AND** on confirm, the session's waypoints and no-go areas are replaced with the imported data
|
||||
|
||||
#### Scenario: Cancel import
|
||||
- **WHEN** a user drops a GPX file and the confirmation dialog appears
|
||||
- **THEN** clicking "Cancel" leaves the session unchanged
|
||||
|
||||
### Requirement: Non-GPX files are rejected
|
||||
#### Scenario: Drop non-GPX file
|
||||
- **WHEN** a user drops a non-GPX file on the map
|
||||
- **THEN** the file is ignored with a brief error toast
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Export Plan reimport
|
||||
The "Export Plan" GPX can now be reimported directly in the planner via the file upload UI, completing the round-trip without needing the journal.
|
||||
|
||||
#### Scenario: Reimport exported plan
|
||||
- **WHEN** a user exports a plan and later imports it via the planner's GPX upload
|
||||
- **THEN** waypoints, no-go areas, and track data are restored from the GPX
|
||||
- **AND** BRouter re-routes between the imported waypoints with the imported no-go areas active
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Session initialization
|
||||
Sessions can now be initialized from a GPX file upload in addition to URL parameters and the journal handoff.
|
||||
|
||||
#### Scenario: Session created from GPX upload
|
||||
- **WHEN** a session is created via GPX file upload on the home page
|
||||
- **THEN** waypoints and no-go areas from the GPX are passed via URL parameters to the session page
|
||||
- **AND** the Yjs document is initialized with the extracted data on the client side
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
## 1. Home Page Import
|
||||
|
||||
- [x] 1.1 Add "Import GPX" button next to "Start Planning" on the planner home page
|
||||
- [x] 1.2 Add hidden file input (`accept=".gpx"`) triggered by the button
|
||||
- [x] 1.3 On file select: parse GPX client-side with `parseGpxAsync`, extract waypoints and no-go areas
|
||||
- [x] 1.4 POST extracted data to `/api/sessions`, redirect to new session with waypoints + no-go areas in URL params
|
||||
- [x] 1.5 Show error toast if GPX parsing fails
|
||||
|
||||
## 2. Drag-and-Drop Import
|
||||
|
||||
- [x] 2.1 Add drag-and-drop zone to `PlannerMap` (listen for `dragenter`, `dragover`, `drop` on map container)
|
||||
- [x] 2.2 Show visual overlay when a file is dragged over the map ("Drop GPX file here")
|
||||
- [x] 2.3 On drop: validate file extension is `.gpx`, reject others with error toast
|
||||
- [x] 2.4 Parse dropped GPX file client-side
|
||||
- [x] 2.5 Show confirmation dialog ("Replace current route with imported GPX?")
|
||||
- [x] 2.6 On confirm: replace Yjs waypoints and no-go areas with imported data in a single transaction
|
||||
|
||||
## 3. i18n
|
||||
|
||||
- [x] 3.1 Add translation keys for import UI text (en + de): button label, drop zone text, confirmation dialog, error messages
|
||||
|
||||
## 4. Testing
|
||||
|
||||
### Unit tests
|
||||
- [x] 4.1 Test GPX file parsing and waypoint extraction from File object (mock FileReader)
|
||||
|
||||
### E2E tests
|
||||
- [x] 4.2 Home page import: upload GPX file via file input → session created with waypoints
|
||||
- [x] 4.3 Drag-and-drop: drop GPX on map → waypoints replaced (Playwright file drop)
|
||||
- [x] 4.4 Invalid file: upload non-GPX → error toast shown, no session created
|
||||
- [x] 4.5 Plan round-trip: export plan → reimport via upload → waypoints and no-go areas match
|
||||
Loading…
Add table
Add a link
Reference in a new issue