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));
+}