From 2dcfd1345726c216529b0f50d7ecaf0911c04e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Mon, 1 Jul 2024 20:18:09 -0500 Subject: [PATCH] feat: sort --- .../tank-performance/components/Header.tsx | 95 ++++++++++-------- .../tank-performance/components/Info.tsx | 11 ++- .../tank-performance/components/TankRow.tsx | 19 ++-- .../tank-performance/components/Tanks.tsx | 98 +++++++++++++++++-- src/stores/tankPerformanceSort.ts | 41 ++++++++ 5 files changed, 199 insertions(+), 65 deletions(-) create mode 100644 src/stores/tankPerformanceSort.ts diff --git a/src/app/tools/tank-performance/components/Header.tsx b/src/app/tools/tank-performance/components/Header.tsx index dcd672b75..ff5095e43 100644 --- a/src/app/tools/tank-performance/components/Header.tsx +++ b/src/app/tools/tank-performance/components/Header.tsx @@ -1,49 +1,62 @@ -import { Table } from '@radix-ui/themes'; +'use client'; + +import { + CaretDownIcon, + CaretSortIcon, + CaretUpIcon, +} from '@radix-ui/react-icons'; +import { Flex, IconButton, Table } from '@radix-ui/themes'; +import { + tankPerformanceSortTypeNames, + tankPerformanceSortTypeNamesArray, + useTankPerformanceSort, +} from '../../../../stores/tankPerformanceSort'; export function Header() { + const sort = useTankPerformanceSort(); + return ( - + Tank - - Winrate - - - Players (30d) - - - Damage - - - Survival - - - XP - - - Kills - - - Spots - - - Accuracy - - - Shots - - - Hits - - - Dmg. ratio - - - Dmg. taken - - - Cap points - + {tankPerformanceSortTypeNamesArray.map((type) => { + const isSelected = sort.type === type; + + return ( + + + { + if (isSelected) { + useTankPerformanceSort.setState({ + direction: -sort.direction as 1 | -1, + }); + } else { + useTankPerformanceSort.setState({ type }); + } + }} + > + {!isSelected && } + {isSelected && ( + <> + {sort.direction === 1 && } + {sort.direction === -1 && } + + )} + + {tankPerformanceSortTypeNames[type]} + + + ); + })} ); diff --git a/src/app/tools/tank-performance/components/Info.tsx b/src/app/tools/tank-performance/components/Info.tsx index 0932d0c8b..6588dc43f 100644 --- a/src/app/tools/tank-performance/components/Info.tsx +++ b/src/app/tools/tank-performance/components/Info.tsx @@ -13,6 +13,7 @@ export function Info() { const ratio = awaitedDiscoveredIdsDefinitions.count / awaitedAverageDefinitions.scanned_players; + const samples = ratio * awaitedAverageDefinitions.sampled_players; return ( @@ -21,12 +22,12 @@ export function Info() { - Career stats based on{' '} - {numberFormat.format( - Math.round(ratio * awaitedAverageDefinitions.sampled_players), - )}{' '} + Career stats based on {numberFormat.format(Math.round(samples))}{' '} players with at least 5K career battles and 1 battle in the past 30 - days. Updated daily. + days; updated daily. +
+ May differ from BlitzStars because BlitzKit includes{' '} + {Math.round(samples / 1e4)}x more players.
diff --git a/src/app/tools/tank-performance/components/TankRow.tsx b/src/app/tools/tank-performance/components/TankRow.tsx index 145b0ac2f..9fe4e10cf 100644 --- a/src/app/tools/tank-performance/components/TankRow.tsx +++ b/src/app/tools/tank-performance/components/TankRow.tsx @@ -28,6 +28,11 @@ export const TankRow = memo( {numberFormat.format(Math.round(ratio * averages.samples))} + + {numberFormat.format( + Math.round(ratio * averages.mu.battles * averages.samples), + )} + {Math.round( averages.mu.damage_dealt / averages.mu.battles, @@ -49,13 +54,7 @@ export const TankRow = memo( {(averages.mu.spotted / averages.mu.battles).toFixed(2)} - {Math.round( - (averages.mu.hits / - averages.mu.battles / - (averages.mu.shots / averages.mu.battles)) * - 100, - )} - % + {Math.round((averages.mu.hits / averages.mu.shots) * 100)}% {(averages.mu.shots / averages.mu.battles).toFixed(1)} @@ -64,11 +63,7 @@ export const TankRow = memo( {(averages.mu.hits / averages.mu.battles).toFixed(1)} - {( - averages.mu.damage_dealt / - averages.mu.battles / - (averages.mu.damage_received / averages.mu.battles) - ).toFixed(2)} + {(averages.mu.damage_dealt / averages.mu.damage_received).toFixed(2)} {Math.round( diff --git a/src/app/tools/tank-performance/components/Tanks.tsx b/src/app/tools/tank-performance/components/Tanks.tsx index 953080a3b..8b156a9b0 100644 --- a/src/app/tools/tank-performance/components/Tanks.tsx +++ b/src/app/tools/tank-performance/components/Tanks.tsx @@ -4,19 +4,103 @@ import { Table } from '@radix-ui/themes'; import { Suspense, use, useMemo } from 'react'; import { averageDefinitionsArray } from '../../../../core/blitzkit/averageDefinitions'; import { tankDefinitions } from '../../../../core/blitzkit/tankDefinitions'; +import { useTankPerformanceSort } from '../../../../stores/tankPerformanceSort'; import { RowLoader } from './RowLoader'; import { TankRow } from './TankRow'; export function Tanks() { const awaitedTankDefinitions = use(tankDefinitions); const awaitedAverageDefinitionsArray = use(averageDefinitionsArray); - const tankAverages = useMemo( - () => - awaitedAverageDefinitionsArray.sort( - (a, b) => b.mu.wins / b.mu.battles - a.mu.wins / a.mu.battles, - ), - [], - ); + const sort = useTankPerformanceSort(); + const tankAverages = useMemo(() => { + switch (sort.type) { + case 'accuracy': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * (a.mu.hits / a.mu.shots - b.mu.hits / b.mu.shots), + ); + case 'battles': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.samples * a.mu.battles - b.samples * b.mu.battles), + ); + case 'capturePoints': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.mu.capture_points / a.mu.battles - + b.mu.capture_points / b.mu.battles), + ); + case 'damage': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.mu.damage_dealt / a.mu.battles - + b.mu.damage_dealt / b.mu.battles), + ); + case 'damageRatio': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.mu.damage_dealt / a.mu.damage_received - + b.mu.damage_dealt / b.mu.damage_received), + ); + case 'damageTaken': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.mu.damage_received / a.mu.battles - + b.mu.damage_received / b.mu.battles), + ); + case 'hits': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.mu.hits / a.mu.battles - b.mu.hits / b.mu.battles), + ); + case 'kills': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.mu.frags / a.mu.battles - b.mu.frags / b.mu.battles), + ); + case 'shots': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.mu.shots / a.mu.battles - b.mu.shots / b.mu.battles), + ); + case 'players': + return awaitedAverageDefinitionsArray.sort( + (a, b) => sort.direction * (a.samples - b.samples), + ); + case 'spots': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.mu.spotted / a.mu.battles - b.mu.spotted / b.mu.battles), + ); + case 'survival': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.mu.survived_battles / a.mu.battles - + b.mu.survived_battles / b.mu.battles), + ); + case 'winrate': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * + (a.mu.wins / a.mu.battles - b.mu.wins / b.mu.battles), + ); + case 'xp': + return awaitedAverageDefinitionsArray.sort( + (a, b) => + sort.direction * (a.mu.xp / a.mu.battles - b.mu.xp / b.mu.battles), + ); + } + }, [sort]); return ( diff --git a/src/stores/tankPerformanceSort.ts b/src/stores/tankPerformanceSort.ts new file mode 100644 index 000000000..2bd500a96 --- /dev/null +++ b/src/stores/tankPerformanceSort.ts @@ -0,0 +1,41 @@ +import { produce } from 'immer'; +import { create } from 'zustand'; + +export const tankPerformanceSortTypeNames = { + winrate: 'Winrate', + players: 'Players (30d)', + battles: 'Battles (career)', + damage: 'Damage', + survival: 'Survival', + xp: 'XP', + kills: 'Kills', + spots: 'Spots', + accuracy: 'Accuracy', + shots: 'Shots', + hits: 'Hits', + damageRatio: 'Dmg. ratio', + damageTaken: 'Dmg. taken', + capturePoints: 'Cap points', +} as const; + +export const tankPerformanceSortTypeNamesArray = Object.keys( + tankPerformanceSortTypeNames, +) as TankPerformanceSortType[]; + +type TankPerformanceSortType = keyof typeof tankPerformanceSortTypeNames; + +export interface TankPerformanceSort { + type: TankPerformanceSortType; + direction: -1 | 1; +} + +export const useTankPerformanceSort = create()(() => ({ + type: 'battles', + direction: -1, +})); + +export function mutateTankPerformanceSort( + recipe: (draft: TankPerformanceSort) => void, +) { + useTankPerformanceSort.setState(produce(recipe)); +}