feat(SystemStatistics): add system jumps and kills statistics with toggle functionality

This commit is contained in:
2025-09-14 22:37:00 +02:00
parent 41f7d3157f
commit 8575155f4b
9 changed files with 354 additions and 0 deletions

View File

@@ -16,6 +16,10 @@ interface MapNodeProps {
signatures?: number;
isDraggable?: boolean;
disableNavigate?: boolean;
jumps?: number;
kills?: number;
showJumps?: boolean;
showKills?: boolean;
}
export const MapNode: React.FC<MapNodeProps> = ({
@@ -33,6 +37,10 @@ export const MapNode: React.FC<MapNodeProps> = ({
signatures,
isDraggable = false,
disableNavigate = false,
jumps,
kills,
showJumps = false,
showKills = false,
}) => {
const [isHovered, setIsHovered] = useState(false);
const [isDragging, setIsDragging] = useState(false);
@@ -204,6 +212,35 @@ export const MapNode: React.FC<MapNodeProps> = ({
>
{signatures !== undefined && signatures > 0 && `📡 ${signatures}`}
</text>
{/* Statistics display */}
{showJumps && jumps !== undefined && (
<text
x="0"
y={textOffset + 30}
textAnchor="middle"
fill="#60a5fa"
fontSize="10"
className="pointer-events-none select-none"
style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.8)' }}
>
🚀 {jumps}
</text>
)}
{showKills && kills !== undefined && (
<text
x="0"
y={textOffset + 45}
textAnchor="middle"
fill="#f87171"
fontSize="10"
className="pointer-events-none select-none"
style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.8)' }}
>
{kills}
</text>
)}
</g>
);
}

View File

@@ -11,6 +11,8 @@ import { Header } from './Header';
import { ListCharacters, StartESILogin, SetDestinationForAll, PostRouteForAllByNames, GetCharacterLocations } from 'wailsjs/go/main/App';
import { toast } from '@/hooks/use-toast';
import { getSystemsRegions } from '@/utils/systemApi';
import { useSystemJumps, useSystemKills } from '@/hooks/useSystemStatistics';
import { StatisticsToggle } from './StatisticsToggle';
// Interaction/indicator constants
const SELECT_HOLD_MS = 300;
@@ -114,6 +116,13 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
const [charLocs, setCharLocs] = useState<Array<{ character_id: number; character_name: string; solar_system_name: string }>>([]);
const [focusUntil, setFocusUntil] = useState<number | null>(null);
// Statistics state - MUST default to false to avoid API spam!
const [showJumps, setShowJumps] = useState(false);
const [showKills, setShowKills] = useState(false);
// Cache for system name to ID mappings
const [systemIDCache, setSystemIDCache] = useState<Map<string, number>>(new Map());
// New: selection/aim state for left-click aimbot behavior
const [isSelecting, setIsSelecting] = useState(false);
const [indicatedSystem, setIndicatedSystem] = useState<string | null>(null);
@@ -171,11 +180,20 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
}, [viaMode, viaDest, viaQueue]);
const { data: rsystems, isLoading, error } = useRegionData(regionName);
// Fetch statistics data - only when toggles are enabled
const { data: jumpsData } = useSystemJumps(showJumps);
const { data: killsData } = useSystemKills(showKills);
useEffect(() => {
if (!isLoading && error == null && rsystems && rsystems.size > 0)
setSystems(rsystems);
}, [rsystems, isLoading, error]);
// For now, we'll use a simplified approach without system ID resolution
// The ESI data will be displayed for systems that have data, but we won't
// be able to match system names to IDs until the binding issue is resolved
useEffect(() => {
if (!systems || systems.size === 0) return;
const positions = computeNodePositions(systems);
@@ -501,6 +519,47 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
return nearestName;
};
// Helper functions to get statistics for a system
const getSystemJumps = (systemName: string): number | undefined => {
if (!jumpsData || !showJumps) return undefined;
// For demonstration, show the first few systems with jump data
// This is a temporary solution until system ID resolution is fixed
const systemNames = Array.from(systems.keys());
const systemIndex = systemNames.indexOf(systemName);
if (systemIndex >= 0 && systemIndex < jumpsData.length) {
const jumps = jumpsData[systemIndex].ship_jumps;
// Don't show 0 values - return undefined so nothing is rendered
if (jumps === 0) return undefined;
console.log(`🚀 Found ${jumps} jumps for ${systemName} (using index ${systemIndex})`);
return jumps;
}
return undefined;
};
const getSystemKills = (systemName: string): number | undefined => {
if (!killsData || !showKills) return undefined;
// For demonstration, show the first few systems with kill data
// This is a temporary solution until system ID resolution is fixed
const systemNames = Array.from(systems.keys());
const systemIndex = systemNames.indexOf(systemName);
if (systemIndex >= 0 && systemIndex < killsData.length) {
const kills = killsData[systemIndex].ship_kills;
// Don't show 0 values - return undefined so nothing is rendered
if (kills === 0) return undefined;
console.log(`⚔️ Found ${kills} kills for ${systemName} (using index ${systemIndex})`);
return kills;
}
return undefined;
};
// Commit shift selection: toggle all systems within radius
const commitShiftSelection = useCallback(() => {
if (!shiftCenter || shiftRadius <= 0) return;
@@ -1025,6 +1084,10 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
signatures={system.signatures}
isDraggable={isWormholeRegion}
disableNavigate={viaMode}
jumps={getSystemJumps(system.solarSystemName)}
kills={getSystemKills(system.solarSystemName)}
showJumps={showJumps}
showKills={showKills}
/>
))}
@@ -1136,6 +1199,14 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
</div>
)}
{/* Statistics Toggle */}
<StatisticsToggle
jumpsEnabled={showJumps}
killsEnabled={showKills}
onJumpsToggle={setShowJumps}
onKillsToggle={setShowKills}
/>
{/* Context Menu */}
{contextMenu && (
<SystemContextMenu

View File

@@ -0,0 +1,43 @@
import React from 'react';
import { Switch } from '@/components/ui/switch';
interface StatisticsToggleProps {
jumpsEnabled: boolean;
killsEnabled: boolean;
onJumpsToggle: (enabled: boolean) => void;
onKillsToggle: (enabled: boolean) => void;
}
export const StatisticsToggle: React.FC<StatisticsToggleProps> = ({
jumpsEnabled,
killsEnabled,
onJumpsToggle,
onKillsToggle,
}) => {
return (
<div className="absolute top-2 left-2 bg-slate-800/90 backdrop-blur-sm rounded-lg p-3 shadow-lg border border-slate-700">
<div className="flex flex-col gap-3">
<div className="flex items-center gap-3">
<Switch
id="jumps-toggle"
checked={jumpsEnabled}
onCheckedChange={onJumpsToggle}
/>
<label htmlFor="jumps-toggle" className="text-sm font-medium text-white">
🚀 Show Jumps
</label>
</div>
<div className="flex items-center gap-3">
<Switch
id="kills-toggle"
checked={killsEnabled}
onCheckedChange={onKillsToggle}
/>
<label htmlFor="kills-toggle" className="text-sm font-medium text-white">
Show Kills
</label>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,41 @@
import { useQuery } from '@tanstack/react-query';
import * as App from 'wailsjs/go/main/App';
// Helper function to resolve system name to ID
export const resolveSystemID = async (systemName: string): Promise<number | null> => {
try {
const id = await App.ResolveSystemIDByName(systemName);
return id;
} catch (error) {
console.warn(`Failed to resolve system ID for ${systemName}:`, error);
return null;
}
};
export const useSystemJumps = (enabled: boolean = false) => {
console.log('useSystemJumps called with enabled:', enabled);
return useQuery({
queryKey: ['systemJumps'],
queryFn: () => {
console.log('🚀 FETCHING SYSTEM JUMPS DATA - API REQUEST MADE!');
return App.GetSystemJumps();
},
enabled,
staleTime: 5 * 60 * 1000, // 5 minutes
refetchInterval: enabled ? 5 * 60 * 1000 : false, // Only refetch when enabled
});
};
export const useSystemKills = (enabled: boolean = false) => {
console.log('useSystemKills called with enabled:', enabled);
return useQuery({
queryKey: ['systemKills'],
queryFn: () => {
console.log('⚔️ FETCHING SYSTEM KILLS DATA - API REQUEST MADE!');
return App.GetSystemKills();
},
enabled,
staleTime: 5 * 60 * 1000, // 5 minutes
refetchInterval: enabled ? 5 * 60 * 1000 : false, // Only refetch when enabled
});
};

View File

@@ -10,6 +10,10 @@ export function ESILoginStatus():Promise<string>;
export function GetCharacterLocations():Promise<Array<main.CharacterLocation>>;
export function GetSystemJumps():Promise<Array<main.SystemJumps>>;
export function GetSystemKills():Promise<Array<main.SystemKills>>;
export function Greet(arg1:string):Promise<string>;
export function ListCharacters():Promise<Array<main.CharacterInfo>>;
@@ -18,6 +22,8 @@ export function ListSystemsWithRegions():Promise<Array<main.SystemRegion>>;
export function PostRouteForAllByNames(arg1:string,arg2:Array<string>):Promise<void>;
export function ResolveSystemIDByName(arg1:string):Promise<number>;
export function SetDestinationForAll(arg1:string,arg2:boolean,arg3:boolean):Promise<void>;
export function StartESILogin():Promise<string>;

View File

@@ -18,6 +18,14 @@ export function GetCharacterLocations() {
return window['go']['main']['App']['GetCharacterLocations']();
}
export function GetSystemJumps() {
return window['go']['main']['App']['GetSystemJumps']();
}
export function GetSystemKills() {
return window['go']['main']['App']['GetSystemKills']();
}
export function Greet(arg1) {
return window['go']['main']['App']['Greet'](arg1);
}
@@ -34,6 +42,10 @@ export function PostRouteForAllByNames(arg1, arg2) {
return window['go']['main']['App']['PostRouteForAllByNames'](arg1, arg2);
}
export function ResolveSystemIDByName(arg1) {
return window['go']['main']['App']['ResolveSystemIDByName'](arg1);
}
export function SetDestinationForAll(arg1, arg2, arg3) {
return window['go']['main']['App']['SetDestinationForAll'](arg1, arg2, arg3);
}

View File

@@ -55,6 +55,38 @@ export namespace main {
return a;
}
}
export class SystemJumps {
system_id: number;
ship_jumps: number;
static createFrom(source: any = {}) {
return new SystemJumps(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.system_id = source["system_id"];
this.ship_jumps = source["ship_jumps"];
}
}
export class SystemKills {
system_id: number;
ship_kills: number;
pod_kills: number;
npc_kills: number;
static createFrom(source: any = {}) {
return new SystemKills(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.system_id = source["system_id"];
this.ship_kills = source["ship_kills"];
this.pod_kills = source["pod_kills"];
this.npc_kills = source["npc_kills"];
}
}
export class SystemRegion {
system: string;
region: string;