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
This commit is contained in:
Matt 2026-04-26 21:14:39 +00:00
commit 5eb83e9b4b
6 changed files with 626 additions and 100 deletions

View file

@ -5,7 +5,7 @@ import { requestRoute } from './api'
import { decodePolyline } from './utils/decode'
import MapView from './components/MapView'
import Panel from './components/Panel'
import PlaceDetail from './components/PlaceDetail'
import ContactModal from './components/ContactModal'
import LayerControl from './components/LayerControl'
import LocateButton from './components/LocateButton'
@ -88,7 +88,7 @@ export default function App() {
<div className="relative w-screen h-screen overflow-hidden" style={{ background: 'var(--bg-base)' }}>
<MapView ref={mapViewRef} />
<Panel onManeuverClick={handleManeuverClick} />
<PlaceDetail />
<ContactModal />
<LayerControl mapRef={mapViewRef} />
<LocateButton mapRef={mapViewRef} />