Commit graph

19 commits

Author SHA1 Message Date
816ea8dd1f feat: wilderness maneuvers, pick-from-map, distance formatting, place card panel
- Wilderness maneuvers render with compass arrows and cardinal directions
- Network maneuvers prefixed with transport mode (Drive/Walk/Ride)
- Distances under 1 mile show feet with commas
- Pick-from-map mode replaces auto-fill-on-focus (crosshair + toast)
- ESC cancels pick mode
- Place card slides out right during active routing
- Removed debug toasts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-09 06:09:14 +00:00
7523ddd0a2 feat: add directions panel with editable origin/destination inputs
New UX for Get Directions:
- DirectionsPanel component with two stacked input fields
- LocationInput component with autocomplete, coordinate parsing
- Swap button to flip origin/destination
- Travel mode selector (Drive default, Foot, MTB, ATV, 4x4)
- Boundary selector (only visible for non-Drive modes)
- Map click fills active input field with crosshair cursor
- Auto-route when both endpoints are filled
- X button closes directions and returns to search view

Store changes:
- directionsMode state for panel switching
- activeDirectionsField for map click targeting
- startDirections now enters directions mode with destination pre-filled

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-08 22:44:45 +00:00
09d68adf09 feat: unified routing with Drive mode default and Add stop wedge
- Add Drive (auto) as default route mode, first in travel modes list
- Hide boundary mode selector when Drive mode is active
- Restore Add stop radial menu wedge with stops system integration
- Unify routing through single computeRoute() function in store
- Add coordinate parsing to SearchBar for direct lat/lon input
- Bridge stops system with routeStart/routeEnd for seamless UX
- Support 3+ stops with Valhalla optimization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-08 21:59:10 +00:00
d6aa125215 feat: unified routing UI with wilderness + network segments
- Single routing system (removed duplicate Valhalla-only flow)
- Unified radial menu: From here, To here, Clear, Save, Measure
- Removed "Offroute" section from panel (single directions display)
- Better error messages without technical "Offroute" prefix
- ManeuverList shows wilderness + network breakdown
- PlaceCard integration for previews

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-08 19:03:44 +00:00
869391ee4e fix(ux): Three UX improvements for feature selection
Fix 1: Never zoom out when clicking a feature - preserves user's
intentional zoom level by checking cameraForBounds before fitBounds

Fix 2: Single-click to switch between features - clicking outside
the current feature's circle clears selection and selects new feature

Fix 3: View mode toggle reflects saved state on load - initialize
viewMode from localStorage on store creation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-02 16:57:32 +00:00
e786bb8870 feat: Add satellite imagery with Map/Satellite/Hybrid view modes
- Add viewMode state to store with localStorage persistence
- Add satellite layer functions to MapView (ESRI World Imagery via nginx proxy)
- Add view mode segmented control in LayerControl popover
- Add view-mode-control CSS styles
- Hide/show vector fills and lines based on view mode

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-02 02:01:56 +00:00
ac7cec972f fix(map): call updateBoundary directly, remove useEffect
The useEffect-based boundary rendering was unreliable due to React's
state lifecycle - the effect would fire before boundary data arrived
from the API, then not re-trigger properly when data was populated.

New approach:
- Remove the boundary useEffect entirely
- Define updateBoundary function in map load handler
- Store function reference in Zustand store and local ref
- PlaceCard calls updateBoundary(geometry) directly when API returns
- Click handlers call updateBoundary(null) to clear

This bypasses React's render cycle - the map library handles its own
state and we tell it what to draw when we have the data.

