import { SessionContext } from "@/app/context/Context"; import { PI_TYPES_MAP, STORAGE_IDS } from "@/const"; import { planetCalculations } from "@/planets"; import { AccessToken } from "@/types"; import { TableContainer, Paper, Table, TableHead, TableRow, TableCell, Typography, Tooltip, TableBody, useTheme, Stack, styled, Accordion, AccordionSummary, AccordionDetails, TableSortLabel, Box, TextField, } from "@mui/material"; import { useContext, useState, useEffect } from "react"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import { DateTime } from "luxon"; const StackItem = styled(Stack)(({ theme }) => ({ ...theme.typography.body2, padding: theme.custom.compactMode ? theme.spacing(1) : theme.spacing(2), textAlign: "left", justifyContent: "center", alignItems: "center", })); interface Grouped { [key: number]: number; } const displayValue = (valueInMillions: number): string => valueInMillions >= 1000 ? `${(valueInMillions / 1000).toFixed(2)} B` : `${valueInMillions.toFixed(2)} M`; type SortBy = "name" | "perHour" | "storage" | "price" | "storagePrice" | "progress"; type SortDirection = "asc" | "desc"; export const Summary = ({ characters }: { characters: AccessToken[] }) => { const { piPrices } = useContext(SessionContext); const [sortDirection, setSortDirection] = useState("asc"); const [sortBy, setSortBy] = useState("name"); const [startDate, setStartDate] = useState(DateTime.now().startOf('day').toISO()); const [activityPercentage, setActivityPercentage] = useState(100); // Load saved values from localStorage on mount useEffect(() => { const savedDate = localStorage.getItem('productionStartDate'); if (savedDate) { setStartDate(savedDate); } const savedActivity = localStorage.getItem('activityPercentage'); if (savedActivity) { setActivityPercentage(parseFloat(savedActivity)); } }, []); // Save values to localStorage when they change useEffect(() => { localStorage.setItem('productionStartDate', startDate); }, [startDate]); useEffect(() => { localStorage.setItem('activityPercentage', activityPercentage.toString()); }, [activityPercentage]); // Calculate exports and storage amounts const exports = characters.flatMap((char) => { return char.planets .filter( (p) => !char.planetConfig.some( (c) => c.planetId == p.planet_id && c.excludeFromTotals, ), ) .flatMap((planet) => { const { localExports } = planetCalculations(planet); return localExports; }); }); // Calculate storage amounts const storageAmounts = characters.reduce((totals, character) => { character.planets .filter( (p) => !character.planetConfig.some( (c) => c.planetId == p.planet_id && c.excludeFromTotals, ), ) .forEach((planet) => { planet.info.pins .filter(pin => STORAGE_IDS().some(storage => storage.type_id === pin.type_id)) .forEach(storage => { storage.contents?.forEach(content => { const current = totals[content.type_id] || 0; totals[content.type_id] = current + content.amount; }); }); }); return totals; }, {}); const groupedByMaterial = exports.reduce((totals, material) => { const { typeId, amount } = material; const newTotal = isNaN(totals[typeId]) ? amount : totals[typeId] + amount; totals[typeId] = newTotal; return totals; }, {}); const startDateTime = DateTime.fromISO(startDate); const hoursSinceStart = DateTime.now().diff(startDateTime, 'hours').hours; const withProductNameAndPrice = Object.keys(groupedByMaterial).map( (typeIdString) => { const typeId = parseInt(typeIdString); const amount = groupedByMaterial[typeId]; const storageAmount = storageAmounts[typeId] || 0; const adjustedAmount = amount * (activityPercentage / 100); const valueInMillions = (((piPrices?.appraisal.items.find((a) => a.typeID === typeId)?.prices .sell.min ?? 0) * adjustedAmount) / 1000000) * 24 * 30; const storageValueInMillions = ((piPrices?.appraisal.items.find((a) => a.typeID === typeId)?.prices .sell.min ?? 0) * storageAmount) / 1000000; // Calculate expected production and progress const expectedProduction = adjustedAmount * hoursSinceStart; const progress = hoursSinceStart > 0 ? (storageAmount / expectedProduction) * 100 : 0; return { typeId, amount: adjustedAmount, storageAmount, materialName: PI_TYPES_MAP[typeId].name, price: valueInMillions, storageValue: storageValueInMillions, progress, }; }, ); return ( }>

Totals

{ const newDate = DateTime.fromFormat(e.target.value, 'yyyy-MM-dd').startOf('day').toISO(); if (newDate) setStartDate(newDate); }} InputLabelProps={{ shrink: true, }} size="small" /> { const value = Math.min(100, Math.max(0, parseFloat(e.target.value) || 0)); setActivityPercentage(value); }} InputLabelProps={{ shrink: true, }} inputProps={{ min: 0, max: 100, step: 1, }} size="small" sx={{ width: '100px' }} /> { setSortDirection( sortDirection === "asc" ? "desc" : "asc", ); setSortBy("name"); }} > Exports { setSortDirection( sortDirection === "asc" ? "desc" : "asc", ); setSortBy("perHour"); }} > u/h { setSortDirection( sortDirection === "asc" ? "desc" : "asc", ); setSortBy("storage"); }} > Units in storage { setSortDirection( sortDirection === "asc" ? "desc" : "asc", ); setSortBy("storagePrice"); }} > Produced ISK value { setSortDirection( sortDirection === "asc" ? "desc" : "asc", ); setSortBy("price"); }} > Maximum possible ISK/M { setSortDirection( sortDirection === "asc" ? "desc" : "asc", ); setSortBy("progress"); }} > Progress from start date {withProductNameAndPrice .sort((a, b) => { if (sortBy === "name") { if (sortDirection === "asc") return a.materialName > b.materialName ? 1 : -1; if (sortDirection === "desc") return a.materialName > b.materialName ? -1 : 1; } if (sortBy === "perHour") { if (sortDirection === "asc") return a.amount > b.amount ? 1 : -1; if (sortDirection === "desc") return a.amount > b.amount ? -1 : 1; } if (sortBy === "storage") { if (sortDirection === "asc") return a.storageAmount > b.storageAmount ? 1 : -1; if (sortDirection === "desc") return a.storageAmount > b.storageAmount ? -1 : 1; } if (sortBy === "price") { if (sortDirection === "asc") return a.price > b.price ? 1 : -1; if (sortDirection === "desc") return a.price > b.price ? -1 : 1; } if (sortBy === "storagePrice") { if (sortDirection === "asc") return a.storageValue > b.storageValue ? 1 : -1; if (sortDirection === "desc") return a.storageValue > b.storageValue ? -1 : 1; } if (sortBy === "progress") { if (sortDirection === "asc") return a.progress > b.progress ? 1 : -1; if (sortDirection === "desc") return a.progress > b.progress ? -1 : 1; } return 0; }) .map((product) => ( ))} amount + p.price, 0, )} storageValue={withProductNameAndPrice.reduce( (amount, p) => amount + p.storageValue, 0, )} />
); }; const SummaryRow = ({ material, amount, storageAmount, price, storageValue, progress, }: { material: string; amount?: number; storageAmount?: number; price: number; storageValue?: number; progress?: number; }) => ( {material} {amount?.toFixed(1)} {storageAmount?.toFixed(1)} {storageValue !== undefined && displayValue(storageValue)} {displayValue(price)} {progress !== undefined && ( = 100 ? 'success.main' : progress >= 75 ? 'warning.main' : 'error.main'}> {progress.toFixed(1)}% )} );