'use client' import { useCallback, useEffect, useState } from 'react' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import type { ExploreResult, SystemState } from '@/lib/game/save' import type { FoundItem } from '@/lib/game/world' // LOCAL SYSTEM tab — the bodies of the current system (planets + staggered moons) with an // EXPLORE button per unexplored row. Fetches /api/system on mount; explore responses are // applied directly (no polling). Turn-based: one scan costs a turn, each body once. export function LocalSystemPanel() { const [state, setState] = useState(null) const [lastOutcome, setLastOutcome] = useState(null) const [error, setError] = useState(null) const [exploringId, setExploringId] = useState(null) useEffect(() => { let cancelled = false fetch('/api/system') .then(async (res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`) return (await res.json()) as SystemState }) .then((s) => { if (!cancelled) setState(s) }) .catch((err: unknown) => { if (!cancelled) setError(err instanceof Error ? err.message : String(err)) }) return () => { cancelled = true } }, []) const explore = useCallback(async (planetId: number) => { setExploringId(planetId) try { const res = await fetch('/api/explore', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ planetId }), }) const body = (await res.json()) as ExploreResult & { error?: string } if (!res.ok) throw new Error(body.error ?? `HTTP ${res.status}`) setState(body) // fresh state straight from the response — no refetch needed setLastOutcome(body.outcome) } catch (err: unknown) { setError(err instanceof Error ? err.message : String(err)) } finally { setExploringId(null) } }, []) if (error && !state) { return (
SYSTEM SCAN OFFLINE // {error.toUpperCase()}
) } if (!state) { return (
SCANNING SYSTEM…
) } const badgeFor = (explored: boolean, hazard: boolean) => explored ? { label: 'SCANNED', variant: 'ACTIVE' as const } : hazard ? { label: 'HAZARD', variant: 'WARNING' as const } : { label: 'UNEXPLORED', variant: 'SCANNING' as const } return (
EXPLORABLE WORLDS // {state.systemName.toUpperCase()} — TURN {state.turn}
{state.planets.map((row, i) => { const depth = row.parent !== null ? 1 : 0 const badge = badgeFor(row.explored, row.hazard) return (
{row.name}
{depth ? `MOON OF ${state.planets.find((p) => p.id === row.parent)?.name ?? '?'} // ${row.type}` : row.type}
{badge.label} {!row.explored && ( )}
) })}
{lastOutcome !== null && (
SCAN RESULT //{' '} {lastOutcome.length > 0 ? lastOutcome.map((i) => `${i.qty}× ${i.name.toUpperCase()}`).join(', ') : 'NOTHING FOUND'}
)}
CARGO HOLD // INVENTORY
{state.inventory.length === 0 ? (
EMPTY — EXPLORE WORLDS TO FIND THINGS
) : ( state.inventory.map((item) => (
{item.name} // {item.kind} ×{item.qty}
)) )}
{error && (
SCAN FAILED // {error.toUpperCase()}
)}
) }