Refactor: Reuse RegionMap component

Use the existing RegionMap component in SystemView and remove the copy.
This commit is contained in:
gpt-engineer-app[bot]
2025-06-14 15:36:53 +00:00
parent 87fe01e281
commit 988e907c48
3 changed files with 131 additions and 313 deletions

View File

@@ -8,7 +8,6 @@ import { useQuery } from '@tanstack/react-query';
import { getSecurityColor } from '../utils/securityColors';
const pocketbaseUrl = `https://evebase.site.quack-lab.dev/api/collections/regionview/records`;
// const pocketbaseUrl = `https://evebase.site.quack-lab.dev/api/collections/regionview/records?filter=(sysregion%3D'${encodedSystem}')`;
interface SolarSystem {
solarSystemName: string;
@@ -31,6 +30,12 @@ interface ProcessedConnection {
color: string;
}
interface RegionMapProps {
regionName: string;
focusSystem?: string;
isCompact?: boolean;
}
const fetchRegionData = async (regionName: string): Promise<SolarSystem[]> => {
const response = await fetch(`/${regionName}.json`);
if (!response.ok) {
@@ -52,7 +57,11 @@ const fetchRegionData = async (regionName: string): Promise<SolarSystem[]> => {
return systems;
};
export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
export const RegionMap: React.FC<RegionMapProps> = ({
regionName,
focusSystem,
isCompact = false
}) => {
const navigate = useNavigate();
const [viewBox, setViewBox] = useState({ x: 0, y: 0, width: 1200, height: 800 });
const [isPanning, setIsPanning] = useState(false);
@@ -73,19 +82,13 @@ export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
systems.forEach(system => {
system.connectedSystems?.forEach(connectedSystem => {
// Create a unique key by sorting system names alphabetically
const connectionKey = [system.solarSystemName, connectedSystem].sort().join('-');
// Skip if we've already processed this connection
if (connections.has(connectionKey)) return;
const fromPos = nodePositions[system.solarSystemName];
const toPos = nodePositions[connectedSystem];
// Skip if positions are not available
if (!fromPos || !toPos) return;
// Calculate average security for the connection
const toSystem = systems.find(s => s.solarSystemName === connectedSystem);
if (!toSystem) return;
@@ -104,7 +107,7 @@ export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
return Array.from(connections.values());
}, [systems, nodePositions]);
// Initialize node positions when data is loaded
// Initialize node positions and focus on system if specified
useEffect(() => {
if (systems) {
const positions: Record<string, Position> = {};
@@ -115,8 +118,21 @@ export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
};
});
setNodePositions(positions);
// If focusSystem is specified and we're in compact mode, center on it
if (focusSystem && isCompact) {
const focusSystemData = systems.find(s => s.solarSystemName === focusSystem);
if (focusSystemData) {
setViewBox({
x: focusSystemData.x - 200,
y: focusSystemData.y - 150,
width: 400,
height: 300
});
}
}
}
}, [systems]);
}, [systems, focusSystem, isCompact]);
const handleSystemClick = (systemName: string) => {
navigate(`/systems/${systemName}`);
@@ -165,7 +181,6 @@ export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
// Invert scroll direction: negative deltaY (scroll up) zooms out, positive (scroll down) zooms in
const scale = e.deltaY < 0 ? 1.1 : 0.9;
const newWidth = viewBox.width * scale;
const newHeight = viewBox.height * scale;
@@ -186,7 +201,7 @@ export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
if (isLoading) {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 flex items-center justify-center">
<div className={`${isCompact ? 'h-full' : 'min-h-screen'} bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 flex items-center justify-center`}>
<div className="text-white text-xl">Loading {regionName} data...</div>
</div>
);
@@ -194,22 +209,103 @@ export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
if (error) {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 flex items-center justify-center">
<div className={`${isCompact ? 'h-full' : 'min-h-screen'} bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 flex items-center justify-center`}>
<div className="text-center">
<h1 className="text-4xl font-bold text-white mb-4">Error Loading Region</h1>
<p className="text-red-400 mb-6">Failed to load data for {regionName}</p>
<Button
onClick={() => navigate('/')}
className="bg-purple-600 hover:bg-purple-700"
>
<ArrowLeft className="w-4 h-4 mr-2" />
Return to Galaxy Map
</Button>
{!isCompact && (
<Button
onClick={() => navigate('/')}
className="bg-purple-600 hover:bg-purple-700"
>
<ArrowLeft className="w-4 h-4 mr-2" />
Return to Galaxy Map
</Button>
)}
</div>
</div>
);
}
// Compact mode (for system page)
if (isCompact) {
return (
<div className="w-full h-full bg-black/20 backdrop-blur-sm">
<svg
ref={svgRef}
width="100%"
height="100%"
viewBox={`${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`}
className="cursor-grab active:cursor-grabbing"
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
onWheel={handleWheel}
>
<defs>
<filter id="glow-compact">
<feGaussianBlur stdDeviation="3" result="coloredBlur" />
<feMerge>
<feMergeNode in="coloredBlur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
{/* Render connections */}
{processedConnections.map(connection => (
<Connection
key={connection.key}
from={connection.from}
to={connection.to}
color={connection.color}
/>
))}
{/* Render systems */}
{systems?.map((system) => (
<MapNode
key={system.solarSystemName}
id={system.solarSystemName}
name={system.solarSystemName}
position={nodePositions[system.solarSystemName] || { x: 0, y: 0 }}
onClick={() => handleSystemClick(system.solarSystemName)}
type="system"
security={system.security}
signatures={system.signatures}
/>
))}
{/* Highlight focused system */}
{focusSystem && nodePositions[focusSystem] && (
<circle
cx={nodePositions[focusSystem].x}
cy={nodePositions[focusSystem].y}
r="15"
fill="none"
stroke="#a855f7"
strokeWidth="3"
strokeDasharray="5,5"
opacity="0.8"
>
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
from={`0 ${nodePositions[focusSystem].x} ${nodePositions[focusSystem].y}`}
to={`360 ${nodePositions[focusSystem].x} ${nodePositions[focusSystem].y}`}
dur="3s"
repeatCount="indefinite"
/>
</circle>
)}
</svg>
</div>
);
}
// Full page mode (original region page)
return (
<div className="w-full h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 overflow-hidden relative">
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-purple-900/20 via-slate-900/40 to-black"></div>
@@ -252,7 +348,7 @@ export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
</filter>
</defs>
{/* Render connections first (behind nodes) */}
{/* Render connections */}
{processedConnections.map(connection => (
<Connection
key={connection.key}