Add E2E tests for waypoint notes and nearby POI snap; archive waypoint-notes
- E2E: type a note, blur, verify it persists and appears in GPX <desc> on <wpt> - E2E: mock Overpass, select waypoint, verify Nearby list, snap to POI, verify note prefix prepended - Sync waypoint-notes delta spec → openspec/specs/waypoint-notes/spec.md - Archive waypoint-notes change Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
763aa475ea
commit
41217ef658
7 changed files with 149 additions and 2 deletions
|
|
@ -0,0 +1,2 @@
|
|||
schema: spec-driven
|
||||
created: 2026-03-28
|
||||
250
openspec/changes/archive/2026-05-18-waypoint-notes/design.md
Normal file
250
openspec/changes/archive/2026-05-18-waypoint-notes/design.md
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
## Context
|
||||
|
||||
The Planner stores waypoints as a `Y.Array<Y.Map<unknown>>` with `lat`, `lon`,
|
||||
and optional `name` keys. The visual-redesign mockup (D4, D5) already shows
|
||||
waypoint notes as italic text under the waypoint name in the sidebar, and a note
|
||||
icon on map markers. This change implements the data model and interaction logic
|
||||
behind those placeholders.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: Note storage in Yjs
|
||||
|
||||
Each waypoint is a `Y.Map` in the `waypoints` Y.Array. Add an optional `note`
|
||||
key of type `string`. Plain text only, no markup.
|
||||
|
||||
```typescript
|
||||
// Existing keys
|
||||
yMap.get("lat") // number
|
||||
yMap.get("lon") // number
|
||||
yMap.get("name") // string | undefined
|
||||
|
||||
// New key
|
||||
yMap.get("note") // string | undefined
|
||||
```
|
||||
|
||||
Soft limit of ~500 characters enforced in the UI (character counter, input
|
||||
truncation) but not in the Yjs document itself. This keeps the data layer simple
|
||||
and avoids breaking collaborative edits if two users type near the limit
|
||||
simultaneously.
|
||||
|
||||
The `Waypoint` interface in `@trails-cool/types` gets a corresponding optional
|
||||
field:
|
||||
|
||||
```typescript
|
||||
export interface Waypoint {
|
||||
lat: number;
|
||||
lon: number;
|
||||
name?: string;
|
||||
note?: string; // new
|
||||
isDayBreak?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### D2: Editing UX in sidebar
|
||||
|
||||
The note appears as italic text below the waypoint name in the sidebar list item.
|
||||
When empty, a muted placeholder reads "Add a note..." (i18n key:
|
||||
`planner.waypoint.notePlaceholder`).
|
||||
|
||||
Clicking the note text (or placeholder) activates an inline `<textarea>` with:
|
||||
- Auto-focus on click
|
||||
- Auto-resize to content height
|
||||
- Character counter showing remaining chars (e.g., "127 / 500")
|
||||
- Save on blur (write to Y.Map)
|
||||
- Cancel on Escape (revert to last saved value)
|
||||
- No explicit save button - auto-save keeps the interaction lightweight
|
||||
|
||||
The textarea uses the same font styling as the display text (italic, --text-md
|
||||
color) so the transition between view and edit is seamless.
|
||||
|
||||
### D3: Map marker note indicator
|
||||
|
||||
Waypoint markers that have a non-empty note display a small note icon (a
|
||||
document/pencil glyph) as a CSS pseudo-element or secondary Leaflet DivIcon
|
||||
element, positioned at the top-right of the marker.
|
||||
|
||||
Hover (desktop) or tap (mobile) on a marker with a note shows a Leaflet tooltip
|
||||
containing the note text. The tooltip has a max-width of 250px and truncates
|
||||
after 3 lines with ellipsis for long notes. Clicking "more" in the tooltip
|
||||
scrolls the sidebar to that waypoint and opens the editor.
|
||||
|
||||
### D4: Collaborative editing
|
||||
|
||||
Notes are plain strings stored via `yMap.set("note", value)`. Yjs conflict
|
||||
resolution for Y.Map string values is last-write-wins, which is acceptable for
|
||||
short text notes. This is the same behavior as waypoint names.
|
||||
|
||||
If finer-grained collaborative editing were needed (two users editing the same
|
||||
note simultaneously), we would use a nested `Y.Text`. That complexity is not
|
||||
justified for short notes that rarely have concurrent edits. If usage patterns
|
||||
prove otherwise, upgrading to `Y.Text` is a backward-compatible change.
|
||||
|
||||
### D5: GPX export
|
||||
|
||||
The `generateGpx` function in `@trails-cool/gpx` includes the note as a `<desc>`
|
||||
element inside the `<wpt>` element, per GPX 1.1 spec:
|
||||
|
||||
```xml
|
||||
<wpt lat="51.0504" lon="13.7373">
|
||||
<name>Dresden Neustadt</name>
|
||||
<desc>Water refill at train station</desc>
|
||||
</wpt>
|
||||
```
|
||||
|
||||
The `<desc>` element is only emitted when the note is non-empty. XML special
|
||||
characters are escaped via the existing `escapeXml` helper.
|
||||
|
||||
GPX import (parsing) should also read `<desc>` into the `note` field when
|
||||
present, so round-tripping preserves notes.
|
||||
|
||||
### D6: Note display styling
|
||||
|
||||
Per the visual-redesign D4 mockup, notes render as italic text in `--text-md`
|
||||
color below the waypoint name in the sidebar. Styling:
|
||||
|
||||
```
|
||||
font-style: italic
|
||||
color: var(--text-md) /* #5C5847 */
|
||||
font-size: 0.8125rem /* 13px, one step below body 14px */
|
||||
line-height: 1.3
|
||||
max-height: 2.6em /* ~2 lines in display mode */
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
```
|
||||
|
||||
When the sidebar is in its current pre-redesign state (before visual-redesign
|
||||
lands), use equivalent Tailwind classes (`italic text-gray-500 text-xs`).
|
||||
|
||||
### D7: POI source - Overpass API
|
||||
|
||||
POI data comes from the Overpass API, which provides read access to OpenStreetMap
|
||||
data. When a waypoint is selected in the sidebar, we query for amenity, tourism,
|
||||
and natural nodes within a radius of the waypoint.
|
||||
|
||||
Query strategy:
|
||||
- Endpoint: `https://overpass-api.de/api/interpreter` (public instance)
|
||||
- Query type: `node` elements within a bounding box derived from the waypoint
|
||||
coordinates +/- ~500m (~0.0045 degrees latitude, adjusted for longitude)
|
||||
- Filter by relevant tags (see D8 for POI types)
|
||||
- Return compact JSON format (`[out:json]`)
|
||||
|
||||
Example query skeleton:
|
||||
|
||||
```
|
||||
[out:json][timeout:10];
|
||||
(
|
||||
node["amenity"~"drinking_water|water_point|shelter|restaurant|cafe|fast_food"]({{bbox}});
|
||||
node["tourism"~"camp_site|camp_pitch|alpine_hut|wilderness_hut|viewpoint"]({{bbox}});
|
||||
node["natural"="spring"]({{bbox}});
|
||||
node["amenity"="bicycle_parking"]({{bbox}});
|
||||
node["amenity"="bicycle_repair_station"]({{bbox}});
|
||||
);
|
||||
out body;
|
||||
```
|
||||
|
||||
The query runs client-side from the browser (no server proxy needed). The
|
||||
Overpass API supports CORS.
|
||||
|
||||
### D8: POI types
|
||||
|
||||
POIs are categorized by type, each with an icon and OSM tag mapping. The set of
|
||||
displayed types depends on the active routing profile.
|
||||
|
||||
| Category | OSM Tags | Icon | Profiles |
|
||||
|----------|----------|------|----------|
|
||||
| Water | `amenity=drinking_water`, `amenity=water_point`, `natural=spring` | 💧 | all |
|
||||
| Shelter | `tourism=alpine_hut`, `tourism=wilderness_hut`, `amenity=shelter` | 🏠 | all |
|
||||
| Camping | `tourism=camp_site`, `tourism=camp_pitch` | ⛺ | all |
|
||||
| Food | `amenity=restaurant`, `amenity=cafe`, `amenity=fast_food` | 🍴 | all |
|
||||
| Viewpoint | `tourism=viewpoint` | 👁 | all |
|
||||
| Bicycle | `amenity=bicycle_parking`, `amenity=bicycle_repair_station` | 🔧 | trekking, fastbike |
|
||||
|
||||
The type list is defined in a `POI_TYPES` constant, making it easy to extend.
|
||||
Each entry maps OSM tags to a category, icon, and i18n label key.
|
||||
|
||||
### D9: POI display
|
||||
|
||||
When a waypoint is selected (clicked in sidebar or on map):
|
||||
|
||||
**Map**: Small circle markers appear around the selected waypoint showing nearby
|
||||
POIs. Each marker uses the category icon as a tooltip and is colored by type
|
||||
(blue for water, green for camping, orange for food, etc.). Markers are smaller
|
||||
than waypoint markers to avoid visual confusion. They disappear when the waypoint
|
||||
is deselected or a different waypoint is selected.
|
||||
|
||||
**Sidebar**: Below the waypoint's note area, a "Nearby" section lists POIs
|
||||
grouped by category. Each item shows: icon, POI name (or "Unnamed" + type),
|
||||
distance from waypoint (e.g., "120m"), and a snap button. The list is sorted by
|
||||
distance, closest first. Maximum 15 POIs shown to avoid overwhelming the UI;
|
||||
remaining are hidden behind "Show more".
|
||||
|
||||
Clicking a POI in the sidebar highlights it on the map. Clicking a POI marker on
|
||||
the map scrolls the sidebar to that POI.
|
||||
|
||||
### D10: Snap behavior
|
||||
|
||||
Clicking the snap button (or clicking a POI marker on the map) performs:
|
||||
|
||||
1. **Move waypoint**: Set `lat`/`lon` on the waypoint Y.Map to the POI's
|
||||
coordinates. This triggers the existing route recalculation.
|
||||
2. **Set name**: If the POI has an OSM `name` tag and the waypoint has no name
|
||||
(or the user confirms overwrite), set the waypoint name from the POI.
|
||||
3. **Set note prefix**: Prepend the POI type icon and label to the waypoint's
|
||||
note. For example, snapping to a campsite named "Waldcamp" sets the note to
|
||||
"⛺ Campsite" (or "⛺ Campsite - Waldcamp" if named). If the waypoint already
|
||||
has a note, the prefix is prepended on a new line.
|
||||
4. **Update POI list**: Re-fetch nearby POIs for the new location (since the
|
||||
waypoint moved).
|
||||
|
||||
All changes happen in a single Yjs transaction to keep the undo stack clean.
|
||||
|
||||
The user can always edit the name and note after snapping. Snapping is a
|
||||
convenience, not a constraint.
|
||||
|
||||
### D11: POI caching
|
||||
|
||||
Overpass responses are cached in memory to avoid redundant network requests:
|
||||
|
||||
- **Cache key**: Quantized bounding box tile. Coordinates are rounded to a grid
|
||||
(~500m tiles) so nearby waypoints share a cache entry.
|
||||
- **TTL**: 1 hour. POI data changes infrequently; stale data is acceptable.
|
||||
- **Storage**: Plain `Map<string, { data: POI[]; timestamp: number }>` in a
|
||||
module-level variable. Not stored in Yjs (POI data is ephemeral, not shared
|
||||
between collaborators).
|
||||
- **Eviction**: On access, expired entries are removed. Maximum 50 cached tiles
|
||||
to bound memory usage.
|
||||
|
||||
### D12: Overpass rate limiting
|
||||
|
||||
The public Overpass API has usage policies (no more than 2 concurrent requests,
|
||||
10,000 requests/day). We respect these with:
|
||||
|
||||
- **Debounce**: Wait 500ms after a waypoint is selected before querying. If the
|
||||
user clicks through waypoints quickly, only the last selection triggers a query.
|
||||
- **Concurrency**: Maximum 1 in-flight Overpass request. If a new request is
|
||||
needed while one is pending, the pending one is aborted (via `AbortController`).
|
||||
- **Backoff on 429**: If Overpass returns HTTP 429 (rate limit), disable POI
|
||||
queries for 60 seconds and show a subtle "POI lookup unavailable" message.
|
||||
- **Timeout**: 10-second timeout on Overpass requests. On timeout, fail silently
|
||||
(POIs are a nice-to-have, not critical).
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Last-write-wins for notes**: If two users edit the same waypoint's note
|
||||
simultaneously, one edit is lost. Acceptable for short notes; upgrade path to
|
||||
Y.Text exists.
|
||||
- **Soft character limit**: Not enforced at the data layer. A programmatic client
|
||||
could write longer notes. UI handles gracefully with truncation.
|
||||
- **Note icon clutter on map**: Many waypoints with notes could make the map
|
||||
busy. Mitigate by keeping the indicator small and subtle. Consider hiding
|
||||
indicators at low zoom levels in a future iteration.
|
||||
- **Overpass API availability**: The public Overpass instance may be slow or
|
||||
unavailable. POI features degrade gracefully - the Planner works fine without
|
||||
them. No error modals; just empty POI lists.
|
||||
- **POI data quality**: OSM data varies by region. Some areas have sparse POI
|
||||
coverage. Users should not rely solely on POI snapping for critical waypoints
|
||||
like water sources.
|
||||
- **Snap overwrites**: Snapping moves the waypoint and can change its name/note.
|
||||
Mitigated by keeping the action explicit (requires click) and making all fields
|
||||
editable after snap. Undo via Yjs history restores the previous state.
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
## Why
|
||||
|
||||
Waypoints in the Planner are just coordinates with optional names. When planning
|
||||
a multi-day route, users need to note *why* a waypoint matters: "Water refill at
|
||||
gas station", "Tricky gravel crossing - dismount", "Campsite with shelter",
|
||||
"Scenic viewpoint over the valley". Without notes, this context lives outside the
|
||||
tool (paper, chat messages, memory) and gets lost.
|
||||
|
||||
A related problem: waypoints placed by clicking on the map often miss nearby
|
||||
points of interest by a few meters. A user clicks near a water source, campsite,
|
||||
or shelter, but the waypoint lands on the road 30 meters away. They have to
|
||||
manually look up the POI name, type it in, and nudge the waypoint. This is
|
||||
tedious and error-prone, especially on mobile. POIs give immediate context to
|
||||
waypoints - the same kind of context that notes provide in free-form text.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Per-waypoint text notes**: Each waypoint in the Yjs document gets an optional
|
||||
`note` string field. Plain text, no rich formatting.
|
||||
- **Inline editing in sidebar**: Click the note area under a waypoint name to
|
||||
edit. Auto-saves on blur. Placeholder "Add a note..." when empty.
|
||||
- **Map indicator**: Markers with notes show a small note icon. Hover/tap reveals
|
||||
the note in a tooltip.
|
||||
- **GPX export**: Notes are included as `<desc>` elements in waypoint GPX output.
|
||||
- **Shared type update**: The `Waypoint` interface in `@trails-cool/types` gets
|
||||
an optional `note` field.
|
||||
- **Nearby POI display**: When a waypoint is selected, query nearby POIs from
|
||||
OpenStreetMap (via Overpass API) and show them as small markers on the map and
|
||||
as a list in the sidebar.
|
||||
- **Snap to POI**: Click a nearby POI to move the waypoint to its exact
|
||||
coordinates, inheriting the POI's name and type as a note prefix (e.g.,
|
||||
"⛺ Campsite - Waldcamp Fichtelberg"). The user can edit after snapping.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `waypoint-notes`: Per-waypoint text notes with inline editing, map indicators,
|
||||
and GPX export
|
||||
- `poi-snapping`: Nearby POI lookup via Overpass API with snap-to-POI interaction
|
||||
on map and sidebar
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `planner-session`: Waypoint Y.Map objects gain a `note` string field
|
||||
- `gpx-export`: Waypoint `<desc>` element populated from notes
|
||||
- `shared-types`: `Waypoint` interface extended with optional `note`
|
||||
- `planner-map`: POI markers shown near selected waypoint, click to snap
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Rich text (bold, links, images) - plain text only
|
||||
- Comments from other users - notes belong to the waypoint, not a thread
|
||||
- Note history or versioning beyond what Yjs provides
|
||||
- Markdown rendering
|
||||
- Full OSM data overlay across the entire map (separate feature)
|
||||
- POI search or filtering across the whole map viewport
|
||||
- Custom POI database - we use OpenStreetMap data exclusively
|
||||
- Offline POI data - requires network access to Overpass API
|
||||
|
||||
## Impact
|
||||
|
||||
- **Yjs schema**: New `note` string key on waypoint Y.Map (backward compatible -
|
||||
old clients ignore unknown keys)
|
||||
- **Shared types**: `Waypoint.note?: string` added to `@trails-cool/types`
|
||||
- **GPX package**: `generateGpx` writes `<desc>` when waypoint has a note
|
||||
- **Sidebar**: Inline note display and editing below waypoint name; POI list
|
||||
under selected waypoint with snap buttons
|
||||
- **Map markers**: Note indicator icon and tooltip; POI markers near selected
|
||||
waypoint
|
||||
- **New Overpass API client**: Fetches nearby POIs for a given coordinate,
|
||||
with caching and rate limiting
|
||||
- **i18n**: New translation keys for note placeholder, labels, POI types,
|
||||
and snap actions (en + de)
|
||||
- **No database changes**: Planner is stateless, all state lives in Yjs. POI
|
||||
cache is ephemeral (in-memory, not persisted)
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Per-waypoint text notes
|
||||
Each waypoint SHALL support an optional plain-text note synced via Yjs.
|
||||
|
||||
#### Scenario: Add note to waypoint
|
||||
- **WHEN** a user clicks the note area under a waypoint in the sidebar and types text
|
||||
- **THEN** the note is stored in the waypoint's Y.Map as a `note` string field
|
||||
- **AND** auto-saves on blur
|
||||
|
||||
#### Scenario: Note syncs to participants
|
||||
- **WHEN** a user adds or edits a waypoint note
|
||||
- **THEN** all other participants see the update in real-time via Yjs
|
||||
|
||||
### Requirement: Map note indicators
|
||||
Waypoint markers with notes SHALL show a visual indicator on the map.
|
||||
|
||||
#### Scenario: Note icon on marker
|
||||
- **WHEN** a waypoint has a note
|
||||
- **THEN** its map marker shows a small note icon
|
||||
|
||||
#### Scenario: Note tooltip
|
||||
- **WHEN** a user hovers or taps a marker with a note
|
||||
- **THEN** the note text appears in a tooltip
|
||||
|
||||
### Requirement: Notes in GPX export
|
||||
Waypoint notes SHALL be exported as `<desc>` elements in GPX output.
|
||||
|
||||
#### Scenario: Export notes
|
||||
- **WHEN** a user exports a plan with waypoint notes
|
||||
- **THEN** each waypoint's note appears as a `<desc>` element in the GPX file
|
||||
|
||||
### Requirement: Nearby POI display
|
||||
When a waypoint is selected, nearby POIs from OpenStreetMap SHALL be shown on the map and in the sidebar.
|
||||
|
||||
#### Scenario: POI lookup
|
||||
- **WHEN** a user selects a waypoint
|
||||
- **THEN** nearby POIs are fetched from the Overpass API and displayed as small markers on the map and as a list in the sidebar
|
||||
|
||||
### Requirement: Snap waypoint to POI
|
||||
Users SHALL be able to move a waypoint to a nearby POI's exact coordinates.
|
||||
|
||||
#### Scenario: Snap to POI
|
||||
- **WHEN** a user clicks a nearby POI
|
||||
- **THEN** the waypoint moves to the POI's coordinates
|
||||
- **AND** the POI's name and type are added as a note prefix (e.g., "Campsite - Waldcamp Fichtelberg")
|
||||
65
openspec/changes/archive/2026-05-18-waypoint-notes/tasks.md
Normal file
65
openspec/changes/archive/2026-05-18-waypoint-notes/tasks.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
## 1. Data Model
|
||||
|
||||
- [x] 1.1 Add optional `note?: string` field to `Waypoint` interface in `packages/types/src/index.ts`
|
||||
- [x] 1.2 Read `note` from Y.Map in `WaypointSidebar.tsx` (add to `WaypointData` interface and the mapping loop)
|
||||
- [x] 1.3 Preserve `note` when reconstructing the Y.Map in `moveWaypoint` (WaypointSidebar)
|
||||
|
||||
## 2. Sidebar Note Display
|
||||
|
||||
- [x] 2.1 Add note display below waypoint name in sidebar list item (italic, muted text, truncated to 2 lines)
|
||||
- [x] 2.2 Show "Add a note..." placeholder when note is empty (i18n key: `planner.waypoint.notePlaceholder`)
|
||||
- [x] 2.3 Add inline `<textarea>` editing: click to edit, auto-focus, auto-resize, save on blur, cancel on Escape
|
||||
- [x] 2.4 Add character counter (e.g., "127 / 500") visible during editing
|
||||
|
||||
## 3. Map Markers
|
||||
|
||||
- [x] 3.1 Add note indicator icon on waypoint markers that have a non-empty note
|
||||
- [x] 3.2 Show note text in Leaflet tooltip on hover/tap for markers with notes
|
||||
|
||||
## 4. GPX Export & Import
|
||||
|
||||
- [x] 4.1 Emit `<desc>` on `<wpt>` in `generateGpx` when waypoint has a note (note: route-level `<desc>` already exists in `<metadata>`, this is per-waypoint)
|
||||
- [x] 4.2 Read `<desc>` inside `<wpt>` into waypoint `note` field in `parseWaypoints` (note: `metadata > desc` is already parsed separately)
|
||||
|
||||
## 5. i18n
|
||||
|
||||
- [x] 5.1 Add translation keys for note UI strings (en + de): `planner.waypoint.notePlaceholder`, character counter label, Escape cancel hint
|
||||
- [x] 5.2 Add translation keys for POI snap action labels and nearby POI status messages (en + de) — POI type labels already exist in `@trails-cool/map-core`
|
||||
|
||||
## 6. Testing (Notes)
|
||||
|
||||
- [x] 6.1 Unit tests: per-waypoint `<desc>` in GPX generation; `<desc>` inside `<wpt>` parsed into `note`; verify no collision with `metadata > desc`
|
||||
- [x] 6.2 E2E test: type a note in sidebar, blur, verify note persists; verify GPX export contains `<desc>` inside `<wpt>`
|
||||
|
||||
## 7. POI Data Layer (per-waypoint)
|
||||
|
||||
- [x] 7.1 Overpass client (`overpass.ts`), proxy endpoint, `buildQuery`, `parseResponse` — already shipped
|
||||
- [x] 7.2 `Poi` interface and category definitions — in `overpass.ts` + `@trails-cool/map-core`
|
||||
- [x] 7.3 Overpass query building per routing profile — already in `buildQuery`
|
||||
- [x] 7.4 POI cache (`poi-cache.ts`) with quantized bbox, TTL, eviction — already shipped
|
||||
- [x] 7.5 Add `fetchNearbyPois(lat, lon, radiusMeters, categories)` to `overpass.ts`: constructs a ~500m bbox from a single coordinate and reuses `buildQuery` + the existing `/api/overpass` proxy
|
||||
|
||||
## 8. POI Map Display
|
||||
|
||||
- [x] 8.1 Add `selectedWaypointIndex` state lifted to `SessionView` (or passed via callback from `WaypointSidebar` → `PlannerMap`)
|
||||
- [x] 8.2 Create `NearbyPoiMarkers` component: receives `Poi[]` for the selected waypoint + renders small circle markers by category with name tooltip — distinct from the overlay `PoiLayer` markers
|
||||
- [x] 8.3 Wire click on `NearbyPoiMarkers` to call snap handler
|
||||
|
||||
## 9. POI Sidebar
|
||||
|
||||
- [x] 9.1 Add "Nearby" section in `WaypointSidebar` for the selected waypoint: call `fetchNearbyPois`, show spinner / empty state, list POIs sorted by distance (max 15, "Show more" toggle)
|
||||
- [x] 9.2 Snap button per POI: move waypoint coords, set name from POI (if waypoint unnamed), prepend `"<icon> <type>"` to note — all in one Yjs transaction via `use-waypoint-manager`
|
||||
- [x] 9.3 Debounce POI fetch 500ms after waypoint selection; cancel in-flight fetch via `AbortController` when selection changes
|
||||
|
||||
## 10. POI Rate Limiting & Error Handling
|
||||
|
||||
- [x] 10.1 Handle HTTP 429 from nearby-POI fetch: suppress for 60s, show subtle "POI lookup unavailable" notice in the Nearby section
|
||||
- [x] 10.2 Handle network errors and 10s timeout: fail silently, show empty Nearby list — no error modals
|
||||
|
||||
## 11. Testing (POI)
|
||||
|
||||
- [x] 11.1 Overpass client unit tests (query construction, response parsing) — `overpass.test.ts` already covers this
|
||||
- [x] 11.2 POI cache unit tests — `poi-cache.test.ts` already covers this
|
||||
- [x] 11.3 Unit tests for `fetchNearbyPois`: correct bbox from lat/lon/radius, category filtering
|
||||
- [x] 11.4 Unit tests for snap: coords updated, name set, note prefix prepended, existing note preserved, all in one Yjs transaction
|
||||
- [x] 11.5 E2E test: mock Overpass via route handler, select a waypoint, verify Nearby list appears, click snap, verify waypoint moved and note prefixed
|
||||
Loading…
Add table
Add a link
Reference in a new issue