From a91a339ec0455ec52d6a29398e80332e66bce5e1 Mon Sep 17 00:00:00 2001 From: ampersanded Date: Thu, 22 Aug 2024 22:20:26 -0700 Subject: [PATCH] epa over time chart --- app/[eventId]/[teamNumber]/client-page.tsx | 15 +- .../[teamNumber]/matches/matches.tsx | 4 + app/[eventId]/[teamNumber]/page.tsx | 5 + .../overview/[teamNumber]/client-page.tsx | 2 + .../overview/[teamNumber]/matches/matches.tsx | 4 + app/[eventId]/overview/overview-charts.tsx | 6 +- app/[eventId]/overview/scouting-charts.tsx | 6 +- components/epa-over-time.tsx | 129 ++++++++++++++++++ components/ui/chart.tsx | 7 +- components/youtube-embed.tsx | 2 +- 10 files changed, 167 insertions(+), 13 deletions(-) create mode 100644 components/epa-over-time.tsx diff --git a/app/[eventId]/[teamNumber]/client-page.tsx b/app/[eventId]/[teamNumber]/client-page.tsx index 81638e1..8115165 100644 --- a/app/[eventId]/[teamNumber]/client-page.tsx +++ b/app/[eventId]/[teamNumber]/client-page.tsx @@ -21,12 +21,23 @@ import {cn} from "@/lib/utils"; import KeyBindListener from "@/components/key-bind-listener"; import {Tooltip, TooltipContent, TooltipTrigger} from "@/components/ui/tooltip"; import Matches, {Match} from "@/app/[eventId]/[teamNumber]/matches/matches"; +import EPAOverTime from "@/components/epa-over-time"; export const teamDataSchema = z.object({ notes: z.string(), }); -export default function ClientPage({event, team, teamEntry, teamDetails, statistics, events, pastSeasons, matches}: { +export default function ClientPage({ + event, + team, + teamEntry, + teamDetails, + statistics, + events, + eventsList, + pastSeasons, + matches + }: { event: { id: number }, team: { rookieYear: number | null, state: string | null, school: string | null, number: number }, teamEntry: { @@ -60,6 +71,7 @@ export default function ClientPage({event, team, teamEntry, teamDetails, statist teamDetails: ReactNode, statistics: ReactNode, events: ReactNode, + eventsList: { eventKey: string, name: string }[] pastSeasons: ReactNode }) { const [status, setStatus] = useState(teamEntry.status as TeamStatus); @@ -222,6 +234,7 @@ export default function ClientPage({event, team, teamEntry, teamDetails, statist

Statistics

{statistics} +

Events

{events} diff --git a/app/[eventId]/[teamNumber]/matches/matches.tsx b/app/[eventId]/[teamNumber]/matches/matches.tsx index 6efa8c3..9cd3eb6 100644 --- a/app/[eventId]/[teamNumber]/matches/matches.tsx +++ b/app/[eventId]/[teamNumber]/matches/matches.tsx @@ -15,6 +15,10 @@ import MatchView from "@/app/[eventId]/[teamNumber]/matches/matchView"; export type Match = { key: string, + totalEPA: number, + autoEPA: number, + teleopEPA: number, + endgameEPA: number, eventKey: string, matchNumber: number, winningAlliance: "red" | "blue" | "", diff --git a/app/[eventId]/[teamNumber]/page.tsx b/app/[eventId]/[teamNumber]/page.tsx index 1d58ddd..997e948 100644 --- a/app/[eventId]/[teamNumber]/page.tsx +++ b/app/[eventId]/[teamNumber]/page.tsx @@ -266,6 +266,11 @@ export default async function Team({params}: { params: { eventId: string, teamNu ) ) } + eventsList={await prisma.teamEvent.findMany({ + where: { + teamNumber: teamEntry.teamNumber + } + })} matches={matches.map(value => ({ ...value, compLevel: value.compLevel as "qm" | "ef" | "qf" | "sf" | "f", diff --git a/app/[eventId]/overview/[teamNumber]/client-page.tsx b/app/[eventId]/overview/[teamNumber]/client-page.tsx index a4a3a92..f35c20b 100644 --- a/app/[eventId]/overview/[teamNumber]/client-page.tsx +++ b/app/[eventId]/overview/[teamNumber]/client-page.tsx @@ -18,6 +18,7 @@ import {Button} from "@/components/ui/button"; import EventCard from "@/app/[eventId]/[teamNumber]/event-card"; import ScoutingCharts from "@/app/[eventId]/overview/scouting-charts"; import Matches, {Match} from "@/app/[eventId]/overview/[teamNumber]/matches/matches"; +import EPAOverTime from "@/components/epa-over-time"; export default function ClientPage({event, team, teamEntry, matches, matchEntries, scoutedMatches, events}: { event: Event, @@ -246,6 +247,7 @@ export default function ClientPage({event, team, teamEntry, matches, matchEntrie )} +

Events

{ diff --git a/app/[eventId]/overview/[teamNumber]/matches/matches.tsx b/app/[eventId]/overview/[teamNumber]/matches/matches.tsx index 03e8f69..3217d8d 100644 --- a/app/[eventId]/overview/[teamNumber]/matches/matches.tsx +++ b/app/[eventId]/overview/[teamNumber]/matches/matches.tsx @@ -11,6 +11,10 @@ import MatchView from "./matchView"; export type Match = { key: string, + totalEPA: number, + autoEPA: number, + teleopEPA: number, + endgameEPA: number, eventKey: string, matchNumber: number, winningAlliance: "red" | "blue" | "", diff --git a/app/[eventId]/overview/overview-charts.tsx b/app/[eventId]/overview/overview-charts.tsx index ba5a659..59e50ba 100644 --- a/app/[eventId]/overview/overview-charts.tsx +++ b/app/[eventId]/overview/overview-charts.tsx @@ -1,6 +1,6 @@ "use client"; -import {Bar, BarChart, CartesianGrid, LabelList, Pie, PieChart} from "recharts"; +import {Bar, BarChart, CartesianGrid, LabelList, Pie, PieChart, XAxis} from "recharts"; import {ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent} from "@/components/ui/chart"; import {TeamEntry} from "@prisma/client"; @@ -123,8 +123,8 @@ export default function OverviewCharts({teamEntries}: { teamEntries: TeamEntry[] - }/> + }/> + diff --git a/app/[eventId]/overview/scouting-charts.tsx b/app/[eventId]/overview/scouting-charts.tsx index 5cdc1b8..e9b466c 100644 --- a/app/[eventId]/overview/scouting-charts.tsx +++ b/app/[eventId]/overview/scouting-charts.tsx @@ -3,7 +3,7 @@ import {MatchEntry} from "@prisma/client"; import React from "react"; import {ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent} from "@/components/ui/chart"; -import {Area, AreaChart, Bar, BarChart, CartesianGrid, LabelList, Pie, PieChart} from "recharts"; +import {Area, AreaChart, Bar, BarChart, CartesianGrid, LabelList, Pie, PieChart, XAxis} from "recharts"; import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card"; export default function ScoutingCharts({matches, forTeam}: { @@ -54,8 +54,8 @@ export default function ScoutingCharts({matches, forTeam}: { - }/> + }/> + diff --git a/components/epa-over-time.tsx b/components/epa-over-time.tsx new file mode 100644 index 0000000..af4ad9c --- /dev/null +++ b/components/epa-over-time.tsx @@ -0,0 +1,129 @@ +import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card"; +import React from "react"; +import {Area, AreaChart, CartesianGrid, XAxis, YAxis} from "recharts"; +import { + ChartConfig, + ChartContainer, + ChartLegend, + ChartLegendContent, + ChartTooltip, + ChartTooltipContent +} from "@/components/ui/chart"; +import {Match} from "@/app/[eventId]/[teamNumber]/matches/matches"; + +const chartConfig = { + totalEPA: { + label: "Total EPA", + color: "var(--chart-1)" + }, + autoEPA: { + label: "Auto EPA", + }, + teleopEPA: { + label: "Teleop EPA", + }, + endgameEPA: { + label: "Endgame EPA", + } +} satisfies ChartConfig + +export default function EPAOverTime({matches, events}: { + matches: Match[], + events: { eventKey: string, name: string }[] +}) { + const formatedMatches = [...matches] + .filter(value => value.totalEPA >= 0) + .sort((a, b) => a.startTime - b.startTime) + .map(value => ({ + ...value, + xAxis: `${events.find(value1 => value1.eventKey == value.eventKey)?.name ?? ""}\nMatch ${value.matchNumber}` + })); + + return ( + + + EPA Over Time + + + + + + ( +
+ {value.split("\n")[0]} + {value.split("\n")[1]} +
+ )} className={"w-40"}/> + } + cursor={false} + /> + value.split("\n")[0]} + /> + + + + + + }/> +
+
+
+
+ ); +} \ No newline at end of file diff --git a/components/ui/chart.tsx b/components/ui/chart.tsx index 49779cd..e18ac2e 100644 --- a/components/ui/chart.tsx +++ b/components/ui/chart.tsx @@ -141,14 +141,11 @@ const ChartTooltipContent = React.forwardRef< const [item] = payload const key = `${labelKey || item.dataKey || item.name || "value"}` const itemConfig = getPayloadConfigFromPayload(config, item, key) - let value = - !labelKey && typeof label === "string" + const value = + !labelKey ? config[label as keyof typeof config]?.label || label : itemConfig?.label - if (labelKey == "teamNumber") value = item.payload.teamNumber; - if (labelKey == "score") value = `${item.payload.score} Scores`; - if (labelFormatter) { return (
diff --git a/components/youtube-embed.tsx b/components/youtube-embed.tsx index 95fa7ab..9905904 100644 --- a/components/youtube-embed.tsx +++ b/components/youtube-embed.tsx @@ -2,8 +2,8 @@ import {cn} from "@/lib/utils"; import {Button} from "@/components/ui/button"; -import {Match} from "@/app/[eventId]/[teamNumber]/matches/matches"; import {GetEvent} from "@/lib/database/get-event"; +import {Match} from "@/app/[eventId]/overview/[teamNumber]/matches/matches"; export default function YoutubeEmbed({id, match, className}: { id: string | null,