'use client' import { useCallback, useEffect, useState } from 'react' import { Button } from '@/components/ui/button' import { NodeGraph } from '@/components/ui/node-graph' import type { WorldState } from '@/lib/game/save' // WORLD MAP tab — the first live game panel: renders real world state from /api/world and // offers a jump button per system adjacent to the current one (NodeGraph nodes are not // clickable upstream, so the affordance lives in this list). Turn-based: fetch on mount, // refetch via the jump response — no polling. interface WorldMapPanelProps { height?: number } export function WorldMapPanel({ height = 380 }: WorldMapPanelProps) { const [world, setWorld] = useState(null) const [error, setError] = useState(null) const [jumpingId, setJumpingId] = useState(null) useEffect(() => { let cancelled = false fetch('/api/world') .then(async (res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`) return (await res.json()) as WorldState }) .then((state) => { if (!cancelled) setWorld(state) }) .catch((err: unknown) => { if (!cancelled) setError(err instanceof Error ? err.message : String(err)) }) return () => { cancelled = true } }, []) const jump = useCallback(async (systemId: number) => { setJumpingId(systemId) try { const res = await fetch('/api/jump', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ systemId }), }) const body = (await res.json()) as WorldState & { error?: string } if (!res.ok) throw new Error(body.error ?? `HTTP ${res.status}`) setWorld(body) // fresh state straight from the response — no refetch needed } catch (err: unknown) { setError(err instanceof Error ? err.message : String(err)) } finally { setJumpingId(null) } }, []) if (error && !world) { return (
CHART OFFLINE // {error.toUpperCase()}
) } if (!world) { return (
SCANNING CHART…
) } const current = world.systems.find((s) => s.current)! const reachable = world.edges .filter(([a, b]) => a === current.id || b === current.id) .map(([a, b]) => (a === current.id ? b : a)) .map((id) => world.systems.find((s) => s.id === id)!) return (
({ id: String(s.id), label: s.name, x: s.x, y: s.y, status: s.current ? 'ACTIVE' : s.discovered ? 'NEUTRAL' : 'OFFLINE', sublabel: s.current ? 'CURRENT' : s.discovered ? 'DISCOVERED' : 'UNKNOWN', }))} edges={world.edges.map(([a, b]) => ({ from: String(a), to: String(b) }))} variant="ACTIVE" title={`GALACTIC CHART // JUMP ROUTES — TURN ${world.turn}`} height={height} style={{ width: '100%' }} />
JUMP ROUTES // FROM {current.name.toUpperCase()}
{reachable.map((s) => ( ))}
{error && (
JUMP FAILED // {error.toUpperCase()}
)}
) }