diff --git a/.vscode/settings.json b/.vscode/settings.json
index 17e5e3eb5..61f4ce672 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -102,6 +102,7 @@
"NEGATIVEX",
"NEGATIVEY",
"NEGATIVEZ",
+ "nivo",
"nmatname",
"noout",
"normalmap",
diff --git a/bun.lockb b/bun.lockb
index 509c67288..374b1c460 100644
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index bebbeda26..9197143a4 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"license": "MIT",
"dependencies": {
"@gltf-transform/core": "^3.9.0",
+ "@nivo/bar": "^0.87.0",
"@octokit/plugin-throttling": "^8.1.3",
"@octokit/rest": "^20.0.1",
"@prisma/client": "^5.4.1",
diff --git a/src/app/tools/tank-performance/components/Header.tsx b/src/app/tools/tank-performance/components/Header.tsx
index 96d8efe33..44a77b328 100644
--- a/src/app/tools/tank-performance/components/Header.tsx
+++ b/src/app/tools/tank-performance/components/Header.tsx
@@ -16,7 +16,7 @@ export function Header() {
const sort = useTankPerformanceSort();
return (
-
+
Tank
{tankPerformanceSortTypeNamesArray.map((type) => {
@@ -24,6 +24,7 @@ export function Header() {
return (
+
+
+ Players (30d) per tier
+
+
+
+ {
+ const tier = (index + 1) as Tier;
+ const tierTanks = awaitedAverageDefinitionsArray.filter(
+ ({ id }) => awaitedTankDefinitions[id].tier === tier,
+ );
+ const populations: Record = {
+ 'AT-SPG': 0,
+ heavyTank: 0,
+ lightTank: 0,
+ mediumTank: 0,
+ };
+
+ tierTanks.forEach((stats) => {
+ const tank = awaitedTankDefinitions[stats.id];
+ populations[tank.class] += stats.mu.battles * stats.samples;
+ });
+
+ TANK_CLASSES.forEach((tankClass) => {
+ populations[tankClass] = Math.round(populations[tankClass]);
+ });
+
+ return { tier: TIER_ROMAN_NUMERALS[tier], ...populations };
+ })}
+ keys={[...TANK_CLASSES].reverse()}
+ indexBy="tier"
+ colors={{ scheme: 'dark2' }}
+ label={(d) => numberFormat.format(d.value as number)}
+ margin={{ right: 96, bottom: 20, left: 40 }}
+ legendLabel={(d) =>
+ strings.common.tank_class_short[d.id as TankClass]
+ }
+ axisLeft={{
+ tickSize: 0,
+ format: (value) => numberFormat.format(value as number),
+ }}
+ axisBottom={{ tickSize: 0 }}
+ legends={[
+ {
+ dataFrom: 'keys',
+ anchor: 'bottom-right',
+ direction: 'column',
+ translateX: 120,
+ itemsSpacing: 4,
+ itemWidth: 100,
+ itemHeight: 20,
+ },
+ ]}
+ />
+
+
+
+ );
+}
diff --git a/src/app/tools/tank-performance/page.tsx b/src/app/tools/tank-performance/page.tsx
index 108dbb10c..fc21cd5f5 100644
--- a/src/app/tools/tank-performance/page.tsx
+++ b/src/app/tools/tank-performance/page.tsx
@@ -3,12 +3,14 @@ import { TankFiltersProvider } from '../../../stores/tankFilters';
import { FilterControl } from '../tankopedia/components/FilterControl';
import { Info } from './components/Info';
import { TankTable } from './components/Table';
+import { TierBreakdown } from './components/TierBreakdown';
export default function Page() {
return (
+
diff --git a/src/constants/tools.tsx b/src/constants/tools.tsx
index 7f365657e..bbfb3cbe5 100644
--- a/src/constants/tools.tsx
+++ b/src/constants/tools.tsx
@@ -111,7 +111,7 @@ export const tankPerformanceTool: Tool = {
id: 'tank-performance',
title: 'Tank performance',
description: 'Tank performance statistics',
- pageDescription: 'Tank performance statistics in World of Tanks Blitz',
+ pageDescription: 'Best tanks and their performances in World of Tanks Blitz',
button: {
text: 'View tanks',
color: 'jade',
diff --git a/src/core/nivo/theme.ts b/src/core/nivo/theme.ts
new file mode 100644
index 000000000..93a0ffe2a
--- /dev/null
+++ b/src/core/nivo/theme.ts
@@ -0,0 +1,10 @@
+import { Theme } from '@nivo/core';
+
+export const nivoTheme = {
+ theme: {
+ text: { fill: 'var(--gray-11)' },
+ grid: { line: { stroke: 'var(--gray-3)' } },
+ } satisfies Theme,
+ borderRadius: 2,
+ innerPadding: 1,
+};