diff --git a/frontend/src/components/RegionMap.tsx b/frontend/src/components/RegionMap.tsx index 8363140..acb2898 100644 --- a/frontend/src/components/RegionMap.tsx +++ b/frontend/src/components/RegionMap.tsx @@ -10,6 +10,7 @@ import { getSecurityColor } from '@/utils/securityColors'; import { Header } from './Header'; import { ListCharacters, StartESILogin, SetDestinationForAll, AddWaypointForAllByName, PostRouteForAllByNames } from 'wailsjs/go/main/App'; import { toast } from '@/hooks/use-toast'; +import { getSystemsRegions } from '@/utils/systemApi'; interface RegionMapProps { regionName: string; @@ -73,6 +74,8 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho const [viaDest, setViaDest] = useState(null); const [viaQueue, setViaQueue] = useState([]); + const [offRegionLinks, setOffRegionLinks] = useState>([]); + useEffect(() => { const onKeyDown = async (e: KeyboardEvent) => { if (e.key === 'Escape' && viaMode) { @@ -109,6 +112,41 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho setConnections(connections); }, [systems]); + // Compute off-region links lazily using PB only for missing nodes + useEffect(() => { + const computeOffRegion = async () => { + if (!systems || systems.size === 0) { + setOffRegionLinks([]); + return; + } + const out: Array<{ from: string; to: string; toRegion: string }> = []; + const toLookup: Set = new Set(); + for (const [fromName, sys] of systems.entries()) { + const neighbors = (sys.connectedSystems || '').split(',').map(s => s.trim()).filter(Boolean); + for (const n of neighbors) { + if (!systems.has(n)) { + toLookup.add(n); + } + } + } + if (toLookup.size === 0) { setOffRegionLinks([]); return; } + const regionMap = await getSystemsRegions(Array.from(toLookup)); + for (const [fromName, sys] of systems.entries()) { + const neighbors = (sys.connectedSystems || '').split(',').map(s => s.trim()).filter(Boolean); + for (const n of neighbors) { + if (!systems.has(n)) { + const toRegion = regionMap[n]; + if (toRegion && toRegion !== regionName) { + out.push({ from: fromName, to: n, toRegion }); + } + } + } + } + setOffRegionLinks(out); + }; + computeOffRegion(); + }, [systems, regionName]); + useEffect(() => { if (isWormholeRegion) { loadWormholeSystems().then(wormholeSystems => { @@ -470,6 +508,9 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho + + + {/* Render all connections */} @@ -512,6 +553,22 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho /> ))} + {/* Off-region link indicators (clickable arrows) */} + {offRegionLinks.map((link, idx) => { + const pos = positions[link.from]; + if (!pos) return null; + const offsetX = 10; + const offsetY = -14; + return ( + + + + {link.toRegion} + { e.stopPropagation(); navigate(`/regions/${encodeURIComponent(link.toRegion)}/${encodeURIComponent(link.to)}`); }} /> + + ); + })} + {/* Highlight focused system */} {focusSystem && positions[focusSystem] && ( => { const system = await pb.collection('regionview').getFirstListItem(`sysname='${systemName}'`); return system.id; }; + +const regionCache: Map = new Map(); + +export const getSystemRegion = async (systemName: string): Promise => { + const key = systemName; + const cached = regionCache.get(key); + if (cached) return cached; + const rec = await pb.collection('regionview').getFirstListItem(`sysname='${systemName}'`); + regionCache.set(key, rec.sysregion); + return rec.sysregion as string; +}; + +export const getSystemsRegions = async (systemNames: string[]): Promise> => { + const result: Record = {}; + const pending: string[] = []; + for (const name of systemNames) { + const cached = regionCache.get(name); + if (cached) { + result[name] = cached; + } else { + pending.push(name); + } + } + if (pending.length === 0) return result; + // Fetch uncached in parallel + const fetched = await Promise.all( + pending.map(async (name) => { + const rec = await pb.collection('regionview').getFirstListItem(`sysname='${name}'`); + regionCache.set(name, rec.sysregion); + return { name, region: rec.sysregion as string }; + }) + ); + for (const { name, region } of fetched) { + result[name] = region; + } + return result; +};