From eb95230a5f4e7f1cbc37ddf9723f00ff57957738 Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Mon, 23 Dec 2024 12:48:52 -0600 Subject: [PATCH] feat: start of charts --- .../src/components/Charts/ChartsControl.tsx | 32 ++++++ .../src/components/Charts/PlayersInPeriod.tsx | 99 +++++++++++++++++ .../components/PlayersByTierAndClass.tsx | 100 ------------------ .../components/Performance/Charts/index.tsx | 12 --- .../components/Performance/ModeSwitcher.tsx | 31 ------ packages/website/src/constants/tools.ts | 7 +- .../website/src/pages/tools/charts/_index.tsx | 29 +++++ .../src/pages/tools/charts/index.astro | 9 ++ .../src/pages/tools/performance/_index.tsx | 16 +-- packages/website/src/stores/charts.ts | 15 +++ .../src/stores/tankPerformanceEphemeral.ts | 7 -- 11 files changed, 192 insertions(+), 165 deletions(-) create mode 100644 packages/website/src/components/Charts/ChartsControl.tsx create mode 100644 packages/website/src/components/Charts/PlayersInPeriod.tsx delete mode 100644 packages/website/src/components/Performance/Charts/components/PlayersByTierAndClass.tsx delete mode 100644 packages/website/src/components/Performance/Charts/index.tsx delete mode 100644 packages/website/src/components/Performance/ModeSwitcher.tsx create mode 100644 packages/website/src/pages/tools/charts/_index.tsx create mode 100644 packages/website/src/pages/tools/charts/index.astro create mode 100644 packages/website/src/stores/charts.ts diff --git a/packages/website/src/components/Charts/ChartsControl.tsx b/packages/website/src/components/Charts/ChartsControl.tsx new file mode 100644 index 000000000..8c019dd77 --- /dev/null +++ b/packages/website/src/components/Charts/ChartsControl.tsx @@ -0,0 +1,32 @@ +import { Flex, Select, Text } from '@radix-ui/themes'; +import { Charts, type ChartsPeriod } from '../../stores/charts'; + +export function ChartsControl() { + const period = Charts.use((state) => state.period); + const mutateCharts = Charts.useMutation(); + + return ( + + Player count + { + mutateCharts((draft) => { + draft.period = period as ChartsPeriod; + }); + }} + > + + + Total + Past 120 days + Past 90 days + Past 60 days + This month + This week + Yesterday + + + + ); +} diff --git a/packages/website/src/components/Charts/PlayersInPeriod.tsx b/packages/website/src/components/Charts/PlayersInPeriod.tsx new file mode 100644 index 000000000..7d109ca99 --- /dev/null +++ b/packages/website/src/components/Charts/PlayersInPeriod.tsx @@ -0,0 +1,99 @@ +import { + TANK_CLASSES, + TIERS, + TIER_ROMAN_NUMERALS, + TankClass, + formatCompact, +} from '@blitzkit/core'; +import strings from '@blitzkit/core/lang/en-US.json'; +import { useStore } from '@nanostores/react'; +import { Box, Flex, Heading } from '@radix-ui/themes'; +import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions'; +import { awaitableTankDefinitions } from '../../core/awaitables/tankDefinitions'; +import { filterTank } from '../../core/blitzkit/filterTank'; +import { useAveragesExclusionRatio } from '../../hooks/useAveragesExclusionRatio'; +import { Charts } from '../../stores/charts'; +import { $tankFilters } from '../../stores/tankFilters'; +import { ThemedBar } from '../Nivo/ThemedBar'; + +const [tankDefinitions, averageDefinitions] = await Promise.all([ + awaitableTankDefinitions, + awaitableAverageDefinitions, +]); + +const averageDefinitionsArray = Object.entries(averageDefinitions.averages); + +export function PlayersInPeriod() { + const period = Charts.use((state) => state.period); + const ratio = useAveragesExclusionRatio(); + const filters = useStore($tankFilters); + const data = TIERS.map((tier) => { + const players: Record = { + 0: 0, + 1: 0, + 2: 0, + 3: 0, + }; + + averageDefinitionsArray.forEach(([idString, entry]) => { + const id = Number(idString); + const tank = tankDefinitions.tanks[id]; + + if ( + tank === undefined || + tank.tier !== tier || + !filterTank(filters, tank) + ) { + return; + } + + players[tank.class] += entry.samples[period] * ratio; + }); + + const bar: Record = { + tier: TIER_ROMAN_NUMERALS[tier], + }; + + TANK_CLASSES.forEach((c) => { + bar[strings.common.tank_class_short[c]] = players[c]; + }); + + return bar; + }); + + return ( + + Players by tier & class + + strings.common.tank_class_short[c])} + indexBy="tier" + margin={{ right: 90, bottom: 50, left: 90 }} + axisBottom={{ + legend: 'Tier', + legendPosition: 'middle', + legendOffset: 32, + }} + axisLeft={{ + legend: 'Players', + legendPosition: 'middle', + legendOffset: -60, + format: formatCompact, + }} + legends={[ + { + dataFrom: 'keys', + anchor: 'bottom-right', + direction: 'column', + translateX: 120, + itemWidth: 100, + itemHeight: 20, + }, + ]} + valueFormat={formatCompact} + /> + + + ); +} diff --git a/packages/website/src/components/Performance/Charts/components/PlayersByTierAndClass.tsx b/packages/website/src/components/Performance/Charts/components/PlayersByTierAndClass.tsx deleted file mode 100644 index d60694082..000000000 --- a/packages/website/src/components/Performance/Charts/components/PlayersByTierAndClass.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { - formatCompact, - TANK_CLASSES, - TankClass, - TIER_ROMAN_NUMERALS, - TIERS, -} from '@blitzkit/core'; -import strings from '@blitzkit/core/lang/en-US.json'; -import { useStore } from '@nanostores/react'; -import { Box, Flex, Heading } from '@radix-ui/themes'; -import { awaitableAverageDefinitions } from '../../../../core/awaitables/averageDefinitions'; -import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions'; -import { filterTank } from '../../../../core/blitzkit/filterTank'; -import { useAveragesExclusionRatio } from '../../../../hooks/useAveragesExclusionRatio'; -import { $tankFilters } from '../../../../stores/tankFilters'; -import { TankPerformanceEphemeral } from '../../../../stores/tankPerformanceEphemeral'; -import { ThemedBar } from '../../../Nivo/ThemedBar'; - -const [tankDefinitions, averageDefinitions] = await Promise.all([ - awaitableTankDefinitions, - awaitableAverageDefinitions, -]); - -const averageDefinitionsArray = Object.entries(averageDefinitions.averages); - -export function PlayersByTierAndClass() { - const playerCountPeriod = TankPerformanceEphemeral.use( - (state) => state.playerCountPeriod, - ); - const ratio = useAveragesExclusionRatio(); - const filters = useStore($tankFilters); - - return ( - - Players by tier & class - - { - const players: Record = { - 0: 0, - 1: 0, - 2: 0, - 3: 0, - }; - - averageDefinitionsArray.forEach(([idString, entry]) => { - const id = Number(idString); - const tank = tankDefinitions.tanks[id]; - - if ( - tank === undefined || - tank.tier !== tier || - !filterTank(filters, tank) - ) { - return; - } - - players[tank.class] += entry.samples[playerCountPeriod] * ratio; - }); - - const bar: Record = { - tier: TIER_ROMAN_NUMERALS[tier], - }; - - TANK_CLASSES.forEach((c) => { - bar[strings.common.tank_class_short[c]] = players[c]; - }); - - return bar; - })} - keys={TANK_CLASSES.map((c) => strings.common.tank_class_short[c])} - indexBy="tier" - margin={{ right: 90, bottom: 50, left: 90 }} - axisBottom={{ - legend: 'Tier', - legendPosition: 'middle', - legendOffset: 32, - }} - axisLeft={{ - legend: 'Players', - legendPosition: 'middle', - legendOffset: -60, - format: formatCompact, - }} - legends={[ - { - dataFrom: 'keys', - anchor: 'bottom-right', - direction: 'column', - translateX: 120, - itemWidth: 100, - itemHeight: 20, - }, - ]} - valueFormat={formatCompact} - /> - - - ); -} diff --git a/packages/website/src/components/Performance/Charts/index.tsx b/packages/website/src/components/Performance/Charts/index.tsx deleted file mode 100644 index 8c936d257..000000000 --- a/packages/website/src/components/Performance/Charts/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Flex } from '@radix-ui/themes'; -import { PlayersByTierAndClass } from './components/PlayersByTierAndClass'; - -export function Charts() { - return ( - - - - - - ); -} diff --git a/packages/website/src/components/Performance/ModeSwitcher.tsx b/packages/website/src/components/Performance/ModeSwitcher.tsx deleted file mode 100644 index ede901745..000000000 --- a/packages/website/src/components/Performance/ModeSwitcher.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Tabs } from '@radix-ui/themes'; -import { - TankPerformanceEphemeral, - TankPerformanceMode, -} from '../../stores/tankPerformanceEphemeral'; - -export function ModeSwitcher() { - const mode = TankPerformanceEphemeral.use((state) => state.mode); - const mutateTankPerformanceMode = TankPerformanceEphemeral.useMutation(); - - return ( - { - mutateTankPerformanceMode((draft) => { - draft.mode = Number(value) as TankPerformanceMode; - }); - }} - mb="5" - > - - - Table - - - Charts - - - - ); -} diff --git a/packages/website/src/constants/tools.ts b/packages/website/src/constants/tools.ts index 7c16e3dc0..e2a2d7358 100644 --- a/packages/website/src/constants/tools.ts +++ b/packages/website/src/constants/tools.ts @@ -179,14 +179,14 @@ export const tierListTool: Tool = { export const chartsTool: Tool = { id: 'charts', title: 'Charts', - description: 'Lots of Blitz charts', + description: 'Lots of graphs', pageDescription: 'Charts and graphs on anything and everything about tanks, players, and events in World of Tanks Blitz', button: { text: 'Nerd out', - color: 'yellow', + color: 'bronze', }, - image: '9s68NwR', + image: '6fhkljA', branches: ['main', 'dev', 'opentest', 'preview'], }; @@ -194,6 +194,7 @@ export const TOOLS: Tool[] = [ tankopediaTool, compareTool, performanceTool, + chartsTool, galleryTool, sessionTool, tierListTool, diff --git a/packages/website/src/pages/tools/charts/_index.tsx b/packages/website/src/pages/tools/charts/_index.tsx new file mode 100644 index 000000000..1dcd37693 --- /dev/null +++ b/packages/website/src/pages/tools/charts/_index.tsx @@ -0,0 +1,29 @@ +import { Flex } from '@radix-ui/themes'; +import { ChartsControl } from '../../../components/Charts/ChartsControl'; +import { PlayersInPeriod } from '../../../components/Charts/PlayersInPeriod'; +import { PageWrapper } from '../../../components/PageWrapper'; +import { FilterControl } from '../../../components/TankSearch/components/FilterControl'; +import { Charts } from '../../../stores/charts'; + +export function Page() { + return ( + + + + ); +} + +function Content() { + return ( + + + + + + + + + + + ); +} diff --git a/packages/website/src/pages/tools/charts/index.astro b/packages/website/src/pages/tools/charts/index.astro new file mode 100644 index 000000000..bf78728e5 --- /dev/null +++ b/packages/website/src/pages/tools/charts/index.astro @@ -0,0 +1,9 @@ +--- +import { chartsTool } from '../../../constants/tools'; +import ToolLayout from '../../../layouts/ToolLayout.astro'; +import { Page } from './_index'; +--- + + + + diff --git a/packages/website/src/pages/tools/performance/_index.tsx b/packages/website/src/pages/tools/performance/_index.tsx index 77d42815f..86aea4d74 100644 --- a/packages/website/src/pages/tools/performance/_index.tsx +++ b/packages/website/src/pages/tools/performance/_index.tsx @@ -1,12 +1,9 @@ import { PageWrapper } from '../../../components/PageWrapper'; -import { Charts } from '../../../components/Performance/Charts'; +import { PerformanceInfo } from '../../../components/Performance/PerformanceInfo'; import { PlayerCountControl } from '../../../components/Performance/PlayerCountControl'; import { TankTable } from '../../../components/Performance/Table'; import { FilterControl } from '../../../components/TankSearch/components/FilterControl'; -import { - TankPerformanceEphemeral, - TankPerformanceMode, -} from '../../../stores/tankPerformanceEphemeral'; +import { TankPerformanceEphemeral } from '../../../stores/tankPerformanceEphemeral'; import { TankPerformanceSort } from '../../../stores/tankPerformanceSort'; import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps'; @@ -21,18 +18,13 @@ export function Page({ skeleton }: MaybeSkeletonComponentProps) { } function Content({ skeleton }: MaybeSkeletonComponentProps) { - const mode = TankPerformanceEphemeral.use((state) => state.mode); - return ( - {/* */} - - {/* */} + - {mode === TankPerformanceMode.Table && } - {mode === TankPerformanceMode.Charts && } + ); } diff --git a/packages/website/src/stores/charts.ts b/packages/website/src/stores/charts.ts new file mode 100644 index 000000000..862bc552b --- /dev/null +++ b/packages/website/src/stores/charts.ts @@ -0,0 +1,15 @@ +import type { Samples } from '@blitzkit/core'; +import { create } from 'zustand'; +import { createContextualStore } from '../core/zustand/createContextualStore'; + +export interface Charts { + period: ChartsPeriod; +} + +export type ChartsPeriod = keyof Samples; + +export const Charts = createContextualStore(() => + create()(() => ({ + period: 'd_30', + })), +); diff --git a/packages/website/src/stores/tankPerformanceEphemeral.ts b/packages/website/src/stores/tankPerformanceEphemeral.ts index 912d141f5..1ec8714d3 100644 --- a/packages/website/src/stores/tankPerformanceEphemeral.ts +++ b/packages/website/src/stores/tankPerformanceEphemeral.ts @@ -4,12 +4,6 @@ import { createContextualStore } from '../core/zustand/createContextualStore'; export interface TankPerformanceEphemeral { playerCountPeriod: PlayerCountPeriod; - mode: TankPerformanceMode; -} - -export enum TankPerformanceMode { - Table, - Charts, } export type PlayerCountPeriod = keyof Samples; @@ -17,6 +11,5 @@ export type PlayerCountPeriod = keyof Samples; export const TankPerformanceEphemeral = createContextualStore(() => create()(() => ({ playerCountPeriod: 'd_30', - mode: TankPerformanceMode.Table, })), );