From 53aad06c1a416440f375699a6cfeaeb14b5bf784 Mon Sep 17 00:00:00 2001 From: ampersanded Date: Fri, 23 Aug 2024 13:56:24 -0700 Subject: [PATCH 1/6] compare --- .../overview/compare/client-page.tsx | 29 ++++++++++++ app/[eventId]/overview/compare/page.tsx | 47 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 app/[eventId]/overview/compare/client-page.tsx create mode 100644 app/[eventId]/overview/compare/page.tsx diff --git a/app/[eventId]/overview/compare/client-page.tsx b/app/[eventId]/overview/compare/client-page.tsx new file mode 100644 index 0000000..14cf893 --- /dev/null +++ b/app/[eventId]/overview/compare/client-page.tsx @@ -0,0 +1,29 @@ +'use client' + +import {Team, TeamEntry} from "@prisma/client"; +import Back from "@/components/back"; +import React from "react"; + +export default function ClientPage({eventId, a, b}: { + eventId: number, + a: { + team: Team, + entry: TeamEntry + }, + b: { + team: Team, + entry: TeamEntry + } +}) { + return ( + <> + +
+ +
+
+ +
+ + ); +} \ No newline at end of file diff --git a/app/[eventId]/overview/compare/page.tsx b/app/[eventId]/overview/compare/page.tsx new file mode 100644 index 0000000..e526ba3 --- /dev/null +++ b/app/[eventId]/overview/compare/page.tsx @@ -0,0 +1,47 @@ +'use server' + +import prisma from "@/lib/prisma"; +import NotFound from "@/app/not-found"; +import React from "react"; +import ClientPage from "@/app/[eventId]/overview/compare/client-page"; + +export default async function Compare({params, searchParams}: { + params: { eventId: string } + searchParams: { a?: string, b?: string } +}) { + const a = await prisma.team.findUnique( + { + where: { + number: +(searchParams.a ?? -1) + } + } + ); + const b = await prisma.team.findUnique( + { + where: { + number: +(searchParams.b ?? -1) + } + } + ); + if (!a || !b) return ; + + const aEntry = await prisma.teamEntry.findMany( + { + where: { + eventId: +params.eventId, + teamNumber: a.number + } + } + ); + const bEntry = await prisma.teamEntry.findMany( + { + where: { + eventId: +params.eventId, + teamNumber: b.number + } + } + ); + if (!aEntry || !bEntry) return ; + + return ; +} \ No newline at end of file From 623678428f837ea0ace3630f0ed86396fb3f75a9 Mon Sep 17 00:00:00 2001 From: ampersanded Date: Fri, 23 Aug 2024 14:34:33 -0700 Subject: [PATCH 2/6] compare teams --- .../overview/[teamNumber]/client-page.tsx | 54 ++-- .../overview/compare/client-page.tsx | 239 +++++++++++++++++- app/[eventId]/overview/compare/page.tsx | 20 +- 3 files changed, 283 insertions(+), 30 deletions(-) diff --git a/app/[eventId]/overview/[teamNumber]/client-page.tsx b/app/[eventId]/overview/[teamNumber]/client-page.tsx index 8a571f5..66a122a 100644 --- a/app/[eventId]/overview/[teamNumber]/client-page.tsx +++ b/app/[eventId]/overview/[teamNumber]/client-page.tsx @@ -47,33 +47,6 @@ export default function ClientPage({ )); - function epaValue(epa: number, deviation: number) { - let arrow; - let placement; - if (deviation > 0.2) { - arrow = (); - placement = "Above"; - } else if (deviation > -0.2) { - arrow = (); - placement = "Around"; - } else { - arrow = (); - placement = "Below"; - } - - return ( - -

{epa.toFixed(1)}

- {arrow} - - } - content={`${placement} average; ${withOrdinalSuffix((percentile(deviation) * 100))} percentile`} - /> - ); - } - return ( <> @@ -283,4 +256,31 @@ export default function ClientPage({ {previousSeasons} ); +} + +export function epaValue(epa: number, deviation: number) { + let arrow; + let placement; + if (deviation > 0.2) { + arrow = (); + placement = "Above"; + } else if (deviation > -0.2) { + arrow = (); + placement = "Around"; + } else { + arrow = (); + placement = "Below"; + } + + return ( + +

{epa.toFixed(1)}

+ {arrow} + + } + content={`${placement} average; ${withOrdinalSuffix((percentile(deviation) * 100))} percentile`} + /> + ); } \ No newline at end of file diff --git a/app/[eventId]/overview/compare/client-page.tsx b/app/[eventId]/overview/compare/client-page.tsx index 14cf893..7bfb9c3 100644 --- a/app/[eventId]/overview/compare/client-page.tsx +++ b/app/[eventId]/overview/compare/client-page.tsx @@ -3,26 +3,261 @@ import {Team, TeamEntry} from "@prisma/client"; import Back from "@/components/back"; import React from "react"; +import {Separator} from "@/components/ui/separator"; +import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from "@/components/ui/dropdown-menu"; +import {Button} from "@/components/ui/button"; +import {Edit, MoreVertical, ScanEye} from "lucide-react"; +import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card"; +import ThreatGrade from "@/components/threat-grade"; +import {ThreatGradeType} from "@/lib/database/set-threat-grade"; +import StatusBadge from "@/components/status-badge"; +import {TeamStatus} from "@/lib/database/set-team-statues"; +import RichTextarea from "@/components/rich-textarea"; +import QuickTooltip from "@/components/quick-tooltip"; +import {epaValue} from "@/app/[eventId]/overview/[teamNumber]/client-page"; export default function ClientPage({eventId, a, b}: { eventId: number, a: { team: Team, entry: TeamEntry + matches: { count: number } }, b: { team: Team, entry: TeamEntry + matches: { count: number } } }) { + function teamHeader(teamData: typeof a | typeof b) { + return ( +
+
+
+

