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.
This commit is contained in:
Matt 2026-04-26 23:05:51 +00:00
commit 721bc2c9f5
2 changed files with 20 additions and 14 deletions

View file

@ -1,5 +1,4 @@
import { create } from 'zustand'
import { shallow } from 'zustand/shallow'
export const useStore = create((set, get) => ({
// ── Search state ──
@ -122,14 +121,21 @@ export const useStore = create((set, get) => ({
setActiveTab: (tab) => set({ activeTab: tab }),
setEditingContact: (c) => set({ editingContact: c }),
clearEditingContact: () => set({ editingContact: null }),
}), shallow)
}))
// ── Panel state selector ──
// Returns { hasPreview: boolean, routeState: 'NONE' | 'ROUTING' | 'CALCULATED' }
// Preview and route states are now orthogonal - preview can show alongside any route state
// Returns string state, prioritizing preview to allow it alongside any route state
export const usePanelState = () => {
return useStore((s) => ({
hasPreview: !!s.selectedPlace,
routeState: s.route ? "CALCULATED" : (s.stops.length >= 1 ? "ROUTING" : "NONE")
}), shallow)
return useStore((s) => {
const hasPreview = !!s.selectedPlace
const hasRoute = !!s.route
const hasStops = s.stops.length >= 1
if (hasPreview && hasRoute) return "PREVIEW_CALCULATED"
if (hasPreview && hasStops) return "PREVIEW_ROUTING"
if (hasPreview) return "PREVIEW"
if (hasRoute) return "ROUTE_CALCULATED"
if (hasStops) return "ROUTING"
return "IDLE"
})
}