| 6 | * Simplified version of useDispatch for read-only scene rendering. |
| 7 | */ |
| 8 | export function useMapData() { |
| 9 | const [zones, setZones] = useState<Zone[]>([]); |
| 10 | const [loading, setLoading] = useState(false); |
| 11 | const [error, setError] = useState<string | null>(null); |
| 12 | |
| 13 | const getDispatchUrl = useCallback((path: string) => { |
| 14 | if (window.location.hostname === "localhost") { |
| 15 | // Dev mode: connect to depot directly |
| 16 | const depotHost = import.meta.env.VITE_DEPOT_HOST || "depot"; |
| 17 | return `http://${depotHost}:4890${path}`; |
| 18 | } |
| 19 | // Production: route through nginx reverse proxy |
| 20 | const protocol = window.location.protocol; |
| 21 | return `${protocol}//${window.location.host}/api/dispatch${path}`; |
| 22 | }, []); |
| 23 | |
| 24 | const fetchZones = useCallback(async () => { |
| 25 | setLoading(true); |
| 26 | try { |
| 27 | const url = getDispatchUrl("/zones"); |
| 28 | const response = await fetch(url); |
| 29 | if (!response.ok) { |
| 30 | throw new Error(`Failed to fetch zones: ${response.statusText}`); |
| 31 | } |
| 32 | const data = await response.json(); |
| 33 | setZones(data); |
| 34 | setError(null); |
| 35 | } catch (e) { |
| 36 | console.warn("[useMapData] Failed to fetch zones:", e); |
| 37 | setError(e instanceof Error ? e.message : "Failed to fetch zones"); |
| 38 | } finally { |
| 39 | setLoading(false); |
| 40 | } |
| 41 | }, [getDispatchUrl]); |
| 42 | |
| 43 | // Fetch zones on mount |
| 44 | useEffect(() => { |
| 45 | fetchZones(); |
| 46 | }, [fetchZones]); |
| 47 | |
| 48 | return { |
| 49 | zones, |
| 50 | loading, |
| 51 | error, |
| 52 | refetch: fetchZones, |
| 53 | }; |
| 54 | } |