{teamData.entry.name}

+

Team {teamData.team.number}

+
+ + + + + + + + + Make Changes + + + + + + View Overview + + + + +
+ +
+ ); + } + + function teamData(teamData: typeof a | typeof b) { + return ( +
+ + + Team Details + + +
+

Region

+

{teamData.team.state}

+
+
+

School

+

+ {teamData.team.school?.replace("&", "and")} +

+
+
+

Rookie year

+

{teamData.team.rookieYear}

+
+
+
+ + + Threat Grade + + + + + + + + Status + + +
+

Current Status

+ +
+
+
+ { + teamData.entry.notes != "[{\"children\":[{\"text\":\"\"}],\"type\":\"p\"}]" && ( + + + Notes + + + + + + ) + } +
+ ); + } + + function statistics(teamData: typeof a | typeof b) { + return ( +
+

Statistics

+ {teamData.matches.count == 0 ? ( +

+ This team has not competed in any matches this season, yet. +

+ ) : ( +
+ + + Record + + +
+

Wins

+

{teamData.entry.wins}

+
+
+

Losses

+

{teamData.entry.losses}

+
+
+

Total

+

{(teamData.entry.wins ?? 0) + (teamData.entry.ties ?? 0) + (teamData.entry.losses ?? 0)}

+
+
+

Win rate

+

+ + {((teamData.entry.wins ?? 0) / ((teamData.entry.wins ?? 0) + (teamData.entry.losses ?? 0)) * 100).toFixed()} + of 100 + + } + content={`Theoretically would win ${ + ((teamData.entry.wins ?? 0) / ((teamData.entry.wins ?? 0) + (teamData.entry.losses ?? 0)) * 100).toFixed() + } out of 100 matches if put up against the same opponents`} + /> +

+
+
+
+ + + + + +
+

World

+

{teamData.entry.worldRank} of {teamData.entry.worldTotal} +

+
+
+

Country

+

{teamData.entry.countyRank} of {teamData.entry.countyTotal}

+
+
+

District

+

{teamData.entry.districtRank} of {teamData.entry.districtTotal}

+
+
+

Event

+

{teamData.entry.eventRank} of {teamData.entry.eventTotal} +

+
+
+
+ + + Expected Points Added + }/> + + +
+

Auto

+ {epaValue(teamData.entry.autoEPA ?? 0, teamData.entry.autoDeviation ?? 0)} +
+
+

Teleop

+ {epaValue(teamData.entry.teleopEPA ?? 0, teamData.entry.teleopDeviation ?? 0)} +
+
+

Endgame

+ {epaValue(teamData.entry.endgameEPA ?? 0, teamData.entry.endgameDeviation ?? 0)} +
+
+

Total

+ {epaValue(teamData.entry.totalEPA ?? 0, teamData.entry.totalDeviation ?? 0)} +
+
+
+
+ )} +
+ ); + } + return ( <>
- + {teamHeader(a)} + {teamData(a)} +
+ {teamHeader(b)} +
+ {statistics(b)} + {teamData(b)} + {statistics(a)}
- +
+ {teamHeader(a)} + {teamHeader(b)} +
+
+ {teamData(a)} + {teamData(b)} +
+
+ {statistics(a)} + {statistics(b)} +
); diff --git a/app/[eventId]/overview/compare/page.tsx b/app/[eventId]/overview/compare/page.tsx index e526ba3..7380a85 100644 --- a/app/[eventId]/overview/compare/page.tsx +++ b/app/[eventId]/overview/compare/page.tsx @@ -43,5 +43,23 @@ export default async function Compare({params, searchParams}: { ); if (!aEntry || !bEntry) return ; - return ; + const aMatchEntries = await prisma.matchEntry.count( + { + where: { + eventId: aEntry[0].eventId, + teamEntryId: aEntry[0].id + } + } + ); + const bMatchEntries = await prisma.matchEntry.count( + { + where: { + eventId: bEntry[0].eventId, + teamEntryId: bEntry[0].id + } + } + ); + + return ; } \ No newline at end of file From de464d203285b0940ae4de3d75702cde83bf44dc Mon Sep 17 00:00:00 2001 From: ampersanded Date: Fri, 23 Aug 2024 22:50:33 -0700 Subject: [PATCH 3/6] compare teams --- app/[eventId]/[teamNumber]/client-page.tsx | 6 +- .../overview/[teamNumber]/client-page.tsx | 2 +- .../overview/compare/client-page.tsx | 12 +- .../overview/compare/epa-over-time.tsx | 141 ++++++++++++++++++ app/[eventId]/overview/compare/page.tsx | 68 ++++++++- components/epa-over-time.tsx | 4 +- 6 files changed, 217 insertions(+), 16 deletions(-) create mode 100644 app/[eventId]/overview/compare/epa-over-time.tsx diff --git a/app/[eventId]/[teamNumber]/client-page.tsx b/app/[eventId]/[teamNumber]/client-page.tsx index 992e651..aee240f 100644 --- a/app/[eventId]/[teamNumber]/client-page.tsx +++ b/app/[eventId]/[teamNumber]/client-page.tsx @@ -232,7 +232,11 @@ export default function ClientPage({

Statistics

{statistics} - + { + matches.length != 0 && ( +
+ ) + }

Events

{events} diff --git a/app/[eventId]/overview/[teamNumber]/client-page.tsx b/app/[eventId]/overview/[teamNumber]/client-page.tsx index 66a122a..e6f4ca5 100644 --- a/app/[eventId]/overview/[teamNumber]/client-page.tsx +++ b/app/[eventId]/overview/[teamNumber]/client-page.tsx @@ -227,9 +227,9 @@ export default function ClientPage({ + )} -

Events

{ diff --git a/app/[eventId]/overview/compare/client-page.tsx b/app/[eventId]/overview/compare/client-page.tsx index 7bfb9c3..fcb07d0 100644 --- a/app/[eventId]/overview/compare/client-page.tsx +++ b/app/[eventId]/overview/compare/client-page.tsx @@ -1,6 +1,6 @@ 'use client' -import {Team, TeamEntry} from "@prisma/client"; +import {Team, TeamEntry, TeamEvent} from "@prisma/client"; import Back from "@/components/back"; import React from "react"; import {Separator} from "@/components/ui/separator"; @@ -15,18 +15,21 @@ import {TeamStatus} from "@/lib/database/set-team-statues"; import RichTextarea from "@/components/rich-textarea"; import QuickTooltip from "@/components/quick-tooltip"; import {epaValue} from "@/app/[eventId]/overview/[teamNumber]/client-page"; +import EPAOverTime from "@/app/[eventId]/overview/compare/epa-over-time"; export default function ClientPage({eventId, a, b}: { eventId: number, a: { team: Team, entry: TeamEntry - matches: { count: number } + matches: any[], + events: TeamEvent[] }, b: { team: Team, entry: TeamEntry - matches: { count: number } + matches: any[], + events: TeamEvent[] } }) { function teamHeader(teamData: typeof a | typeof b) { @@ -130,7 +133,7 @@ export default function ClientPage({eventId, a, b}: { return (

Statistics

- {teamData.matches.count == 0 ? ( + {teamData.matches.length == 0 ? (

This team has not competed in any matches this season, yet.

@@ -259,6 +262,7 @@ export default function ClientPage({eventId, a, b}: { {statistics(b)}
+ ); } \ No newline at end of file diff --git a/app/[eventId]/overview/compare/epa-over-time.tsx b/app/[eventId]/overview/compare/epa-over-time.tsx new file mode 100644 index 0000000..6a53d41 --- /dev/null +++ b/app/[eventId]/overview/compare/epa-over-time.tsx @@ -0,0 +1,141 @@ +import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card"; +import React, {useState} from "react"; +import {CartesianGrid, Line, LineChart, XAxis, YAxis} from "recharts"; +import { + ChartConfig, + ChartContainer, + ChartLegend, + ChartLegendContent, + ChartTooltip, + ChartTooltipContent +} from "@/components/ui/chart"; +import {Team, TeamEvent} from "@prisma/client"; +import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select"; + +export default function EPAOverTime({a, b}: { + a: { + team: Team, + matches: any[], + events: TeamEvent[] + }, + b: { + team: Team, + matches: any[], + events: TeamEvent[] + } +}) { + const [type, setType] = useState("totalEPA"); + + const chartConfig = { + a: { + label: a.team.number + }, + b: { + label: b.team.number + } + } satisfies ChartConfig + + const matches = [ + ...a.matches.map(value => ({...value, type: "a"})), + ...b.matches.map(value => ({...value, type: "b"})) + ].filter(value => value.totalEPA >= 0).sort((a, b) => a.startTime - b.startTime); + + const chart: any[] = []; + let lastA: number | undefined = undefined; + let lastB: number | undefined = undefined; + + matches.forEach(value => { + if (value.type === "a") { + lastA = value[type]; + } else if (value.type === "b") { + lastB = value[type]; + } + + chart.push({ + time: value.startTime, + a: lastA, + b: lastB + }); + }); + + return ( + + +
+ EPA Over Time + +
+
+ + + + + + } + labelFormatter={(value) => { + return new Date(value * 1000).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + hour: "numeric", + minute: "numeric", + hour12: true + }).replace(" at ", ", "); + }} + cursor={false} + /> + { + return `${ + new Date(value * 1000).toLocaleDateString("en-US", { + month: "short", + day: "numeric" + }) + } `; + }} + /> + + + + }/> + + + +
+ ); +} \ No newline at end of file diff --git a/app/[eventId]/overview/compare/page.tsx b/app/[eventId]/overview/compare/page.tsx index 7380a85..844e076 100644 --- a/app/[eventId]/overview/compare/page.tsx +++ b/app/[eventId]/overview/compare/page.tsx @@ -43,23 +43,75 @@ export default async function Compare({params, searchParams}: { ); if (!aEntry || !bEntry) return ; - const aMatchEntries = await prisma.matchEntry.count( - { + const aMatches = await Promise.all( + (await prisma.matchEntry.findMany({ where: { eventId: aEntry[0].eventId, - teamEntryId: aEntry[0].id + teamEntryId: aEntry[0].id, + }, + })).map(async (value) => { + const match = await prisma.match.findUnique({ + where: { + key: value.matchKey ?? undefined, + }, + }); + return { + ...value, + ...(match ?? {}), // Safely spread the match object if it exists, otherwise spread an empty object + }; + }) + ); + const bMatches = await Promise.all( + (await prisma.matchEntry.findMany({ + where: { + eventId: bEntry[0].eventId, + teamEntryId: bEntry[0].id, + }, + })).map(async (value) => { + const match = await prisma.match.findUnique({ + where: { + key: value.matchKey ?? undefined, + }, + }); + return { + ...value, + ...(match ?? {}), // Safely spread the match object if it exists, otherwise spread an empty object + }; + }) + ); + + const aEvents = await prisma.teamEvent.findMany( + { + where: { + teamNumber: a.number } } ); - const bMatchEntries = await prisma.matchEntry.count( + const bEvents = await prisma.teamEvent.findMany( { where: { - eventId: bEntry[0].eventId, - teamEntryId: bEntry[0].id + teamNumber: b.number } } ); - return ; + console.log(JSON.stringify(aMatches)) + + return ( + + ); } \ No newline at end of file diff --git a/components/epa-over-time.tsx b/components/epa-over-time.tsx index af4ad9c..3fdc941 100644 --- a/components/epa-over-time.tsx +++ b/components/epa-over-time.tsx @@ -36,11 +36,11 @@ export default function EPAOverTime({matches, events}: { .sort((a, b) => a.startTime - b.startTime) .map(value => ({ ...value, - xAxis: `${events.find(value1 => value1.eventKey == value.eventKey)?.name ?? ""}\nMatch ${value.matchNumber}` + xAxis: `${events.find(value1 => value1.eventKey == value.eventKey)?.name ?? ""} \nMatch ${value.matchNumber}` })); return ( - + EPA Over Time From fa3f66f5947f7bc296c45ec6e33faa04c779210b Mon Sep 17 00:00:00 2001 From: ampersanded Date: Sat, 24 Aug 2024 12:02:17 -0700 Subject: [PATCH 4/6] compare teams --- .../overview/compare/client-page.tsx | 54 +++++++++++++++-- app/[eventId]/overview/compare/page.tsx | 58 ++++++++++++++++++- 2 files changed, 104 insertions(+), 8 deletions(-) diff --git a/app/[eventId]/overview/compare/client-page.tsx b/app/[eventId]/overview/compare/client-page.tsx index fcb07d0..e077390 100644 --- a/app/[eventId]/overview/compare/client-page.tsx +++ b/app/[eventId]/overview/compare/client-page.tsx @@ -16,6 +16,7 @@ import RichTextarea from "@/components/rich-textarea"; import QuickTooltip from "@/components/quick-tooltip"; import {epaValue} from "@/app/[eventId]/overview/[teamNumber]/client-page"; import EPAOverTime from "@/app/[eventId]/overview/compare/epa-over-time"; +import PreviousSeasons, {columns, Year} from "@/components/previous-seasons"; export default function ClientPage({eventId, a, b}: { eventId: number, @@ -23,13 +24,15 @@ export default function ClientPage({eventId, a, b}: { team: Team, entry: TeamEntry matches: any[], - events: TeamEvent[] + events: TeamEvent[], + previousYears: Year[] }, b: { team: Team, entry: TeamEntry matches: any[], - events: TeamEvent[] + events: TeamEvent[], + previousYears: Year[] } }) { function teamHeader(teamData: typeof a | typeof b) { @@ -132,7 +135,8 @@ export default function ClientPage({eventId, a, b}: { function statistics(teamData: typeof a | typeof b) { return (
-

Statistics

+

Statistics

+ {teamData.matches.length == 0 ? (

This team has not competed in any matches this season, yet. @@ -241,12 +245,30 @@ export default function ClientPage({eventId, a, b}: {

{teamHeader(a)} {teamData(a)} + {statistics(a)} + { + a.previousYears.length == 0 ? ( +

+ This team has not competed in any previous seasons, yet. +

+ ) : ( + + ) + }
{teamHeader(b)}
- {statistics(b)} {teamData(b)} - {statistics(a)} + {statistics(b)} + { + b.previousYears.length == 0 ? ( +

+ This team has not competed in any previous seasons, yet. +

+ ) : ( + + ) + }
@@ -263,6 +285,28 @@ export default function ClientPage({eventId, a, b}: {
+
+
+ { + a.previousYears.length == 0 ? ( +

+ This team has not competed in any previous seasons, yet. +

+ ) : ( + + ) + } + { + b.previousYears.length == 0 ? ( +

+ This team has not competed in any previous seasons, yet. +

+ ) : ( + + ) + } +
+
); } \ No newline at end of file diff --git a/app/[eventId]/overview/compare/page.tsx b/app/[eventId]/overview/compare/page.tsx index 844e076..46f2975 100644 --- a/app/[eventId]/overview/compare/page.tsx +++ b/app/[eventId]/overview/compare/page.tsx @@ -9,6 +9,15 @@ export default async function Compare({params, searchParams}: { params: { eventId: string } searchParams: { a?: string, b?: string } }) { + const event = await prisma.event.findUnique( + { + where: { + id: +params.eventId + } + } + ); + if (!event) return ; + const a = await prisma.team.findUnique( { where: { @@ -95,7 +104,48 @@ export default async function Compare({params, searchParams}: { } ); - console.log(JSON.stringify(aMatches)) + const aPastSeasons = (await prisma.teamPastSeason.findMany( + { + where: { + teamNumber: a.number, + year: { + lt: event.year + } + } + } + )).map(value => ({ + year: value.year, + winRate: value.winrate, + rank: { + rank: value.rank, + of: value.totalTeams + }, + epa: { + epa: value.epa, + percentile: value.percentile + } + })).sort((a, b) => b.year - a.year); + const bPastSeasons = (await prisma.teamPastSeason.findMany( + { + where: { + teamNumber: b.number, + year: { + lt: event.year + } + } + } + )).map(value => ({ + year: value.year, + winRate: value.winrate, + rank: { + rank: value.rank, + of: value.totalTeams + }, + epa: { + epa: value.epa, + percentile: value.percentile + } + })).sort((a, b) => b.year - a.year); return ( ); From f56ee8d82d8a90af31dd2f19a47a952cef9a0100 Mon Sep 17 00:00:00 2001 From: ampersanded Date: Sat, 24 Aug 2024 16:12:54 -0700 Subject: [PATCH 5/6] compare teams --- .../overview/compare/client-page.tsx | 8 +- app/[eventId]/overview/compare/page.tsx | 78 ++++++++++---- .../overview/compare/team-dropdown.tsx | 102 ++++++++++++++++++ 3 files changed, 163 insertions(+), 25 deletions(-) create mode 100644 app/[eventId]/overview/compare/team-dropdown.tsx diff --git a/app/[eventId]/overview/compare/client-page.tsx b/app/[eventId]/overview/compare/client-page.tsx index e077390..5950964 100644 --- a/app/[eventId]/overview/compare/client-page.tsx +++ b/app/[eventId]/overview/compare/client-page.tsx @@ -1,7 +1,6 @@ 'use client' import {Team, TeamEntry, TeamEvent} from "@prisma/client"; -import Back from "@/components/back"; import React from "react"; import {Separator} from "@/components/ui/separator"; import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from "@/components/ui/dropdown-menu"; @@ -37,7 +36,7 @@ export default function ClientPage({eventId, a, b}: { }) { function teamHeader(teamData: typeof a | typeof b) { return ( -
+

{teamData.entry.name}

@@ -241,7 +240,6 @@ export default function ClientPage({eventId, a, b}: { return ( <> -
{teamHeader(a)} {teamData(a)} @@ -255,9 +253,7 @@ export default function ClientPage({eventId, a, b}: { ) } -
- {teamHeader(b)} -
+ {teamHeader(b)} {teamData(b)} {statistics(b)} { diff --git a/app/[eventId]/overview/compare/page.tsx b/app/[eventId]/overview/compare/page.tsx index 46f2975..60b76df 100644 --- a/app/[eventId]/overview/compare/page.tsx +++ b/app/[eventId]/overview/compare/page.tsx @@ -4,6 +4,8 @@ import prisma from "@/lib/prisma"; import NotFound from "@/app/not-found"; import React from "react"; import ClientPage from "@/app/[eventId]/overview/compare/client-page"; +import {TeamDropdown} from "@/app/[eventId]/overview/compare/team-dropdown"; +import Back from "@/components/back"; export default async function Compare({params, searchParams}: { params: { eventId: string } @@ -18,17 +20,52 @@ export default async function Compare({params, searchParams}: { ); if (!event) return ; + const eventTeams = await Promise.all( + (await prisma.teamEntry.findMany( + { + where: { + eventId: event.id + } + } + )).map(async value => (await prisma.team.findMany( + { + where: { + number: value.teamNumber ?? undefined + } + } + ))[0]) + ); + + function teamSelector(a: string | undefined, b: string | undefined) { + if (!event) return; + + return ( + <> + +
+ +

vs

+ +
+ + ); + } + + if (!searchParams.a || !searchParams.b) { + return teamSelector(searchParams.a, searchParams.b); + } + const a = await prisma.team.findUnique( { where: { - number: +(searchParams.a ?? -1) + number: +searchParams.a } } ); const b = await prisma.team.findUnique( { where: { - number: +(searchParams.b ?? -1) + number: +searchParams.b } } ); @@ -148,22 +185,25 @@ export default async function Compare({params, searchParams}: { })).sort((a, b) => b.year - a.year); return ( - + <> + {teamSelector(searchParams.a, searchParams.b)} + + ); } \ No newline at end of file diff --git a/app/[eventId]/overview/compare/team-dropdown.tsx b/app/[eventId]/overview/compare/team-dropdown.tsx new file mode 100644 index 0000000..abb8a22 --- /dev/null +++ b/app/[eventId]/overview/compare/team-dropdown.tsx @@ -0,0 +1,102 @@ +'use client' + +import * as React from "react" + +import {Button} from "@/components/ui/button" +import {Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList,} from "@/components/ui/command" +import {Drawer, DrawerContent, DrawerTrigger,} from "@/components/ui/drawer" +import {Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover" +import {Team} from "@prisma/client"; +import {ChevronsUpDown} from "lucide-react"; + +export function TeamDropdown({eventId, teams, a, b, side}: { + eventId: number, + teams: Team[], + a: string | undefined, + b: string | undefined, + side: "a" | "b" +}) { + const [open, setOpen] = React.useState(false); + const [selectedTeam, setSelectedTeam] = React.useState( + teams.find(value => `${value.number}` == (side == "a" ? a : b)) ?? null + ); + + if (typeof window !== "undefined" && window.innerWidth > 768) { + return ( + + + + + + + + + ) + } + + return ( + + + + + +
+ +
+
+
+ ) +} + +function StatusList({ + setOpen, + teams, + eventId, + a, + b, + side + }: { + setOpen: (open: boolean) => void + teams: Team[], + eventId: number, a: string | undefined, b: string | undefined, side: "a" | "b" +}) { + return ( + + + + No results found. + + {teams.map((team) => ( + !((side == "a" && b == `${team.number}`) || (side == "b" && a == `${team.number}`)) && ( + { + setOpen(false) + if (side == "a") { + a = value; + } else { + b = value; + } + window.open(`/${eventId}/overview/compare?${side == "a" ? `a=${team.number}` : (a ? `a=${a}` : "")}&${side == "b" ? `b=${team.number}` : (b ? `b=${b}` : "")}`, "_self"); + }} + > + {label(team)} + + ) + ))} + + + + ); +} + +function label(team: Team) { + return `Team ${team.number}, ${team.name}`; +} \ No newline at end of file From e3ec45518f69f42fc354f28e3cb823acd45db3fd Mon Sep 17 00:00:00 2001 From: ampersanded Date: Sat, 24 Aug 2024 16:35:32 -0700 Subject: [PATCH 6/6] compare teams --- app/[eventId]/[teamNumber]/client-page.tsx | 8 +++++++- app/[eventId]/overview/[teamNumber]/client-page.tsx | 8 +++++++- app/[eventId]/overview/compare/page.tsx | 9 ++++++++- app/[eventId]/overview/page.tsx | 8 +++++++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/app/[eventId]/[teamNumber]/client-page.tsx b/app/[eventId]/[teamNumber]/client-page.tsx index aee240f..35713a6 100644 --- a/app/[eventId]/[teamNumber]/client-page.tsx +++ b/app/[eventId]/[teamNumber]/client-page.tsx @@ -15,7 +15,7 @@ import {zodResolver} from "@hookform/resolvers/zod"; import {useForm} from "react-hook-form"; import {Form, FormField, FormItem, FormMessage} from "@/components/ui/form"; import UpdateTeamData from "@/lib/database/update-team-data"; -import {Loader2, MoreVertical, ScanEye} from "lucide-react"; +import {ArrowLeftRight, Loader2, MoreVertical, ScanEye} from "lucide-react"; import {cn} from "@/lib/utils"; import KeyBindListener from "@/components/key-bind-listener"; import {Tooltip, TooltipContent, TooltipTrigger} from "@/components/ui/tooltip"; @@ -154,6 +154,12 @@ export default function ClientPage({ View Overview + + + + Compare + +
diff --git a/app/[eventId]/overview/[teamNumber]/client-page.tsx b/app/[eventId]/overview/[teamNumber]/client-page.tsx index e6f4ca5..437a3a7 100644 --- a/app/[eventId]/overview/[teamNumber]/client-page.tsx +++ b/app/[eventId]/overview/[teamNumber]/client-page.tsx @@ -11,7 +11,7 @@ import StatusBadge from "@/components/status-badge"; import {TeamStatus} from "@/lib/database/set-team-statues"; import RichTextarea from "@/components/rich-textarea"; import React, {ReactNode} from "react"; -import {ArrowDown, ArrowUp, Edit, Minus, MoreVertical} from "lucide-react"; +import {ArrowDown, ArrowLeftRight, ArrowUp, Edit, Minus, MoreVertical} from "lucide-react"; import {percentile, withOrdinalSuffix} from "@/lib/utils"; import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from "@/components/ui/dropdown-menu"; import {Button} from "@/components/ui/button"; @@ -68,6 +68,12 @@ export default function ClientPage({ Make Changes + + + + Compare + +
diff --git a/app/[eventId]/overview/compare/page.tsx b/app/[eventId]/overview/compare/page.tsx index 60b76df..8fe8221 100644 --- a/app/[eventId]/overview/compare/page.tsx +++ b/app/[eventId]/overview/compare/page.tsx @@ -52,7 +52,14 @@ export default async function Compare({params, searchParams}: { } if (!searchParams.a || !searchParams.b) { - return teamSelector(searchParams.a, searchParams.b); + return ( + <> + {teamSelector(searchParams.a, searchParams.b)} +

+ Select two teams to continue. +

+ + ); } const a = await prisma.team.findUnique( diff --git a/app/[eventId]/overview/page.tsx b/app/[eventId]/overview/page.tsx index 8dda23f..05b9626 100644 --- a/app/[eventId]/overview/page.tsx +++ b/app/[eventId]/overview/page.tsx @@ -7,6 +7,7 @@ import React from "react"; import OverviewCharts from "@/app/[eventId]/overview/overview-charts"; import ScoutingCharts from "@/app/[eventId]/overview/scouting-charts"; import Teams from "@/app/[eventId]/overview/teams"; +import {ArrowLeftRight} from "lucide-react"; export default async function Overview({params}: { params: { eventId: string } }) { if (!+params.eventId) return NotFound(); @@ -59,7 +60,12 @@ export default async function Overview({params}: { params: { eventId: string } }

Scouting Overview

-

Team Overviews

+
+

Team Overviews

+ + + +