From 0deeaf0255583875ba98af3c273e9b2ec3b9760d Mon Sep 17 00:00:00 2001 From: Cole McGinn Date: Thu, 22 Jan 2026 15:28:54 -0500 Subject: [PATCH] table for pitch bonuses --- src/components/player/PitchSelectionChart.tsx | 64 +++++++++++++++++-- src/components/player/PlayerPage.tsx | 3 +- src/types/Player.ts | 19 +++++- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/components/player/PitchSelectionChart.tsx b/src/components/player/PitchSelectionChart.tsx index 19dc53f..418462e 100644 --- a/src/components/player/PitchSelectionChart.tsx +++ b/src/components/player/PitchSelectionChart.tsx @@ -1,11 +1,12 @@ import { usePlayerPitchSelection } from "@/hooks/api/Player"; -import { Chart as ChartJS, ArcElement, Tooltip, Legend, ChartOptions, LegendItem, Chart } from 'chart.js'; +import { Chart as ChartJS, ArcElement, Tooltip as ChartTooltip, Legend, ChartOptions, LegendItem, Chart } from 'chart.js'; import { Doughnut } from 'react-chartjs-2'; import { useSettings } from "../Settings"; import { LoadingMini } from "../Loading"; -import { Player } from "@/types/Player"; +import { pitchAbbrToName, Player, pitchAbbrToCategory } from "@/types/Player"; +import { Tooltip } from "../ui/Tooltip"; -ChartJS.register(ArcElement, Tooltip, Legend); +ChartJS.register(ArcElement, ChartTooltip, Legend); ChartJS.defaults.font.family = 'GeistSans, "GeistSans Fallback"'; const pitchTypeColors: Record = { @@ -39,7 +40,7 @@ export function PitchUsageChart({ id }: { id: string }) { } // Expected pitch selection stats from Player object -export function PitchSelectionChart({ player }: { player: Player}) { +export function PitchSelectionChart({ player }: { player: Player }) { const { settings } = useSettings(); @@ -132,3 +133,58 @@ export function PitchChart({ data, settings, pitchSelection, title = "Pitch Sele ); } +// table to show pitch type bonuses and category bonuses +export function PitchBonusesTable({ player }: { player: Player }) { + const pitchCategoryBonuses = player.pitch_category_bonuses; + const pitchTypeBonuses = player.pitch_type_bonuses; + const pitchTypes = player.pitch_selection ? Object.keys(player.pitch_selection) : []; + if (!pitchCategoryBonuses && !pitchTypeBonuses) { + return
; + } + + return ( +
+
Pitch Bonuses
+ + + + + + + + + + + {Object.entries(pitchAbbrToName) + .filter(([_abbr, name]) => pitchTypes.includes(name)) + .map(([abbr, name]) => { + const typeBonus = pitchTypeBonuses?.[abbr] ?? 0; + const category = pitchAbbrToCategory[abbr]; + const categoryBonus = pitchCategoryBonuses?.[category] ?? 0; + const totalBonus = typeBonus + categoryBonus; + + return ( + + + + + + + ); + })} + +
Pitch TypeType Bonus + + {pitchCategoryBonuses && Object.entries(pitchCategoryBonuses).map(([category, bonus]) => ( +
{category}: {bonus > 0 ? `+${(bonus * 100).toFixed(1)}%` : `${(bonus * 100).toFixed(1)}%`}
+ ))} + + }> + Category Bonus +
+
Total Bonus
{name}{typeBonus > 0 ? `+${(typeBonus * 100).toFixed(1)}%` : `${(typeBonus * 100).toFixed(1)}%`}{categoryBonus > 0 ? `+${(categoryBonus * 100).toFixed(1)}%` : `${(categoryBonus * 100).toFixed(1)}%`}{totalBonus > 0 ? `+${(totalBonus * 100).toFixed(1)}%` : `${(totalBonus * 100).toFixed(1)}%`}
+
+ ); + +} diff --git a/src/components/player/PlayerPage.tsx b/src/components/player/PlayerPage.tsx index 2082cde..97d7c2c 100644 --- a/src/components/player/PlayerPage.tsx +++ b/src/components/player/PlayerPage.tsx @@ -5,7 +5,7 @@ import { Player } from "@/types/Player"; import { usePlayer } from "@/hooks/api/Player"; import { useTeam } from "@/hooks/api/Team"; import PlayerAttributes from "./PlayerAttributes"; -import { PitchSelectionChart, PitchUsageChart } from "./PitchSelectionChart"; +import { PitchBonusesTable, PitchSelectionChart, PitchUsageChart } from "./PitchSelectionChart"; import Link from "next/link"; import { PlayerPageHeader } from "./PlayerPageHeader"; import PlayerStatsTables from "./PlayerStatsTables"; @@ -144,6 +144,7 @@ export function PlayerPage({ id }: PlayerPageProps) { ? :
Batter charts coming soon!
} + )} diff --git a/src/types/Player.ts b/src/types/Player.ts index 64be2bc..7e11036 100644 --- a/src/types/Player.ts +++ b/src/types/Player.ts @@ -740,7 +740,7 @@ export function mapBoon(raw: any): Boon | undefined { } } -const pitchAbbrToName: Record = { +export const pitchAbbrToName: Record = { 'FF': 'Fastball', 'SI': 'Sinker', 'FC': 'Cutter', @@ -752,6 +752,23 @@ const pitchAbbrToName: Record = { 'ST': 'Sweeper', } +// fast, offspeed, breaking +export const pitchAbbrToCategory: Record = { + 'FF': 'Fast', + 'SI': 'Fast', + 'FC': 'Fast', + 'SL': 'Breaking', + 'CU': 'Breaking', + 'KC': 'Breaking', + 'CH': 'Offspeed', + 'FS': 'Offspeed', + 'ST': 'Breaking', +} + +export function mapPitchTypeAbbrToName(abbr: string): string { + return pitchAbbrToName[abbr] || abbr; +} + function mapPitchSelection(raw: any): Record { if (!raw || !raw.PitchSelection || !raw.PitchTypes) { return {};