mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
feat: integrate CompareCell component into TankRow and Tanks for improved performance metrics display
This commit is contained in:
1 parent
5b9041a972
commit
8d10d7cffa
4 files changed
+180
-136
No files matched your search
@@ -16,7 +16,7 @@ export function CompareCell({
|
|||||||
value,
|
value,
|
||||||
truth,
|
truth,
|
||||||
style,
|
style,
|
||||||
direction,
|
direction = CompareCellDirection.HIGHER_IS_BETTER,
|
||||||
...props
|
...props
|
||||||
}: CompareCellProps) {
|
}: CompareCellProps) {
|
||||||
const percentage = value / truth - 1;
|
const percentage = value / truth - 1;
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
import { TankDefinition, formatCompact } from '@blitzkit/core';
|
import { TankDefinition, formatCompact } from "@blitzkit/core";
|
||||||
import { Table } from '@radix-ui/themes';
|
import { Table } from "@radix-ui/themes";
|
||||||
import { memo } from 'react';
|
import { memo } from "react";
|
||||||
import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions';
|
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
||||||
import { useAveragesExclusionRatio } from '../../hooks/useAveragesExclusionRatio';
|
import { useAveragesExclusionRatio } from "../../hooks/useAveragesExclusionRatio";
|
||||||
import { useLocale } from '../../hooks/useLocale';
|
import { useLocale } from "../../hooks/useLocale";
|
||||||
import { Performance } from '../../stores/performance';
|
import { Performance } from "../../stores/performance";
|
||||||
import { TankRowHeaderCell } from '../TankRowHeaderCell';
|
import { CompareCell, CompareCellDirection } from "../CompareCell";
|
||||||
|
import { TankRowHeaderCell } from "../TankRowHeaderCell";
|
||||||
|
|
||||||
interface TankRowProps {
|
interface TankRowProps {
|
||||||
tank: TankDefinition;
|
tank: TankDefinition;
|
||||||
|
average: Record<string, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const averageDefinitions = await awaitableAverageDefinitions;
|
const averageDefinitions = await awaitableAverageDefinitions;
|
||||||
|
|
||||||
export const TankRow = memo<TankRowProps>(
|
export const TankRow = memo<TankRowProps>(
|
||||||
({ tank }) => {
|
({ tank, average }) => {
|
||||||
const averages = averageDefinitions.averages[tank.id];
|
const averages = averageDefinitions.averages[tank.id];
|
||||||
const ratio = useAveragesExclusionRatio();
|
const ratio = useAveragesExclusionRatio();
|
||||||
const playerCountPeriod = Performance.use(
|
const playerCountPeriod = Performance.use(
|
||||||
(state) => state.playerCountPeriod,
|
(state) => state.playerCountPeriod
|
||||||
);
|
);
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
|
|
||||||
@@ -26,59 +28,108 @@ export const TankRow = memo<TankRowProps>(
|
|||||||
<Table.Row>
|
<Table.Row>
|
||||||
<TankRowHeaderCell tank={tank} />
|
<TankRowHeaderCell tank={tank} />
|
||||||
|
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.wins / averages.mu.battles}
|
||||||
|
truth={average.winrate}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{((averages.mu.wins / averages.mu.battles) * 100).toFixed(1)}%
|
{((averages.mu.wins / averages.mu.battles) * 100).toFixed(1)}%
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<Table.Cell align="center">
|
||||||
{formatCompact(
|
{formatCompact(
|
||||||
locale,
|
locale,
|
||||||
Math.round(ratio * averages.samples[playerCountPeriod]),
|
Math.round(ratio * averages.samples[playerCountPeriod])
|
||||||
)}
|
)}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.damage_dealt / averages.mu.battles}
|
||||||
|
truth={average.damage}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{Math.round(
|
{Math.round(
|
||||||
averages.mu.damage_dealt / averages.mu.battles,
|
averages.mu.damage_dealt / averages.mu.battles
|
||||||
).toLocaleString(locale)}
|
).toLocaleString(locale)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.survived_battles / averages.mu.battles}
|
||||||
|
truth={average.survival}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{Math.round(
|
{Math.round(
|
||||||
(averages.mu.survived_battles / averages.mu.battles) * 100,
|
(averages.mu.survived_battles / averages.mu.battles) * 100
|
||||||
)}
|
)}
|
||||||
%
|
%
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.xp / averages.mu.battles}
|
||||||
|
truth={average.xp}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{Math.round(averages.mu.xp / averages.mu.battles).toLocaleString(
|
{Math.round(averages.mu.xp / averages.mu.battles).toLocaleString(
|
||||||
locale,
|
locale
|
||||||
)}
|
)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.frags / averages.mu.battles}
|
||||||
|
truth={average.kills}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.frags / averages.mu.battles).toFixed(2)}
|
{(averages.mu.frags / averages.mu.battles).toFixed(2)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.spotted / averages.mu.battles}
|
||||||
|
truth={average.spots}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.spotted / averages.mu.battles).toFixed(2)}
|
{(averages.mu.spotted / averages.mu.battles).toFixed(2)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.hits / averages.mu.shots}
|
||||||
|
truth={average.accuracy}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{Math.round((averages.mu.hits / averages.mu.shots) * 100)}%
|
{Math.round((averages.mu.hits / averages.mu.shots) * 100)}%
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.shots / averages.mu.battles}
|
||||||
|
truth={average.shots}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.shots / averages.mu.battles).toFixed(1)}
|
{(averages.mu.shots / averages.mu.battles).toFixed(1)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.hits / averages.mu.battles}
|
||||||
|
truth={average.hits}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.hits / averages.mu.battles).toFixed(1)}
|
{(averages.mu.hits / averages.mu.battles).toFixed(1)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.damage_dealt / averages.mu.damage_received}
|
||||||
|
truth={average.damageRatio}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.damage_dealt / averages.mu.damage_received).toFixed(2)}
|
{(averages.mu.damage_dealt / averages.mu.damage_received).toFixed(2)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.damage_received / averages.mu.battles}
|
||||||
|
truth={average.damageReceived}
|
||||||
|
direction={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{Math.round(
|
{Math.round(
|
||||||
averages.mu.damage_received / averages.mu.battles,
|
averages.mu.damage_received / averages.mu.battles
|
||||||
).toLocaleString(locale)}
|
).toLocaleString(locale)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.capture_points / averages.mu.battles}
|
||||||
|
truth={average.capturePoints}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.capture_points / averages.mu.battles).toFixed(2)}
|
{(averages.mu.capture_points / averages.mu.battles).toFixed(2)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(prev, next) => prev.tank.id === next.tank.id,
|
(prev, next) => prev.tank.id === next.tank.id
|
||||||
);
|
);
|
||||||
@@ -1,17 +1,27 @@
|
|||||||
|
import { formatCompact, type BlitzkitStats } from "@blitzkit/core";
|
||||||
import { Table } from "@radix-ui/themes";
|
import { Table } from "@radix-ui/themes";
|
||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
import { Fragment, Suspense, useEffect, useMemo, useState } from "react";
|
import {
|
||||||
|
Fragment,
|
||||||
|
Suspense,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import usePromise from "react-promise-suspense";
|
import usePromise from "react-promise-suspense";
|
||||||
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
||||||
import { awaitableTankDefinitions } from "../../core/awaitables/tankDefinitions";
|
import { awaitableTankDefinitions } from "../../core/awaitables/tankDefinitions";
|
||||||
import { filterTanks } from "../../core/blitzkit/filterTanks";
|
import { filterTanks } from "../../core/blitzkit/filterTanks";
|
||||||
|
import { useAveragesExclusionRatio } from "../../hooks/useAveragesExclusionRatio";
|
||||||
|
import { useLocale } from "../../hooks/useLocale";
|
||||||
import { Performance } from "../../stores/performance";
|
import { Performance } from "../../stores/performance";
|
||||||
import { TankFilters } from "../../stores/tankFilters";
|
import { TankFilters } from "../../stores/tankFilters";
|
||||||
import { TankPerformanceSort } from "../../stores/tankPerformanceSort";
|
import { TankPerformanceSort } from "../../stores/tankPerformanceSort";
|
||||||
import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonComponentProps";
|
import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonComponentProps";
|
||||||
|
import { StickyRowHeaderCell } from "../StickyRowHeaderCell";
|
||||||
import { RowLoader } from "./RowLoader";
|
import { RowLoader } from "./RowLoader";
|
||||||
import { TankRow } from "./TankRow";
|
import { TankRow } from "./TankRow";
|
||||||
import { Total } from "./Total";
|
|
||||||
|
|
||||||
const PREVIEW_COUNT = 10;
|
const PREVIEW_COUNT = 10;
|
||||||
const DEFAULT_LOADED_ROWS = 25;
|
const DEFAULT_LOADED_ROWS = 25;
|
||||||
@@ -145,9 +155,83 @@ export function Tanks({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
setLoadedRows(DEFAULT_LOADED_ROWS);
|
setLoadedRows(DEFAULT_LOADED_ROWS);
|
||||||
}, [filters, sort]);
|
}, [filters, sort]);
|
||||||
|
|
||||||
|
const playerCountPeriod = Performance.use((state) => state.playerCountPeriod);
|
||||||
|
const { strings, locale } = useLocale();
|
||||||
|
const ratio = useAveragesExclusionRatio();
|
||||||
|
const sum = useCallback(
|
||||||
|
(slice: (tank: BlitzkitStats) => number) => {
|
||||||
|
return tanksSorted.reduce(
|
||||||
|
(acc, tank) => acc + tank.samples[playerCountPeriod] * slice(tank.mu),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[tanksSorted]
|
||||||
|
);
|
||||||
|
|
||||||
|
const battles = sum(({ battles }) => battles);
|
||||||
|
const winrate = sum(({ wins }) => wins) / battles;
|
||||||
|
const damage = sum(({ damage_dealt }) => damage_dealt) / battles;
|
||||||
|
const survival = sum(({ survived_battles }) => survived_battles) / battles;
|
||||||
|
const xp = sum(({ xp }) => xp) / battles;
|
||||||
|
const kills = sum(({ frags }) => frags) / battles;
|
||||||
|
const spots = sum(({ spotted }) => spotted) / battles;
|
||||||
|
const hits = sum(({ hits }) => hits) / battles;
|
||||||
|
const shots = sum(({ shots }) => shots) / battles;
|
||||||
|
const accuracy = hits / shots;
|
||||||
|
const damageReceived =
|
||||||
|
sum(({ damage_received }) => damage_received) / battles;
|
||||||
|
const damageRatio = damage / damageReceived;
|
||||||
|
const capturePoints = sum(({ capture_points }) => capture_points) / battles;
|
||||||
|
|
||||||
|
const average = {
|
||||||
|
battles,
|
||||||
|
winrate,
|
||||||
|
damage,
|
||||||
|
survival,
|
||||||
|
xp,
|
||||||
|
kills,
|
||||||
|
spots,
|
||||||
|
hits,
|
||||||
|
shots,
|
||||||
|
accuracy,
|
||||||
|
damageReceived,
|
||||||
|
damageRatio,
|
||||||
|
capturePoints,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
<Total tanks={tanksSorted} />
|
<Table.Row>
|
||||||
|
<StickyRowHeaderCell style={{ overflow: "hidden", display: "flex" }}>
|
||||||
|
{strings.website.tools.performance.table.tanks.total}
|
||||||
|
</StickyRowHeaderCell>
|
||||||
|
<Table.Cell align="center">{(winrate * 100).toFixed(1)}%</Table.Cell>
|
||||||
|
<Table.Cell align="center">
|
||||||
|
{formatCompact(
|
||||||
|
locale,
|
||||||
|
ratio * averageDefinitions.samples[playerCountPeriod]
|
||||||
|
)}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell align="center">
|
||||||
|
{Math.round(damage).toLocaleString(locale)}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell align="center">
|
||||||
|
{Math.round(survival * 100).toFixed(1)}%
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell align="center">
|
||||||
|
{Math.round(xp).toLocaleString(locale)}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell align="center">{kills.toFixed(2)}</Table.Cell>
|
||||||
|
<Table.Cell align="center">{spots.toFixed(2)}</Table.Cell>
|
||||||
|
<Table.Cell align="center">{(accuracy * 100).toFixed(0)}%</Table.Cell>
|
||||||
|
<Table.Cell align="center">{shots.toFixed(1)}</Table.Cell>
|
||||||
|
<Table.Cell align="center">{hits.toFixed(1)}</Table.Cell>
|
||||||
|
<Table.Cell align="center">{damageRatio.toFixed(2)}</Table.Cell>
|
||||||
|
<Table.Cell align="center">
|
||||||
|
{Math.round(damageReceived).toLocaleString(locale)}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell align="center">{capturePoints.toFixed(2)}</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
|
||||||
{tanksSorted.slice(0, loadedRows).map((averages) => {
|
{tanksSorted.slice(0, loadedRows).map((averages) => {
|
||||||
const tank = tankDefinitions.tanks[averages.id];
|
const tank = tankDefinitions.tanks[averages.id];
|
||||||
@@ -157,7 +241,7 @@ export function Tanks({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
return (
|
return (
|
||||||
<Fragment key={tank.id}>
|
<Fragment key={tank.id}>
|
||||||
<Suspense fallback={<RowLoader />}>
|
<Suspense fallback={<RowLoader />}>
|
||||||
<TankRow tank={tank} />
|
<TankRow average={average} tank={tank} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
import {
|
|
||||||
AverageDefinitionsEntry,
|
|
||||||
formatCompact,
|
|
||||||
type BlitzkitStats,
|
|
||||||
} from "@blitzkit/core";
|
|
||||||
import { Table } from "@radix-ui/themes";
|
|
||||||
import { memo, useCallback } from "react";
|
|
||||||
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
|
||||||
import { useAveragesExclusionRatio } from "../../hooks/useAveragesExclusionRatio";
|
|
||||||
import { useLocale } from "../../hooks/useLocale";
|
|
||||||
import { Performance } from "../../stores/performance";
|
|
||||||
import { StickyRowHeaderCell } from "../StickyRowHeaderCell";
|
|
||||||
|
|
||||||
export interface AverageDefinitionsEntryWithId extends AverageDefinitionsEntry {
|
|
||||||
id: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TotalProps {
|
|
||||||
tanks: AverageDefinitionsEntryWithId[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const averageDefinitions = await awaitableAverageDefinitions;
|
|
||||||
|
|
||||||
export const Total = memo<TotalProps>(
|
|
||||||
({ tanks }) => {
|
|
||||||
const playerCountPeriod = Performance.use(
|
|
||||||
(state) => state.playerCountPeriod
|
|
||||||
);
|
|
||||||
const { strings, locale } = useLocale();
|
|
||||||
const ratio = useAveragesExclusionRatio();
|
|
||||||
const sum = useCallback(
|
|
||||||
(slice: (tank: BlitzkitStats) => number) => {
|
|
||||||
return tanks.reduce(
|
|
||||||
(acc, tank) => acc + tank.samples[playerCountPeriod] * slice(tank.mu),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[tanks]
|
|
||||||
);
|
|
||||||
|
|
||||||
const battles = sum(({ battles }) => battles);
|
|
||||||
const winrate = sum(({ wins }) => wins) / battles;
|
|
||||||
const damage = sum(({ damage_dealt }) => damage_dealt) / battles;
|
|
||||||
const survival = sum(({ survived_battles }) => survived_battles) / battles;
|
|
||||||
const xp = sum(({ xp }) => xp) / battles;
|
|
||||||
const kills = sum(({ frags }) => frags) / battles;
|
|
||||||
const spots = sum(({ spotted }) => spotted) / battles;
|
|
||||||
const hits = sum(({ hits }) => hits) / battles;
|
|
||||||
const shots = sum(({ shots }) => shots) / battles;
|
|
||||||
const accuracy = hits / shots;
|
|
||||||
const damageReceived =
|
|
||||||
sum(({ damage_received }) => damage_received) / battles;
|
|
||||||
const damageRatio = damage / damageReceived;
|
|
||||||
const capturePoints = sum(({ capture_points }) => capture_points) / battles;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table.Row>
|
|
||||||
<StickyRowHeaderCell style={{ overflow: "hidden", display: "flex" }}>
|
|
||||||
{strings.website.tools.performance.table.tanks.total}
|
|
||||||
</StickyRowHeaderCell>
|
|
||||||
<Table.Cell align="center">{(winrate * 100).toFixed(1)}%</Table.Cell>
|
|
||||||
<Table.Cell align="center">
|
|
||||||
{formatCompact(
|
|
||||||
locale,
|
|
||||||
ratio * averageDefinitions.samples[playerCountPeriod]
|
|
||||||
)}
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell align="center">
|
|
||||||
{Math.round(damage).toLocaleString(locale)}
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell align="center">
|
|
||||||
{Math.round(survival * 100).toFixed(1)}%
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell align="center">
|
|
||||||
{Math.round(xp).toLocaleString(locale)}
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell align="center">{kills.toFixed(2)}</Table.Cell>
|
|
||||||
<Table.Cell align="center">{spots.toFixed(2)}</Table.Cell>
|
|
||||||
<Table.Cell align="center">{(accuracy * 100).toFixed(0)}%</Table.Cell>
|
|
||||||
<Table.Cell align="center">{shots.toFixed(1)}</Table.Cell>
|
|
||||||
<Table.Cell align="center">{hits.toFixed(1)}</Table.Cell>
|
|
||||||
<Table.Cell align="center">{damageRatio.toFixed(2)}</Table.Cell>
|
|
||||||
<Table.Cell align="center">
|
|
||||||
{Math.round(damageReceived).toLocaleString(locale)}
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell align="center">{capturePoints.toFixed(2)}</Table.Cell>
|
|
||||||
</Table.Row>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(prev, next) => prev.tanks === next.tanks
|
|
||||||
);
|
|
||||||
Reference in new issue
Block a user