From bcc13128575cc4e332a79488b5b052330b8caa5e Mon Sep 17 00:00:00 2001 From: Andres Date: Wed, 21 May 2025 22:02:34 +0200 Subject: [PATCH] add rolling winrate chart --- .../[id]/_components/winrate-trend-chart.tsx | 170 ++++++++++++++++++ src/app/(home)/players/[id]/user.tsx | 9 + 2 files changed, 179 insertions(+) create mode 100644 src/app/(home)/players/[id]/_components/winrate-trend-chart.tsx diff --git a/src/app/(home)/players/[id]/_components/winrate-trend-chart.tsx b/src/app/(home)/players/[id]/_components/winrate-trend-chart.tsx new file mode 100644 index 0000000..b4a9f8a --- /dev/null +++ b/src/app/(home)/players/[id]/_components/winrate-trend-chart.tsx @@ -0,0 +1,170 @@ +'use client' +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card' +import { + type ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from '@/components/ui/chart' +import { Slider } from '@/components/ui/slider' +import type { SelectGames } from '@/server/db/types' +import { useState } from 'react' +import { CartesianGrid, Line, LineChart, XAxis, YAxis } from 'recharts' + +const chartConfig = { + winrate: { + label: 'Winrate', + color: 'var(--color-emerald-500)', + }, +} satisfies ChartConfig + +export function WinrateTrendChart({ games }: { games: SelectGames[] }) { + const [gamesWindow, setGamesWindow] = useState(30) + + // Sort games by date (oldest to newest) + const sortedGames = [...games].sort( + (a, b) => a.gameTime.getTime() - b.gameTime.getTime() + ) + + // Calculate rolling winrate + const chartData = calculateRollingWinrate(sortedGames, gamesWindow) + + return ( + + +
+ Winrate Trends + Rolling winrate over time +
+
+
+ + Window size: {gamesWindow} games + +
+ setGamesWindow(value[0])} + min={5} + max={Math.min(100, games.length)} + step={1} + /> +
+
+ + + + + + new Date(value).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + }) + } + /> + `${value}%`} + domain={[0, 100]} + /> + { + const date = new Date(entry.payload.date) + const formattedDate = date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }) + return ( +
+ {value}% + + {formattedDate} + +
+ ) + }} + /> + } + /> + +
+
+
+ +
+ Showing rolling winrate over the last {gamesWindow} games +
+
+
+ ) +} + +function calculateRollingWinrate( + games: SelectGames[], + windowSize: number +): { date: Date; winrate: number }[] { + if (games.length === 0 || windowSize <= 0) return [] + + // Use all games if windowSize is greater than the number of games + const effectiveWindowSize = Math.min(windowSize, games.length) + + const result: { date: Date; winrate: number }[] = [] + + // We need at least windowSize games to start calculating + for (let i = effectiveWindowSize - 1; i < games.length; i++) { + // Get the window of games + const windowGames = games.slice( + Math.max(0, i - effectiveWindowSize + 1), + i + 1 + ) + + // Count wins in the window + const wins = windowGames.filter((game) => game.result === 'win').length + + // Calculate winrate as percentage + const winrate = Math.round((wins / windowGames.length) * 100) + + // Add data point + if (games[i]) { + result.push({ + date: games[i]!.gameTime, + winrate: winrate, + }) + } + } + + return result +} diff --git a/src/app/(home)/players/[id]/user.tsx b/src/app/(home)/players/[id]/user.tsx index 9c44507..e493c25 100644 --- a/src/app/(home)/players/[id]/user.tsx +++ b/src/app/(home)/players/[id]/user.tsx @@ -17,6 +17,7 @@ import { useState } from 'react' import { GamesTable } from '@/app/(home)/players/[id]/_components/games-table' import { MmrTrendChart } from '@/app/(home)/players/[id]/_components/mmr-trend-chart' +import { WinrateTrendChart } from '@/app/(home)/players/[id]/_components/winrate-trend-chart' import { OpponentsTable } from '@/app/(home)/players/[id]/_components/opponents-table' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Badge } from '@/components/ui/badge' @@ -382,6 +383,7 @@ export function UserInfo() { Match History Opponents MMR Trends + Winrate Trends Statistics Achievements @@ -443,6 +445,13 @@ export function UserInfo() { + +
+
+ +
+
+
{(rankedLeaderboard || lastRankedGame) && (