2026-04-20 20:59:18 +00:00
|
|
|
import { useRef, useEffect, useCallback, useState, forwardRef, useImperativeHandle } from 'react'
|
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
|
|
|
import { MapPin, Building2, Star, Crosshair, Coffee, Fuel, ShoppingBag, Hotel, X, User } from 'lucide-react'
|
2026-04-20 20:59:18 +00:00
|
|
|
import toast from 'react-hot-toast'
|
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
|
|
|
import { useStore } from '../store'
|
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
|
|
|
import { buildAddress } from '../utils/place'
|
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
|
|
|
import { searchGeocode } from '../api'
|
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
|
|
|
import { hasFeature } from '../config'
|
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
|
|
|
|
2026-04-20 20:59:18 +00:00
|
|
|
/** Get category icon based on result type/source */
|
|
|
|
|
function CategoryIcon({ result }) {
|
|
|
|
|
const type = result.type || ''
|
|
|
|
|
const source = result.source || ''
|
|
|
|
|
const size = 14
|
|
|
|
|
|
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
|
|
|
if (result._isContact) return <User size={size} />
|
2026-04-20 20:59:18 +00:00
|
|
|
if (source === 'nickname') return <Star size={size} />
|
|
|
|
|
if (type === 'coordinates') return <Crosshair size={size} />
|
|
|
|
|
if (type === 'locality' || type === 'city') return <Building2 size={size} />
|
|
|
|
|
|
|
|
|
|
// POI subcategories from osm_value if available
|
|
|
|
|
const osmVal = result.raw?.osm_value || ''
|
|
|
|
|
if (osmVal.includes('cafe') || osmVal.includes('coffee')) return <Coffee size={size} />
|
|
|
|
|
if (osmVal.includes('fuel') || osmVal.includes('gas')) return <Fuel size={size} />
|
|
|
|
|
if (osmVal.includes('shop') || osmVal.includes('supermarket')) return <ShoppingBag size={size} />
|
|
|
|
|
if (osmVal.includes('hotel') || osmVal.includes('motel')) return <Hotel size={size} />
|
|
|
|
|
|
|
|
|
|
return <MapPin size={size} />
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const SearchBar = forwardRef(function SearchBar(_, ref) {
|
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
|
|
|
const inputRef = useRef(null)
|
|
|
|
|
const [activeIndex, setActiveIndex] = useState(-1)
|
|
|
|
|
const debounceRef = useRef(null)
|
|
|
|
|
|
2026-04-20 20:59:18 +00:00
|
|
|
useImperativeHandle(ref, () => ({
|
|
|
|
|
focus: () => inputRef.current?.focus(),
|
|
|
|
|
}))
|
|
|
|
|
|
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
|
|
|
const query = useStore((s) => s.query)
|
|
|
|
|
const results = useStore((s) => s.results)
|
|
|
|
|
const searchLoading = useStore((s) => s.searchLoading)
|
|
|
|
|
const autocompleteOpen = useStore((s) => s.autocompleteOpen)
|
|
|
|
|
const stops = useStore((s) => s.stops)
|
2026-04-20 20:59:18 +00:00
|
|
|
const pendingDestination = useStore((s) => s.pendingDestination)
|
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
|
|
|
const contacts = useStore((s) => s.contacts)
|
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
|
|
|
const setQuery = useStore((s) => s.setQuery)
|
|
|
|
|
const setResults = useStore((s) => s.setResults)
|
|
|
|
|
const setSearchLoading = useStore((s) => s.setSearchLoading)
|
|
|
|
|
const setAbortController = useStore((s) => s.setAbortController)
|
|
|
|
|
const setAutocompleteOpen = useStore((s) => s.setAutocompleteOpen)
|
|
|
|
|
const addStop = useStore((s) => s.addStop)
|
2026-04-20 20:59:18 +00:00
|
|
|
const setSelectedPlace = useStore((s) => s.setSelectedPlace)
|
2026-04-26 07:17:33 +00:00
|
|
|
const setClickMarker = useStore((s) => s.setClickMarker)
|
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
|
|
|
const setEditingContact = useStore((s) => s.setEditingContact)
|
2026-04-20 20:59:18 +00:00
|
|
|
const clearPendingDestination = useStore((s) => s.clearPendingDestination)
|
2026-04-26 23:38:31 +00:00
|
|
|
// mapCenter now read directly in api.js
|
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
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
inputRef.current?.focus()
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
const doSearch = useCallback(
|
|
|
|
|
async (q) => {
|
|
|
|
|
const prev = useStore.getState().abortController
|
|
|
|
|
if (prev) prev.abort()
|
|
|
|
|
|
|
|
|
|
if (!q.trim()) {
|
|
|
|
|
setResults([])
|
|
|
|
|
setAutocompleteOpen(false)
|
|
|
|
|
setSearchLoading(false)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// Prepend matching contacts
|
|
|
|
|
let contactResults = []
|
|
|
|
|
if (hasFeature('has_contacts') && contacts.length > 0) {
|
|
|
|
|
const lower = q.trim().toLowerCase()
|
|
|
|
|
contactResults = contacts
|
|
|
|
|
.filter((c) =>
|
|
|
|
|
(c.label || '').toLowerCase().startsWith(lower) ||
|
|
|
|
|
(c.name || '').toLowerCase().startsWith(lower) ||
|
|
|
|
|
(c.call_sign || '').toLowerCase().startsWith(lower)
|
|
|
|
|
)
|
|
|
|
|
.slice(0, 3)
|
|
|
|
|
.map((c) => ({
|
|
|
|
|
lat: c.lat,
|
|
|
|
|
lon: c.lon,
|
|
|
|
|
name: c.label,
|
|
|
|
|
address: c.address || c.name || '',
|
|
|
|
|
type: 'contact',
|
|
|
|
|
source: 'contacts',
|
|
|
|
|
match_code: null,
|
|
|
|
|
raw: { osm_type: c.osm_type, osm_id: c.osm_id, contact: c },
|
|
|
|
|
_isContact: true,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
const ctrl = new AbortController()
|
|
|
|
|
setAbortController(ctrl)
|
|
|
|
|
setSearchLoading(true)
|
|
|
|
|
|
|
|
|
|
try {
|
2026-04-26 23:38:31 +00:00
|
|
|
const data = await searchGeocode(q.trim(), 6, ctrl.signal)
|
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
|
|
|
const combined = [...contactResults, ...(data.results || [])]
|
|
|
|
|
setResults(combined)
|
|
|
|
|
setAutocompleteOpen(combined.length > 0)
|
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
|
|
|
setActiveIndex(-1)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
if (e.name !== 'AbortError') {
|
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
|
|
|
// Still show contacts even if geocode fails
|
|
|
|
|
if (contactResults.length > 0) {
|
|
|
|
|
setResults(contactResults)
|
|
|
|
|
setAutocompleteOpen(true)
|
|
|
|
|
} else {
|
|
|
|
|
setResults([])
|
|
|
|
|
setAutocompleteOpen(false)
|
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
setSearchLoading(false)
|
|
|
|
|
}
|
|
|
|
|
},
|
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
|
|
|
[setResults, setAutocompleteOpen, setSearchLoading, setAbortController, contacts]
|
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
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const handleChange = (e) => {
|
|
|
|
|
const val = e.target.value
|
|
|
|
|
setQuery(val)
|
|
|
|
|
if (debounceRef.current) clearTimeout(debounceRef.current)
|
|
|
|
|
debounceRef.current = setTimeout(() => doSearch(val), 150)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 20:59:18 +00:00
|
|
|
const handleClear = () => {
|
|
|
|
|
setQuery('')
|
|
|
|
|
setResults([])
|
|
|
|
|
setAutocompleteOpen(false)
|
|
|
|
|
inputRef.current?.focus()
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
const selectResult = (result) => {
|
2026-04-20 20:59:18 +00:00
|
|
|
const { pendingDestination: pending } = useStore.getState()
|
|
|
|
|
|
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
|
|
|
// Pure contact (no geo) → open edit modal
|
|
|
|
|
if (result._isContact && result.lat == null) {
|
|
|
|
|
setEditingContact(result.raw.contact)
|
|
|
|
|
setQuery('')
|
|
|
|
|
setResults([])
|
|
|
|
|
setAutocompleteOpen(false)
|
|
|
|
|
setActiveIndex(-1)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 20:59:18 +00:00
|
|
|
if (pending) {
|
|
|
|
|
addStop({ lat: result.lat, lon: result.lon, name: result.name, source: result.source, matchCode: result.match_code })
|
|
|
|
|
addStop({ lat: pending.lat, lon: pending.lon, name: pending.name, source: pending.source, matchCode: pending.matchCode })
|
|
|
|
|
clearPendingDestination()
|
|
|
|
|
toast(`Routing from ${result.name} to ${pending.name}`, { icon: '\u{1F9ED}' })
|
|
|
|
|
} else {
|
|
|
|
|
setSelectedPlace({
|
|
|
|
|
lat: result.lat,
|
|
|
|
|
lon: result.lon,
|
|
|
|
|
name: result.name,
|
|
|
|
|
address: result.address || null,
|
|
|
|
|
type: result.type,
|
|
|
|
|
source: result.source,
|
|
|
|
|
matchCode: result.match_code,
|
|
|
|
|
raw: result.raw || {},
|
|
|
|
|
})
|
2026-04-26 07:17:33 +00:00
|
|
|
// Set click marker for two-click model consistency
|
|
|
|
|
setClickMarker({
|
|
|
|
|
lat: result.lat,
|
|
|
|
|
lon: result.lon,
|
|
|
|
|
circleRadiusPx: 14, // matches preview marker size
|
|
|
|
|
})
|
2026-04-20 20:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
setQuery('')
|
|
|
|
|
setResults([])
|
|
|
|
|
setAutocompleteOpen(false)
|
|
|
|
|
setActiveIndex(-1)
|
|
|
|
|
inputRef.current?.focus()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleKeyDown = (e) => {
|
|
|
|
|
if (!autocompleteOpen || results.length === 0) {
|
2026-04-20 20:59:18 +00:00
|
|
|
if (e.key === 'Escape') setAutocompleteOpen(false)
|
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
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (e.key) {
|
|
|
|
|
case 'ArrowDown':
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
setActiveIndex((prev) => Math.min(prev + 1, results.length - 1))
|
|
|
|
|
break
|
|
|
|
|
case 'ArrowUp':
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
setActiveIndex((prev) => Math.max(prev - 1, -1))
|
|
|
|
|
break
|
|
|
|
|
case 'Enter':
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
if (activeIndex >= 0 && activeIndex < results.length) {
|
|
|
|
|
selectResult(results[activeIndex])
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
case 'Escape':
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
setAutocompleteOpen(false)
|
|
|
|
|
setActiveIndex(-1)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const atCap = stops.length >= 10
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="relative">
|
2026-04-20 20:59:18 +00:00
|
|
|
<div className="relative">
|
|
|
|
|
<input
|
|
|
|
|
ref={inputRef}
|
|
|
|
|
type="text"
|
|
|
|
|
value={query}
|
|
|
|
|
onChange={handleChange}
|
|
|
|
|
onKeyDown={handleKeyDown}
|
|
|
|
|
onFocus={() => results.length > 0 && setAutocompleteOpen(true)}
|
|
|
|
|
placeholder={atCap ? 'Max 10 stops reached' : pendingDestination ? 'Starting point...' : 'Search for a place...'}
|
|
|
|
|
disabled={atCap}
|
|
|
|
|
className="navi-input w-full pr-8"
|
|
|
|
|
aria-label="Search places"
|
|
|
|
|
aria-expanded={autocompleteOpen}
|
|
|
|
|
aria-autocomplete="list"
|
|
|
|
|
role="combobox"
|
|
|
|
|
/>
|
|
|
|
|
{/* Clear / Loading indicator */}
|
|
|
|
|
<div className="absolute right-2.5 top-1/2 -translate-y-1/2">
|
|
|
|
|
{searchLoading ? (
|
|
|
|
|
<div
|
|
|
|
|
className="w-4 h-4 border-2 border-t-transparent rounded-full animate-spin"
|
|
|
|
|
style={{ borderColor: 'var(--accent)', borderTopColor: 'transparent' }}
|
|
|
|
|
/>
|
|
|
|
|
) : query ? (
|
|
|
|
|
<button
|
|
|
|
|
onClick={handleClear}
|
|
|
|
|
className="p-0.5"
|
|
|
|
|
style={{ color: 'var(--text-tertiary)' }}
|
|
|
|
|
aria-label="Clear search"
|
|
|
|
|
>
|
|
|
|
|
<X size={14} />
|
|
|
|
|
</button>
|
|
|
|
|
) : null}
|
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
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Autocomplete dropdown */}
|
|
|
|
|
{autocompleteOpen && results.length > 0 && (
|
|
|
|
|
<ul
|
2026-04-20 20:59:18 +00:00
|
|
|
className="absolute z-50 mt-1 w-full rounded-lg overflow-hidden max-h-72 overflow-y-auto"
|
|
|
|
|
style={{
|
|
|
|
|
background: 'var(--bg-overlay)',
|
|
|
|
|
border: '1px solid var(--border)',
|
|
|
|
|
boxShadow: 'var(--shadow-lg)',
|
|
|
|
|
}}
|
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
|
|
|
role="listbox"
|
|
|
|
|
>
|
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
|
|
|
{results.map((r, i) => {
|
|
|
|
|
const isPoi = r.type === 'poi' && r.raw?.name
|
|
|
|
|
const isContact = r._isContact
|
|
|
|
|
const primary = isContact ? r.name : isPoi ? r.raw.name : r.name
|
|
|
|
|
const secondary = isContact ? (r.address || '') : isPoi ? buildAddress(r) : null
|
|
|
|
|
return (
|
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
|
|
|
<li
|
|
|
|
|
key={`${r.lat}-${r.lon}-${i}`}
|
|
|
|
|
role="option"
|
|
|
|
|
aria-selected={i === activeIndex}
|
2026-04-20 20:59:18 +00:00
|
|
|
className="px-3 py-2 cursor-pointer text-sm"
|
|
|
|
|
style={{
|
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
|
|
|
background: i === activeIndex
|
|
|
|
|
? 'var(--accent-muted)'
|
|
|
|
|
: isContact
|
|
|
|
|
? 'var(--accent-muted)'
|
|
|
|
|
: 'transparent',
|
2026-04-20 20:59:18 +00:00
|
|
|
borderBottom: i < results.length - 1 ? '1px solid var(--border-subtle)' : 'none',
|
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
|
|
|
opacity: isContact && i !== activeIndex ? 0.85 : 1,
|
2026-04-20 20:59:18 +00:00
|
|
|
}}
|
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
|
|
|
onClick={() => selectResult(r)}
|
|
|
|
|
onMouseEnter={() => setActiveIndex(i)}
|
|
|
|
|
>
|
2026-04-20 20:59:18 +00:00
|
|
|
<div className="flex items-center gap-2">
|
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
|
|
|
<span className="shrink-0" style={{ color: isContact ? 'var(--accent)' : 'var(--text-tertiary)' }}>
|
2026-04-20 20:59:18 +00:00
|
|
|
<CategoryIcon result={r} />
|
|
|
|
|
</span>
|
|
|
|
|
<span className="truncate flex-1" style={{ color: 'var(--text-primary)' }}>
|
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
|
|
|
{primary}
|
2026-04-20 20:59:18 +00:00
|
|
|
</span>
|
|
|
|
|
<span className="flex items-center gap-1.5 shrink-0">
|
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
|
|
|
{isContact && (
|
|
|
|
|
<span
|
|
|
|
|
className="text-[10px] px-1.5 py-0.5 rounded font-medium"
|
|
|
|
|
style={{ background: 'var(--accent-muted)', color: 'var(--accent)' }}
|
|
|
|
|
>
|
|
|
|
|
saved
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
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
|
|
|
{r.match_code?.housenumber === 'matched' && (
|
2026-04-20 20:59:18 +00:00
|
|
|
<span
|
|
|
|
|
className="text-[10px] px-1.5 py-0.5 rounded font-medium"
|
|
|
|
|
style={{ background: 'var(--accent-muted)', color: 'var(--accent)' }}
|
|
|
|
|
>
|
|
|
|
|
exact
|
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
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
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
|
|
|
{secondary && (
|
|
|
|
|
<div className="text-[11px] mt-0.5 ml-6 truncate" style={{ color: 'var(--text-tertiary)' }}>
|
|
|
|
|
{secondary}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
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
|
|
|
</li>
|
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
|
|
|
)
|
|
|
|
|
})}
|
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
|
|
|
</ul>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)
|
2026-04-20 20:59:18 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export default SearchBar
|