From 42f95c17de562a59e0a7fb2d7b181525b9170a38 Mon Sep 17 00:00:00 2001 From: calli Date: Tue, 22 Apr 2025 12:47:12 +0300 Subject: [PATCH] refactor types and add account level totals --- src/app/components/Account/AccountCard.tsx | 99 ++++++++++++++--- .../PlanetaryInteraction/PlanetTableRow.tsx | 24 +++-- src/planets.ts | 2 +- src/types.ts | 101 +++++++++++++----- 4 files changed, 178 insertions(+), 48 deletions(-) diff --git a/src/app/components/Account/AccountCard.tsx b/src/app/components/Account/AccountCard.tsx index e8308de..e63f49d 100644 --- a/src/app/components/Account/AccountCard.tsx +++ b/src/app/components/Account/AccountCard.tsx @@ -7,11 +7,64 @@ import { useContext, useState } from "react"; import { PlanRow } from "./PlanRow"; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import { planetCalculations } from "@/planets"; +import { EvePraisalResult } from "@/eve-praisal"; +import { STORAGE_IDS } from "@/const"; + +interface AccountTotals { + monthlyEstimate: number; + storageValue: number; +} + +const calculateAccountTotals = (characters: AccessToken[], piPrices: EvePraisalResult | undefined): AccountTotals => { + let totalMonthlyEstimate = 0; + let totalStorageValue = 0; + + characters.forEach((character) => { + character.planets.forEach((planet) => { + const { localExports } = planetCalculations(planet); + const planetConfig = character.planetConfig.find(p => p.planetId === planet.planet_id); + + // Calculate monthly estimate + if (!planetConfig?.excludeFromTotals) { + localExports.forEach((exportItem) => { + const valueInMillions = (((piPrices?.appraisal.items.find( + (a) => a.typeID === exportItem.typeId, + )?.prices.sell.min ?? 0) * + exportItem.amount) / + 1000000) * + 24 * + 30; + totalMonthlyEstimate += valueInMillions; + }); + } + + if (!planetConfig?.excludeFromTotals) { + planet.info.pins + .filter(pin => STORAGE_IDS().some(storage => storage.type_id === pin.type_id)) + .forEach(storage => { + storage.contents?.forEach(content => { + const valueInMillions = (piPrices?.appraisal.items.find( + (a) => a.typeID === content.type_id, + )?.prices.sell.min ?? 0) * content.amount / 1000000; + totalStorageValue += valueInMillions; + }); + }); + } + }); + }); + + return { + monthlyEstimate: totalMonthlyEstimate, + storageValue: totalStorageValue + }; +}; export const AccountCard = ({ characters }: { characters: AccessToken[] }) => { const theme = useTheme(); const [isCollapsed, setIsCollapsed] = useState(false); - const { planMode } = useContext(SessionContext); + const { planMode, piPrices } = useContext(SessionContext); + const { monthlyEstimate, storageValue } = calculateAccountTotals(characters, piPrices); return ( { justifyContent: 'space-between', }} > - - {characters.length > 0 && characters[0].account !== "-" - ? `Account: ${characters[0].account}` - : "No account name"} - + + + {characters.length > 0 && characters[0].account !== "-" + ? `Account: ${characters[0].account}` + : "No account name"} + + + Monthly Estimate: {monthlyEstimate >= 1000 + ? `${(monthlyEstimate / 1000).toFixed(2)} B` + : `${monthlyEstimate.toFixed(2)} M`} ISK + + + Storage Value: {storageValue >= 1000 + ? `${(storageValue / 1000).toFixed(2)} B` + : `${storageValue.toFixed(2)} M`} ISK + + setIsCollapsed(!isCollapsed)} diff --git a/src/app/components/PlanetaryInteraction/PlanetTableRow.tsx b/src/app/components/PlanetaryInteraction/PlanetTableRow.tsx index d16c17c..7d2dec8 100644 --- a/src/app/components/PlanetaryInteraction/PlanetTableRow.tsx +++ b/src/app/components/PlanetaryInteraction/PlanetTableRow.tsx @@ -97,15 +97,6 @@ export const PlanetTableRow = ({ cycleTime: schematic.cycle_time })); - // Convert Map to Array for schematic IDs - const installedSchematicIds = Array.from(localProduction.values()).map(p => p.schematic_id); - - // Get extractor head types safely - const extractedTypeIds = extractors - .map(e => e.extractor_details?.product_type_id) - .filter((id): id is number => id !== undefined); - - // Get storage facilities const storageFacilities = planetInfo.pins.filter(pin => STORAGE_IDS().some(storage => storage.type_id === pin.type_id) ); @@ -116,20 +107,26 @@ export const PlanetTableRow = ({ const storageType = PI_TYPES_MAP[pin.type_id].name; const storageCapacity = STORAGE_CAPACITIES[pin.type_id] || 0; - // Calculate total volume of stored products for this specific pin const totalVolume = (pin.contents || []) .reduce((sum: number, item: any) => { const volume = PI_PRODUCT_VOLUMES[item.type_id] || 0; return sum + (item.amount * volume); }, 0); + const totalValue = (pin.contents || []) + .reduce((sum: number, item: any) => { + const price = piPrices?.appraisal.items.find((a) => a.typeID === item.type_id)?.prices.sell.min ?? 0; + return sum + (item.amount * price); + }, 0); + const fillRate = storageCapacity > 0 ? (totalVolume / storageCapacity) * 100 : 0; return { type: storageType, capacity: storageCapacity, used: totalVolume, - fillRate: fillRate + fillRate: fillRate, + value: totalValue }; }; @@ -319,6 +316,11 @@ export const PlanetTableRow = ({ {fillRate.toFixed(1)}% + {storageInfo.value > 0 && ( + + ({Math.round(storageInfo.value / 1000000)}M) + + )} ); })} diff --git a/src/planets.ts b/src/planets.ts index df774e4..55ae177 100644 --- a/src/planets.ts +++ b/src/planets.ts @@ -51,7 +51,7 @@ export const getPlanetUniverse = async ( export const planetCalculations = (planet: PlanetWithInfo) => { const planetInfo = planet.info; type SchematicId = number; - const extractors: PlanetInfo["pins"] = planetInfo.pins.filter((p) => + const extractors = planetInfo.pins.filter((p) => EXTRACTOR_TYPE_IDS.some((e) => e === p.type_id), ); const localProduction = planetInfo.pins diff --git a/src/types.ts b/src/types.ts index dc1a7a6..b2ad6e8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,4 @@ import { PlanetConfig } from "./app/components/PlanetConfig/PlanetConfigDialog"; -import { Api } from "./esi-api"; export interface AccessToken { access_token: string; @@ -20,10 +19,50 @@ export interface Character { characterId: number; } +export interface Planet { + planet_id: number; + solar_system_id: number; + planet_type: "temperate" | "barren" | "oceanic" | "ice" | "gas" | "lava" | "storm" | "plasma"; + last_update: string; + num_pins: number; + owner_id: number; + upgrade_level: number; +} + +export interface PlanetInfo { + links: Array<{ + destination_pin_id: number; + link_level: number; + source_pin_id: number; + }>; + pins: Pin[]; + routes: Array<{ + content_type_id: number; + destination_pin_id: number; + quantity: number; + route_id: number; + source_pin_id: number; + waypoints?: number[]; + }>; +} + +export interface PlanetInfoUniverse { + name: string; + planet_id: number; + system_id: number; + type_id: number; + position: { + x: number; + y: number; + z: number; + }; +} + export interface PlanetWithInfo extends Planet { info: PlanetInfo; infoUniverse: PlanetInfoUniverse; } + export interface CharacterPlanets { name: string; characterId: number; @@ -38,31 +77,45 @@ export interface CharacterUpdate { system?: string; } -export type Planet = EsiType<"v1", "getCharactersCharacterIdPlanets">[number]; - -export type PlanetInfoUniverse = EsiType<"v1", "getUniversePlanetsPlanetId">; - -export type PlanetInfo = EsiType< - "v3", - "getCharactersCharacterIdPlanetsPlanetId" ->; - export interface Env { EVE_SSO_CALLBACK_URL: string; EVE_SSO_CLIENT_ID: string; } -type EsiApiVersionType = keyof InstanceType>; -type EsiApiPathType = keyof InstanceType< - typeof Api ->[V]; -type EsiApiResponseType< - V extends EsiApiVersionType, - T extends EsiApiPathType, -> = Awaited>[V][T]>>; -export type EsiType< - V extends EsiApiVersionType, - T extends EsiApiPathType, -> = EsiApiResponseType extends { data: any } - ? EsiApiResponseType["data"] - : never; +export interface EvePraisalResult { + appraisal: { + items: Array<{ + typeID: number; + prices: { + sell: { + min: number; + }; + }; + }>; + }; +} + +export interface Pin { + pin_id: number; + type_id: number; + schematic_id?: number; + expiry_time?: string; + install_time?: string; + latitude: number; + longitude: number; + extractor_details?: { + cycle_time?: number; + head_radius?: number; + heads: Array<{ + head_id: number; + latitude: number; + longitude: number; + }>; + product_type_id?: number; + qty_per_cycle?: number; + }; + contents?: Array<{ + type_id: number; + amount: number; + }>; +}