Test sequence:
- Click Twin Falls → boundary shows on first click
- Click Kimberly → boundary shows on first click
- Switch between them → old clears, new shows
- Click empty map → boundary clears

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-29 23:23:11 +00:00
99bd2218a4 feat: geocode search + map pick for contact location 2026-04-28 23:05:28 +00:00
0d4a807a05 feat: add auth-state awareness and graceful degradation
- Add /api/auth/whoami endpoint check on app load
- Store auth state in Zustand (authenticated, username, loaded)
- Hide Contacts tab when unauthenticated
- Gate fetchNearbyContacts calls on auth.authenticated
- Replace Save button with Log in affordance when unauthenticated
- Add Login/Logout buttons to panel header
- Prevent any /api/contacts/* requests from firing when unauthenticated

Public functionality (search, routing, place details) remains
fully functional for unauthenticated users.
2026-04-27 01:32:00 +00:00
9db8cec0f6 fix: duplicate stop when GPS denied and selecting origin via search
Bug: clicking Get Directions with GPS denied would add the destination
to stops AND set pendingDestination. Then when user selected an origin
via search, selectResult would add the origin AND re-add the pending
destination, resulting in 3 stops (A=dest, B=origin, C=dest again).

Fix: in startDirections GPS-denied path, only set pendingDestination
without adding to stops. The SearchBar selectResult handler already
adds both origin and pending destination when pending exists.
2026-04-26 23:07:59 +00:00
721bc2c9f5 fix: usePanelState returns string, preview decoupled from route
Fixes React error #185 (infinite re-render loop) caused by returning
object from Zustand selector without shallow comparison.

Changed usePanelState to return string states that encode both preview
and route status:
- PREVIEW_CALCULATED: preview + calculated route
- PREVIEW_ROUTING: preview + stops (no route yet)
- PREVIEW: preview only
- ROUTE_CALCULATED: calculated route only
- ROUTING: stops only
- IDLE: nothing

Panel.jsx updated to derive show flags from string states using
startsWith and includes checks.
2026-04-26 23:05:51 +00:00
f5e0b9606e fix: search viewport init, theme-clears-route bug, preview-during-route
Three regressions fixed:

1. mapCenter is now initialized on map 'load' event, not just 'moveend'.
   Searches immediately after page load now correctly include viewport
   bias instead of falling back to default Twin Falls coords.

2. setThemeOverride had stray code from startDirections that wiped
   stops and added undefined place data. Toggling theme cleared the
   active route. Restored setThemeOverride to its correct
   theme-only implementation.

3. usePanelState returned ROUTE_CALCULATED before checking selectedPlace,
   so preview cards could never appear alongside a calculated route.
   Refactored to decouple preview state from route state - preview
   renders whenever selectedPlace exists, independent of route state.
2026-04-26 22:21:23 +00:00
5eb83e9b4b feat(panel): single-panel architecture with UX refinements
Major refactor consolidating two-panel layout (Routes/Contacts + floating
PlaceDetail) into one 400px left column with state-driven content.

Architecture:
- New PlaceCard component for preview and stop cards (collapsible)
- Panel states: IDLE, PREVIEW, ROUTING, PREVIEW_ROUTING, ROUTE_CALCULATED
- usePanelState selector in store.js derives state from selectedPlace/stops/route
- StopList now renders stops as PlaceCard with variant=stop
- PlaceDetail.jsx removed from App.jsx (content moved to PlaceCard)

UX refinements:
- Panel width 400px (was 360px) to fit buttons on one line
- Map zoom padding updated to 420px for wider panel
- Body text bumped to text-sm (14px) for readability
- Get Directions button hidden when 2+ stops (route auto-calculates)
- PlaceCard title prefers feature name (raw.name) over formatted address
- Preview card shows above route during PREVIEW_ROUTING state
- Directions flow no longer shows toast when GPS denied
2026-04-26 21:14:39 +00:00
b354fd0aa0 feat: Consolidated UX improvements for map selection
- Snap selection to feature geometry when clicking labeled places
- Add wikidata enrichment for basemap labels (population, description)
- Differentiate visual feedback: reticle marker vs pulsing highlight
- Clear previous feature highlight when selection changes
- Store selection mode (reticle/feature) and feature info in state

Frontend: MapView click handler, PlaceDetail wikidata fetch, CSS
Backend: /api/place/wikidata/<id> route for Wikidata API lookups

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-26 08:15:09 +00:00
37a5eb5b1b feat(map): two-click selection model with precise center dot
Replaces 'every click selects something' with a deliberate two-click
flow:
- First click drops marker (existing circle plus new precise center dot)
  and opens place panel
- Second click INSIDE the marker circle opens the radial menu
- Second click OUTSIDE the circle deselects without selecting the new
  spot — requires another click to select

The 4px filled center dot at exact click coordinates gives precise
visual feedback for GPS-coord readout. The existing circle's radius
defines the same-spot tolerance, visually showing the radial-trigger
hit area.

Right-click radial unchanged. Search-dropdown selection drops a marker
for consistency.
2026-04-26 07:17:33 +00:00
b12ebe672e feat(search): add viewport bias for location-aware geocoding
- Add mapCenter state to store (lat/lon/zoom)
- Track map center on moveend in MapView
- Pass mapCenter to searchGeocode from SearchBar
- Update searchGeocode API call to include viewport params

Search results now prioritize locations near the current map view.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-26 04:03:52 +00:00
3ce860c1e8 Add contacts/phone book UI with search integration
New components:
- ContactModal.jsx: Save/edit overlay with form fields and soft delete
- ContactList.jsx: Contacts tab with filter, create, and tap-to-navigate

Modified:
- store.js: Add contacts slice (contacts, activeTab, editingContact)
- api.js: Add contacts API functions (fetch, create, update, delete, nearby)
- config.js: Add has_contacts fallback flag
- Panel.jsx: Routes/Contacts tab bar (only when has_contacts enabled)
- PlaceDetail.jsx: Save button opens ContactModal, proximity annotation
- SearchBar.jsx: Prepend matching contacts before Photon results
- App.jsx: Render ContactModal at top level
- index.css: Modal overlay, tab bar, contact list item styles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-22 05:30:19 +00:00
Ubuntu
02f2b25db3 feat(navi): GPS origin + place detail panel + basic actions
Adds synthetic "Your location" stop A when GPS granted; place
detail panel slides in on search result click with Directions /
Add stop / Save (stub) / Share actions; elevation via Valhalla
/height; react-hot-toast for feedback; pendingDestination state
for GPS-denied Directions flow.

Phase 3 Step 5 C1 of Navi.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 20:59:18 +00:00
Ubuntu
e7b08a7dc9 feat: search, multi-stop routing, and route display
Full navigation UI with:
- Search bar with 150ms debounced autocomplete from /api/geocode
- Keyboard navigation (arrow keys, Enter, Escape)
- Exact match badge for verified address results
- Multi-stop list with drag-to-reorder (dnd-kit)
- 10-stop cap with disabled state
- Mode selector (drive/walk/bike)
- Valhalla route display with per-leg color polyline
- Maneuver list with instructions, distance, time remaining
- Click maneuver to fly map to that point
- Optimize stops button (3+ stops, uses /optimized_route)
- Responsive: side panel (desktop ≥768px), bottom sheet (mobile)
- Stop pins: green origin, red destination, blue intermediate
- Pin popup with remove button
- Geolocation permission requested on first route, not on load
- Error handling for unroutable pairs
- nginx proxy for /api/ and /valhalla/ endpoints

Dependencies added: zustand, @dnd-kit/core, @dnd-kit/sortable,
@dnd-kit/utilities

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 16:50:53 +00:00