When clicking basemap labels with wikidata IDs (cities, parks, etc),
fetchReverse was returning the nearest POI instead of the clicked
entity, blocking the wikidata fallback that returns correct boundaries.
Changes:
- Effect 1: Skip reverse geocode when wikidataId is present
- Effect 3: Always use wikidata path when available, regardless of
osmType/osmId presence
This fixes missing dashed outline on area feature clicks.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
- Remove redundant "Read more (local)" links from WikiSummarySection
- Summary text now stands alone with population info
- LINKS section now shows Wikipedia/Wikivoyage/Wikidata with brand icons
- Use simple monochrome SVG icons for each wiki service
- Wikipedia and Wikivoyage show (local) indicator when served from Kiwix
- Falls back to wikipedia.org when wiki_url is not available
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When wiki_url is present from the API (has_kiwix_wiki enabled with local
coverage), use it instead of building a wikipedia.org link. Shows article
name with (local) indicator consistent with the Read More link.
Also adds wikivoyage_url link when available.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a place is selected without osm_type/osm_id (e.g., clicking
on a basemap label), trigger a reverse geocode to obtain the OSM
identifiers. This enables fetching place details including boundary,
wiki_summary, and other enriched data.
The existing placeDetails useEffect will then fire once the
osm_type/osm_id are available in the selectedPlace.raw object.
Co-Authored-By: Claude <noreply@anthropic.com>
When place API response includes wiki_summary, display:
- Summary text in PlaceCard and PlaceDetail
- Population with Users icon if wiki_population present
- "Read more" link to wiki_url (local Kiwix article)
- "Travel guide" link if wikivoyage_url present
- (local) indicator on wiki links to signal Kiwix-served content
Gate on has_kiwix_wiki feature flag - no changes when disabled
or when wiki_summary not present in response.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add whiteSpace: nowrap to Get Directions button to prevent text wrap
- Add ScaleControl (imperial units) to bottom-right of map
- Add dark theme styling for scale bar
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
B1: Add ResizeObserver to MapView.jsx to handle layout settling.
The map canvas had 0 height at init because layout hadnt settled
when the useEffect fired. ResizeObserver calls map.resize() on
any container size change.
B11: Use native outpost start URL for login initiation:
/outpost.goauthentik.io/start?rd=%2F
This properly triggers auth flow and redirects to / after login.
Removed the Caddy /login handler that wasnt redirecting correctly.
The /api/auth/whoami endpoint returns JSON after auth, leaving
users on a raw JSON page. The new /login endpoint triggers
forward_auth and redirects to / after successful auth.
- MapView.jsx: extract addBoundaryLayer function, use getComputedStyle
for accent color (MapLibre rejects CSS vars in paint properties)
- PlaceCard.jsx: gate fetchNearbyContacts on auth.authenticated
- PlaceDetail.jsx: gate fetchNearbyContacts on auth.authenticated
- api.js: replace invalid timeout option with AbortSignal.timeout()
- RadialMenu.jsx: remove user-select from SVG style (Firefox rejects)
- Panel.jsx: add Cancel button for pending directions state
- 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.
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