diff --git a/components/stats/PredictionScreen.tsx b/components/stats/PredictionScreen.tsx
new file mode 100644
index 00000000..42309467
--- /dev/null
+++ b/components/stats/PredictionScreen.tsx
@@ -0,0 +1,120 @@
+import { Game, Report } from "@/lib/Types";
+import { useEffect, useState } from "react";
+import { Bar } from "react-chartjs-2";
+
+function AllianceBuilder(props: {
+ teams: number[],
+ alliance: (number | undefined)[],
+ update: (index: number, team: number) => void,
+ name: string,
+ color: string
+ }) {
+ return (
+
+
{props.name} Alliance
+
+ {props.alliance.map((team, index) =>
+
+ )}
+
+
+ );
+}
+
+export default function PredictionScreen(props: { reports: Report[], teams: number[], game: Game }) {
+ const [reportsByTeam, setReportsByTeam] = useState>({});
+
+ // Array(length) constructor doesn't actually fill the array with undefined, so we have to do it manually
+ const [blueAlliance, setBlueAlliance] = useState<(number | undefined)[]>(Array(props.game.allianceSize).fill(undefined));
+ const [redAlliance, setRedAlliance] = useState<(number | undefined)[]>(Array(props.game.allianceSize).fill(undefined));
+
+ useEffect(() => {
+ const reportsByTeam = props.reports.reduce((acc, report) => {
+ if (!acc[report.robotNumber]) {
+ acc[report.robotNumber] = [];
+ }
+
+ acc[report.robotNumber].push(report);
+ return acc;
+ }, {} as Record);
+
+ setReportsByTeam(reportsByTeam);
+ }, [props.reports]);
+
+ function updateAlliance(setAlliance: (alliance: (number | undefined)[]) => void, alliance: (number | undefined)[],
+ index: number, team: number) {
+ alliance[index] = team;
+ setAlliance([...alliance]); // We have to create a new array for the update to work
+ }
+
+ const blueAllianceFilled = blueAlliance.filter((team) => team !== undefined);
+ const redAllianceFilled = redAlliance.filter((team) => team !== undefined);
+
+ const avgPointsBlueAllianceIndividual = blueAllianceFilled.map((team) => props.game.getAvgPoints(reportsByTeam[team!]));
+ const avgPointsRedAllianceIndividual = redAllianceFilled.map((team) => props.game.getAvgPoints(reportsByTeam[team!]));
+
+ const totalPointsBlueAlliance = avgPointsBlueAllianceIndividual.reduce((acc, points) => acc + points, 0);
+ const totalPointsRedAlliance = avgPointsRedAllianceIndividual.reduce((acc, points) => acc + points, 0);
+
+ const datasets = [];
+ for (let i = 0; i < Math.max(avgPointsBlueAllianceIndividual.length, avgPointsRedAllianceIndividual.length); i++) {
+ const blue = avgPointsBlueAllianceIndividual[i] || undefined;
+ const red = avgPointsRedAllianceIndividual[i] || undefined;
+
+ datasets.push({
+ data: [blue, red],
+ label: `Team ${blueAllianceFilled[i] || "Empty"} vs Team ${redAllianceFilled[i] || "Empty"}`,
+ backgroundColor: [`rgba(0, ${i * 255 / 2}, 235, 1)`, `rgba(235, ${i * 255 / 2}, 0, 1)`],
+ });
+ }
+
+ const pointDiff = totalPointsBlueAlliance - totalPointsRedAlliance;
+ let winner = "Tie";
+ let color = "";
+ if (pointDiff > 0) {
+ winner = "Blue Alliance";
+ color = "blue-500";
+ } else if (pointDiff < 0) {
+ winner = "Red Alliance";
+ color = "red-500";
+ }
+
+ return (
+
+
+
updateAlliance(setBlueAlliance, blueAlliance, index, team)} name="Blue" color="blue-500" />
+ updateAlliance(setRedAlliance, redAlliance, index, team)} name="Red" color="red-500" />
+
+
+ {pointDiff != 0 ? `${winner} wins by ${Math.abs(pointDiff)} points` : winner} ({totalPointsBlueAlliance} - {totalPointsRedAlliance})
+
+ {/* If we don't have bar graph in a div, it will vertically expand into infinity. Don't question it. - Renato */ }
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/lib/Types.ts b/lib/Types.ts
index bb91852c..2867e2d4 100644
--- a/lib/Types.ts
+++ b/lib/Types.ts
@@ -131,7 +131,7 @@ export enum League {
FTC = "FTC", FRC = "FRC"
}
-export class Game {
+export class Game {
name: string;
year: number;
diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx
index c6f38f47..10cec442 100644
--- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx
+++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx
@@ -14,6 +14,8 @@ import ClientAPI from "@/lib/client/ClientAPI";
import { team } from "slack";
import { NotLinkedToTba } from "@/lib/client/ClientUtils";
import { defaultGameId } from "@/lib/client/GameId";
+import PredictionScreen from "@/components/stats/PredictionScreen";
+import { games } from "@/lib/games";
const api = new ClientAPI("gearboxiscool");
@@ -110,18 +112,18 @@ export default function Stats(props: StatsPageProps) {
setPage(1);
}}
>
- Picklist (Beta)
+ Picklist
{
- // setPage(2);
+ setPage(2);
}}
>
- Prediction (Coming Soon!)
+ Prediction
{/*
Resync {" "}
@@ -138,11 +140,14 @@ export default function Stats(props: StatsPageProps) {
)}
- {page === 1
- ?
- : <>>
+ }
+ {
+ page === 2 &&
+
}
);