From fa7f6cbfa91ded69cde56a835202e59c3f447b22 Mon Sep 17 00:00:00 2001 From: Davis Becker <143132652+BanEvading@users.noreply.github.com> Date: Tue, 11 Feb 2025 16:43:56 -0500 Subject: [PATCH 01/27] Im embarassingly proud of this --- lib/Types.ts | 3 +++ lib/api/ClientApi.ts | 5 +++-- pages/createTeam.tsx | 12 ++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/Types.ts b/lib/Types.ts index 257b111c..813d2a52 100644 --- a/lib/Types.ts +++ b/lib/Types.ts @@ -88,6 +88,7 @@ export class Team { tbaId: string | undefined; number: number; league: League = League.FRC; + alliance: boolean; owners: string[]; users: string[]; @@ -109,6 +110,7 @@ export class Team { tbaId: string | undefined, number: number, league: League = League.FRC, + alliance: boolean, owners: string[] = [], users: string[] = [], scouters: string[] = [], @@ -122,6 +124,7 @@ export class Team { this.tbaId = tbaId; this.number = number; this.league = league; + this.alliance = alliance; this.owners = owners; this.users = users; this.scouters = scouters; diff --git a/lib/api/ClientApi.ts b/lib/api/ClientApi.ts index 243f0472..9710985e 100644 --- a/lib/api/ClientApi.ts +++ b/lib/api/ClientApi.ts @@ -222,7 +222,7 @@ export default class ClientApi extends NextApiTemplate { }); createTeam = createNextRoute< - [string, string, number, League], + [string, string, number, League, boolean], Team | undefined, ApiDependencies, void @@ -233,7 +233,7 @@ export default class ClientApi extends NextApiTemplate { res, { db: dbPromise, resend, userPromise }, authData, - [name, tbaId, number, league], + [name, tbaId, number, league, alliance], ) => { const user = (await userPromise)!; const db = await dbPromise; @@ -256,6 +256,7 @@ export default class ClientApi extends NextApiTemplate { tbaId, number, league, + alliance, [user._id!.toString()], [user._id!.toString()], [user._id!.toString()], diff --git a/pages/createTeam.tsx b/pages/createTeam.tsx index e53304df..46c1e738 100644 --- a/pages/createTeam.tsx +++ b/pages/createTeam.tsx @@ -18,6 +18,8 @@ export default function CreateTeam() { const [team, setTeam] = useState>({}); const [error, setError] = useState(""); + const[allianceStatus, setAllianceStatus] = useState(false); + const createTeam = async () => { if (!session?.user) { return; @@ -52,6 +54,7 @@ export default function CreateTeam() { team.tbaId ?? NotLinkedToTba, team.number, team.league, + team.alliance as boolean, ); if (!newTeam) { @@ -120,6 +123,14 @@ export default function CreateTeam() { className="input w-full" value={team.name ?? ""} onChange={(e) => setTeam({ ...team, name: e.target.value })} + /> + { + setAllianceStatus(!allianceStatus) + setTeam({ ...team, alliance: allianceStatus as boolean} + + )}} /> + {allianceStatus?

allied

:

not allied

} {error &&

{error}

} From a13e1527daa19e0e006a757012774bf864df1912 Mon Sep 17 00:00:00 2001 From: Davis Becker <143132652+BanEvading@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:03:42 -0500 Subject: [PATCH 02/27] Mostly works, issue with assigning alliance status --- components/TeamCard.tsx | 2 +- pages/[teamSlug]/index.tsx | 5 ++++- pages/createTeam.tsx | 32 ++++++++++++++++++++++---------- pages/profile.tsx | 2 +- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/components/TeamCard.tsx b/components/TeamCard.tsx index eb9b5228..626ae3a2 100644 --- a/components/TeamCard.tsx +++ b/components/TeamCard.tsx @@ -10,7 +10,7 @@ export default function TeamCard(props: { team: Team | undefined }) { className="w-full bg-base-300 border-4 border-base-300 transition ease-in hover:border-primary" >

- {team?.league} Team {team?.number}{" "} + {team?.league} {team?.alliance ? "Team" : "Alliance"} {team?.number}{" "} - {team?.users.length} members

diff --git a/pages/[teamSlug]/index.tsx b/pages/[teamSlug]/index.tsx index 91b79295..06f9de13 100644 --- a/pages/[teamSlug]/index.tsx +++ b/pages/[teamSlug]/index.tsx @@ -479,7 +479,7 @@ export default function TeamIndex(props: TeamPageProps) { size={30} className="inline-block mr-2" > - Team {team?.number} + {props.team?.alliance ? "Team":"Alliance"} {team?.number}

@@ -500,6 +500,7 @@ export default function TeamIndex(props: TeamPageProps) {
{isFrc ? "FRC" : "FTC"}
+ {props.team?.alliance ? + : + <>}
diff --git a/pages/createTeam.tsx b/pages/createTeam.tsx index 46c1e738..e0b09cf4 100644 --- a/pages/createTeam.tsx +++ b/pages/createTeam.tsx @@ -30,6 +30,14 @@ export default function CreateTeam() { return; } + if (allianceStatus) { + //If this ever becomes an issue it might work better to convert the number to a string and check the length - Davis. + if (!team.number || team.number < 10000){ + setError("Alliance numbers must be greater than six digits") + return; + } + } + if (!team?.number) { setError("Must enter a team number"); return; @@ -92,7 +100,18 @@ export default function CreateTeam() { mode="col" className="md:h-full items-center md:justify-center max-sm:py-10" > - + +
{Object.values(League).map((league) => ( - {allianceStatus?

allied

:

not allied

} + {team.alliance?

allied

:

not allied

} {error &&

{error}

} From 72467d4dba0863f9b700ea2cd99b46efd04f18f7 Mon Sep 17 00:00:00 2001 From: Davis Becker <143132652+BanEvading@users.noreply.github.com> Date: Fri, 14 Feb 2025 15:55:18 -0500 Subject: [PATCH 05/27] FIXXED IT! --- components/TeamCard.tsx | 2 +- pages/[teamSlug]/index.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/TeamCard.tsx b/components/TeamCard.tsx index 626ae3a2..3f3b4644 100644 --- a/components/TeamCard.tsx +++ b/components/TeamCard.tsx @@ -10,7 +10,7 @@ export default function TeamCard(props: { team: Team | undefined }) { className="w-full bg-base-300 border-4 border-base-300 transition ease-in hover:border-primary" >

- {team?.league} {team?.alliance ? "Team" : "Alliance"} {team?.number}{" "} + {team?.league} {team?.alliance ? "Alliance" : "Team"} {team?.number}{" "} - {team?.users.length} members

diff --git a/pages/[teamSlug]/index.tsx b/pages/[teamSlug]/index.tsx index 06f9de13..7be9e58f 100644 --- a/pages/[teamSlug]/index.tsx +++ b/pages/[teamSlug]/index.tsx @@ -479,7 +479,7 @@ export default function TeamIndex(props: TeamPageProps) { size={30} className="inline-block mr-2" > - {props.team?.alliance ? "Team":"Alliance"} {team?.number} + {props.team?.alliance ? "Alliance":"Team"} {team?.number}

@@ -501,7 +501,8 @@ export default function TeamIndex(props: TeamPageProps) { {isFrc ? "FRC" : "FTC"} {props.team?.alliance ? - + : - : - <>} + }
From a1cc6a7b5ff2903649e5de11cd73ecf2506ef59b Mon Sep 17 00:00:00 2001 From: Davis Becker <143132652+BanEvading@users.noreply.github.com> Date: Fri, 14 Feb 2025 16:12:57 -0500 Subject: [PATCH 06/27] Added dynamic text for alliances vs teams --- pages/[teamSlug]/index.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pages/[teamSlug]/index.tsx b/pages/[teamSlug]/index.tsx index 7be9e58f..2e2fdd87 100644 --- a/pages/[teamSlug]/index.tsx +++ b/pages/[teamSlug]/index.tsx @@ -246,10 +246,10 @@ function Roster(props: TeamPageProps) { return ( -

View and Manage your Team

+

View and Manage your {team?.alliance ? "Alliance" : "Team"}

{users?.length} total members

@@ -411,7 +411,7 @@ function Settings(props: TeamPageProps) { const updateTeam = async () => { setError(""); if (!validName(teamName, true)) { - setError("Invalid Team Name"); + {props.team?.alliance ? setError("Invalid Alliance Name") : setError("Invalid Team Name");} return; } @@ -421,10 +421,10 @@ function Settings(props: TeamPageProps) { return ( -

Edit your teams configuration

+

Edit your {props.team?.alliance ? "alliances" : "teams"} configuration

{error}

-

Set your Team's Name:

+

Set your {props.team?.alliance ? "Alliance" : "Team"}' Name:

@@ -440,7 +440,7 @@ function Settings(props: TeamPageProps) { className="btn btn-primary md:w-1/4" onClick={updateTeam} > - Update Team + {props.team?.alliance ? "Update Alliance" : "Update Team"}
); @@ -460,7 +460,7 @@ export default function TeamIndex(props: TeamPageProps) { Date: Fri, 14 Feb 2025 16:17:39 -0500 Subject: [PATCH 07/27] Fixed Typos --- pages/[teamSlug]/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/[teamSlug]/index.tsx b/pages/[teamSlug]/index.tsx index 2e2fdd87..93bad4bc 100644 --- a/pages/[teamSlug]/index.tsx +++ b/pages/[teamSlug]/index.tsx @@ -421,10 +421,10 @@ function Settings(props: TeamPageProps) { return ( -

Edit your {props.team?.alliance ? "alliances" : "teams"} configuration

+

Edit your {props.team?.alliance ? "Alliance's" : "Team's"} configuration

{error}

-

Set your {props.team?.alliance ? "Alliance" : "Team"}' Name:

+

Set your {props.team?.alliance ? "Alliance" : "Team"}'s Name:

Date: Fri, 14 Feb 2025 16:25:34 -0500 Subject: [PATCH 08/27] Further added dynamic text for alliances --- components/Container.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Container.tsx b/components/Container.tsx index 896cf4c7..b767f8a2 100644 --- a/components/Container.tsx +++ b/components/Container.tsx @@ -362,7 +362,7 @@ export default function Container(props: ContainerProps) { From 9ebf5aaa961504d61d4b3eb1aee313ace670e7f2 Mon Sep 17 00:00:00 2001 From: Gearbox Bot Date: Fri, 14 Feb 2025 21:31:38 +0000 Subject: [PATCH 09/27] 1.1.21 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d8eb49f4..2b45e283 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sj3", - "version": "1.1.20", + "version": "1.1.21", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sj3", - "version": "1.1.20", + "version": "1.1.21", "license": "CC BY-NC-SA 4.0", "dependencies": { "dependencies": "^0.0.1", diff --git a/package.json b/package.json index e4fc769a..16d67805 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sj3", - "version": "1.1.20", + "version": "1.1.21", "private": true, "repository": "https://github.com/Decatur-Robotics/Gearbox", "license": "CC BY-NC-SA 4.0", From d469613572fd36115201c75324a9e7bc561db889 Mon Sep 17 00:00:00 2001 From: Davis Becker <143132652+BanEvading@users.noreply.github.com> Date: Fri, 14 Feb 2025 16:35:01 -0500 Subject: [PATCH 10/27] Removed console import --- pages/createTeam.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/pages/createTeam.tsx b/pages/createTeam.tsx index 88c6614b..064d4699 100644 --- a/pages/createTeam.tsx +++ b/pages/createTeam.tsx @@ -9,7 +9,6 @@ import Card from "@/components/Card"; import Flex from "@/components/Flex"; import { Analytics } from "@/lib/client/Analytics"; import { NotLinkedToTba } from "@/lib/client/ClientUtils"; -import { Console } from "console"; const api = new ClientApi(); From 1e445d0d847ee3e86734893d8212260ba36e59c1 Mon Sep 17 00:00:00 2001 From: Davis Becker <143132652+BanEvading@users.noreply.github.com> Date: Fri, 14 Feb 2025 16:45:50 -0500 Subject: [PATCH 11/27] Fixed Build and Unit Test issues --- pages/onboarding.tsx | 1 + tests/lib/api/ClientApi.test.ts | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/pages/onboarding.tsx b/pages/onboarding.tsx index 0e0ec00f..888d441f 100644 --- a/pages/onboarding.tsx +++ b/pages/onboarding.tsx @@ -175,6 +175,7 @@ export default function Onboarding() { team?.tbaId ?? NotLinkedToTba, teamNumber, league, + false, ); if (!newTeam) { setErrorMsg("Failed to create team"); diff --git a/tests/lib/api/ClientApi.test.ts b/tests/lib/api/ClientApi.test.ts index aa8e5eec..29d64fb8 100644 --- a/tests/lib/api/ClientApi.test.ts +++ b/tests/lib/api/ClientApi.test.ts @@ -34,6 +34,7 @@ describe(`${ClientApi.name}.${api.requestToJoinTeam.name}`, () => { "tbaId", 1234, League.FRC, + false, [user._id!.toString()], [user._id!.toString()], ), @@ -131,7 +132,7 @@ describe(`${ClientApi.name}.${api.handleTeamJoinRequest.name}`, () => { const teamId = new ObjectId(); await db.addObject(CollectionId.Teams, { - ...new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, [ + ...new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ user._id!.toString(), ]), _id: teamId, @@ -163,7 +164,7 @@ describe(`${ClientApi.name}.${api.handleTeamJoinRequest.name}`, () => { const teamId = new ObjectId(); await db.addObject(CollectionId.Teams, { - ...new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, [ + ...new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ user._id!.toString(), ]), _id: teamId, @@ -578,7 +579,7 @@ describe(`${ClientApi.name}.${api.updateTeam.name}`, () => { test(`${ClientApi.name}.${api.updateTeam.name}: Updates team`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, [ + const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ user._id!.toString(), ]); await db.addObject(CollectionId.Teams, team); @@ -623,7 +624,7 @@ describe(`${ClientApi.name}.${api.updateSeason.name}`, () => { test(`${ClientApi.name}.${api.updateSeason.name}: Updates season`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, [ + const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ user._id!.toString(), ]); await db.addObject(CollectionId.Teams, team); @@ -683,7 +684,7 @@ describe(`${ClientApi.name}.${api.updateReport.name}`, () => { test(`${ClientApi.name}.${api.updateReport.name}: Updates report`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, [ + const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ user._id!.toString(), ]); await db.addObject(CollectionId.Teams, team); @@ -754,7 +755,7 @@ describe(`${ClientApi.name}.${api.updatePitreport.name}`, () => { test(`${ClientApi.name}.${api.updatePitreport.name}: Updates pitreport`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, [ + const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ user._id!.toString(), ]); await db.addObject(CollectionId.Teams, team); @@ -830,7 +831,7 @@ describe(`${ClientApi.name}.${api.setSlackWebhook.name}`, () => { test(`${ClientApi.name}.${api.setSlackWebhook.name}: Sets webhook URL when team does not already have one`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, [ + const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ user._id!.toString(), ]); await db.addObject(CollectionId.Teams, team); @@ -866,7 +867,7 @@ describe(`${ClientApi.name}.${api.setSlackWebhook.name}`, () => { test(`${ClientApi.name}.${api.setSlackWebhook.name}: Updates webhook URL when team already has one`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, [ + const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ user._id!.toString(), ]); await db.addObject(CollectionId.Teams, team); From 771b0b505866456c59ab6fee25af1c82c436a578 Mon Sep 17 00:00:00 2001 From: Davis Becker <143132652+BanEvading@users.noreply.github.com> Date: Fri, 14 Feb 2025 16:51:01 -0500 Subject: [PATCH 12/27] Fixed Formatting --- components/Container.tsx | 3 ++- components/TeamCard.tsx | 5 ++-- pages/[teamSlug]/index.tsx | 55 +++++++++++++++++++++++++------------- pages/createTeam.tsx | 32 ++++++++++++---------- pages/profile.tsx | 2 +- 5 files changed, 60 insertions(+), 37 deletions(-) diff --git a/components/Container.tsx b/components/Container.tsx index b767f8a2..a3ee606d 100644 --- a/components/Container.tsx +++ b/components/Container.tsx @@ -362,7 +362,8 @@ export default function Container(props: ContainerProps) { diff --git a/components/TeamCard.tsx b/components/TeamCard.tsx index 3f3b4644..1475843f 100644 --- a/components/TeamCard.tsx +++ b/components/TeamCard.tsx @@ -10,8 +10,9 @@ export default function TeamCard(props: { team: Team | undefined }) { className="w-full bg-base-300 border-4 border-base-300 transition ease-in hover:border-primary" >

- {team?.league} {team?.alliance ? "Alliance" : "Team"} {team?.number}{" "} - - {team?.users.length} members + {team?.league} {team?.alliance ? "Alliance" : "Team"}{" "} + {team?.number} -{" "} + {team?.users.length} members

); diff --git a/pages/[teamSlug]/index.tsx b/pages/[teamSlug]/index.tsx index 93bad4bc..111042fa 100644 --- a/pages/[teamSlug]/index.tsx +++ b/pages/[teamSlug]/index.tsx @@ -249,7 +249,9 @@ function Roster(props: TeamPageProps) { title={team?.alliance ? "Alliance Roster" : "Team Roster"} className="h-full " > -

View and Manage your {team?.alliance ? "Alliance" : "Team"}

+

+ View and Manage your {team?.alliance ? "Alliance" : "Team"} +

{users?.length} total members

@@ -411,7 +413,11 @@ function Settings(props: TeamPageProps) { const updateTeam = async () => { setError(""); if (!validName(teamName, true)) { - {props.team?.alliance ? setError("Invalid Alliance Name") : setError("Invalid Team Name");} + { + props.team?.alliance + ? setError("Invalid Alliance Name") + : setError("Invalid Team Name"); + } return; } @@ -421,7 +427,9 @@ function Settings(props: TeamPageProps) { return ( -

Edit your {props.team?.alliance ? "Alliance's" : "Team's"} configuration

+

+ Edit your {props.team?.alliance ? "Alliance's" : "Team's"} configuration +

{error}

Set your {props.team?.alliance ? "Alliance" : "Team"}'s Name:

@@ -440,7 +448,8 @@ function Settings(props: TeamPageProps) { className="btn btn-primary md:w-1/4" onClick={updateTeam} > - {props.team?.alliance ? "Update Alliance" : "Update Team"} + + {props.team?.alliance ? "Update Alliance" : "Update Team"}
); @@ -460,7 +469,13 @@ export default function TeamIndex(props: TeamPageProps) { - {props.team?.alliance ? "Alliance":"Team"} {team?.number} + {props.team?.alliance ? "Alliance" : "Team"}{" "} + {team?.number}

@@ -500,19 +516,20 @@ export default function TeamIndex(props: TeamPageProps) {
{isFrc ? "FRC" : "FTC"}
- {props.team?.alliance ? - <> - : -
- - TBA -
- - } + {props.team?.alliance ? ( + <> + ) : ( + +
+ + TBA +
+ + )}
diff --git a/pages/createTeam.tsx b/pages/createTeam.tsx index 064d4699..b2912db5 100644 --- a/pages/createTeam.tsx +++ b/pages/createTeam.tsx @@ -30,8 +30,8 @@ export default function CreateTeam() { if (team.alliance) { //If this ever becomes an issue it might work better to convert the number to a string and check the length - Davis. - if (!team.number || team.number < 10000){ - setError("Alliance numbers must be greater than six digits") + if (!team.number || team.number < 10000) { + setError("Alliance numbers must be greater than six digits"); return; } } @@ -100,15 +100,15 @@ export default function CreateTeam() { > + Scouting Alliance + { + setTeam({ ...team, alliance: !team.alliance }); + }} + /> +
{Object.values(League).map((league) => ( - {team.alliance?

allied

:

not allied

} + {team.alliance ?

allied

:

not allied

} {error &&

{error}

} diff --git a/pages/profile.tsx b/pages/profile.tsx index 9c6615b9..9cae10d8 100644 --- a/pages/profile.tsx +++ b/pages/profile.tsx @@ -146,7 +146,7 @@ export default function Profile(props: { teamList: Team[] }) { {teams.map((team) => ( From 12aa65861de5e302f465f5e3bd9942af95cb14a0 Mon Sep 17 00:00:00 2001 From: Davis Becker <143132652+BanEvading@users.noreply.github.com> Date: Fri, 14 Feb 2025 16:52:38 -0500 Subject: [PATCH 13/27] Fixed Formatting using Prettier --- tests/lib/api/ClientApi.test.ts | 72 ++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/tests/lib/api/ClientApi.test.ts b/tests/lib/api/ClientApi.test.ts index 29d64fb8..554eceb3 100644 --- a/tests/lib/api/ClientApi.test.ts +++ b/tests/lib/api/ClientApi.test.ts @@ -579,9 +579,15 @@ describe(`${ClientApi.name}.${api.updateTeam.name}`, () => { test(`${ClientApi.name}.${api.updateTeam.name}: Updates team`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ - user._id!.toString(), - ]); + const team = new Team( + "Test Team", + "test-team", + "tbaId", + 1234, + League.FRC, + false, + [user._id!.toString()], + ); await db.addObject(CollectionId.Teams, team); const newValues = { name: "Updated Team" }; @@ -624,9 +630,15 @@ describe(`${ClientApi.name}.${api.updateSeason.name}`, () => { test(`${ClientApi.name}.${api.updateSeason.name}: Updates season`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ - user._id!.toString(), - ]); + const team = new Team( + "Test Team", + "test-team", + "tbaId", + 1234, + League.FRC, + false, + [user._id!.toString()], + ); await db.addObject(CollectionId.Teams, team); const season: Season = new Season( @@ -684,9 +696,15 @@ describe(`${ClientApi.name}.${api.updateReport.name}`, () => { test(`${ClientApi.name}.${api.updateReport.name}: Updates report`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ - user._id!.toString(), - ]); + const team = new Team( + "Test Team", + "test-team", + "tbaId", + 1234, + League.FRC, + false, + [user._id!.toString()], + ); await db.addObject(CollectionId.Teams, team); const match: Match = new Match( @@ -755,9 +773,15 @@ describe(`${ClientApi.name}.${api.updatePitreport.name}`, () => { test(`${ClientApi.name}.${api.updatePitreport.name}: Updates pitreport`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ - user._id!.toString(), - ]); + const team = new Team( + "Test Team", + "test-team", + "tbaId", + 1234, + League.FRC, + false, + [user._id!.toString()], + ); await db.addObject(CollectionId.Teams, team); const competition = { @@ -831,9 +855,15 @@ describe(`${ClientApi.name}.${api.setSlackWebhook.name}`, () => { test(`${ClientApi.name}.${api.setSlackWebhook.name}: Sets webhook URL when team does not already have one`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ - user._id!.toString(), - ]); + const team = new Team( + "Test Team", + "test-team", + "tbaId", + 1234, + League.FRC, + false, + [user._id!.toString()], + ); await db.addObject(CollectionId.Teams, team); const webhookUrl = "test-webhook-url"; @@ -867,9 +897,15 @@ describe(`${ClientApi.name}.${api.setSlackWebhook.name}`, () => { test(`${ClientApi.name}.${api.setSlackWebhook.name}: Updates webhook URL when team already has one`, async () => { const { db, res, user } = await getTestApiUtils(); - const team = new Team("Test Team", "test-team", "tbaId", 1234, League.FRC, false, [ - user._id!.toString(), - ]); + const team = new Team( + "Test Team", + "test-team", + "tbaId", + 1234, + League.FRC, + false, + [user._id!.toString()], + ); await db.addObject(CollectionId.Teams, team); const webhookUrl = "test-webhook-url"; From 8188b34181300fc42d36f5dfec610fe899e535ac Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Sat, 15 Feb 2025 20:07:15 -0500 Subject: [PATCH 14/27] Better speedtest --- pages/dev/speedtest.tsx | 117 +++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 50 deletions(-) diff --git a/pages/dev/speedtest.tsx b/pages/dev/speedtest.tsx index 37a0a8ef..9e953972 100644 --- a/pages/dev/speedtest.tsx +++ b/pages/dev/speedtest.tsx @@ -1,7 +1,10 @@ import Container from "@/components/Container"; import ClientApi from "@/lib/api/ClientApi"; import { Round } from "@/lib/client/StatsMath"; +import { useCurrentSession } from "@/lib/client/useCurrentSession"; +import useInterval from "@/lib/client/useInterval"; import { useCallback, useEffect, useState } from "react"; +import toast from "react-hot-toast"; const api = new ClientApi(); @@ -23,17 +26,21 @@ const SPEED_TEST_LENGTH = 12000; const SPEED_TEST_PARALLEL_REQUESTS = 120; export default function SpeedTest() { - const [times, setTimes] = useState(); - const [resultsCompleted, setResultsCompleted] = useState( - undefined, - ); + const { session, status } = useCurrentSession(); + const [trialCountByThread, setTrialCountByThread] = useState(); + const [results, setResults] = useState([]); + const [avgResult, setAvgResult] = useState(); const runSpeedTest = useCallback(async () => { - setResultsCompleted(undefined); + if (!session?.user) { + toast.error("You must be logged in to run a speed test"); + return; + } - const newResults: SpeedTestResponse[] = []; + console.log("Running Speed Test"); + const newResults: SpeedTestResponse[] = []; const trialCountByThread: number[] = []; function onTrialComplete( @@ -43,6 +50,8 @@ export default function SpeedTest() { >, thread: number, ) { + delete newTimes.responseTimestamp; + newResults.push({ ...newTimes, dbTime: @@ -55,11 +64,11 @@ export default function SpeedTest() { totalTime: Object.values(newTimes).reduce((acc, time) => acc + time, 0), } as SpeedTestResponse); - setResultsCompleted(newResults.length); - trialCountByThread[thread]++; setTrialCountByThread(trialCountByThread); + setResults(newResults); + if (newResults.length < SPEED_TEST_LENGTH) api.speedTest().then((res) => onTrialComplete(res, thread)); } @@ -69,11 +78,12 @@ export default function SpeedTest() { trialCountByThread[thread] = 0; api.speedTest().then((res) => onTrialComplete(res, thread)); } + }, [session]); - while (newResults.length < SPEED_TEST_LENGTH) - await new Promise((resolve) => setTimeout(resolve, 25)); + const updateResults = useCallback(() => { + if (!results) return; - const avgTimes: typeof times = newResults.reduce( + const avgTimes: typeof avgResult = results.reduce( (acc, times) => { Object.entries(times).forEach(([key, value]) => { acc[key] += value; @@ -95,57 +105,64 @@ export default function SpeedTest() { ); Object.keys(avgTimes).forEach((key) => { - avgTimes[key] /= newResults.length; + avgTimes[key] /= results.length; }); - setTimes(avgTimes); - setResultsCompleted(undefined); - }, []); + setAvgResult(avgTimes); + }, [results]); - useEffect(() => { - runSpeedTest(); - }, [runSpeedTest]); + useInterval(updateResults, 1000); return ( - {resultsCompleted !== undefined && ( -
- {trialCountByThread!.map((count, index) => ( - - ))} -
- )} - {times ? ( +
- {Object.entries(times).map(([key, value]) => ( -
- {key}: {Round(value)}ms ({Round((value / times.totalTime) * 100)} - %) + {avgResult ? ( +
+
+ Results: {results.length}/{SPEED_TEST_LENGTH} Trials Complete +
+ {Object.entries(avgResult).map(([key, value]) => ( +
+ {key}: {Round(value)}ms ( + {Round((value / avgResult.totalTime) * 100)} + %) +
+ ))}
- ))} -
- ) : ( -
- Loading... {resultsCompleted}/{SPEED_TEST_LENGTH} Trials Complete + ) : ( +
Loading...
+ )} + +
Auth Status: {status}
- )} - + {results && trialCountByThread && ( +
+ {trialCountByThread!.map((count, index) => ( + + ))} +
+ )} +
); } From 15a7d9fc250cff4a9e6db92610364143e2fda518 Mon Sep 17 00:00:00 2001 From: Renato Dell'Osso Date: Sun, 16 Feb 2025 17:18:33 -0500 Subject: [PATCH 15/27] Remove "not allied" and "allied" from createTeam page --- pages/createTeam.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/pages/createTeam.tsx b/pages/createTeam.tsx index b2912db5..0859d0ac 100644 --- a/pages/createTeam.tsx +++ b/pages/createTeam.tsx @@ -151,7 +151,6 @@ export default function CreateTeam() { > Create Team - {team.alliance ?

allied

:

not allied

} {error &&

{error}

} From 820262a4f92fce570b081594f6a53409e99fdf0b Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Sun, 16 Feb 2025 17:19:20 -0500 Subject: [PATCH 16/27] Use CachedDb --- lib/MongoDB.ts | 8 ++- lib/ResendUtils.ts | 18 ++++--- lib/TheBlueAlliance.ts | 3 +- lib/client/dbinterfaces/CachedDbInterface.ts | 54 ++++++++++++++++++++ package-lock.json | 34 +++++++++--- package.json | 2 +- 6 files changed, 101 insertions(+), 18 deletions(-) create mode 100644 lib/client/dbinterfaces/CachedDbInterface.ts diff --git a/lib/MongoDB.ts b/lib/MongoDB.ts index eada8b7f..eab6f292 100644 --- a/lib/MongoDB.ts +++ b/lib/MongoDB.ts @@ -5,6 +5,7 @@ import DbInterface, { WithStringOrObjectIdId, } from "./client/dbinterfaces/DbInterface"; import { default as BaseMongoDbInterface } from "mongo-anywhere/MongoDbInterface"; +import CachedDbInterface from "./client/dbinterfaces/CachedDbInterface"; if (!process.env.MONGODB_URI) { // Necessary to allow connections from files running outside of Next @@ -28,10 +29,13 @@ clientPromise = global.clientPromise; export { clientPromise }; -export async function getDatabase(): Promise { +export async function getDatabase(): Promise { if (!global.interface) { await clientPromise; - const dbInterface = new MongoDBInterface(clientPromise); + const dbInterface = new CachedDbInterface( + new MongoDBInterface(clientPromise), + { stdTTL: 120, useClones: false }, + ); await dbInterface.init(); global.interface = dbInterface; diff --git a/lib/ResendUtils.ts b/lib/ResendUtils.ts index bac16f7b..106b1f79 100644 --- a/lib/ResendUtils.ts +++ b/lib/ResendUtils.ts @@ -3,6 +3,7 @@ import { Resend } from "resend"; import { getDatabase } from "./MongoDB"; import { User } from "./Types"; import CollectionId from "./client/CollectionId"; +import { ObjectId } from "bson"; const resend = new Resend(process.env.SMTP_PASSWORD); @@ -41,13 +42,16 @@ export class ResendUtils implements ResendInterface { } const db = await getDatabase(); - // Going around our own interface is a red flag, but it's 11 PM and I'm tired -Renato - db.db - ?.collection(CollectionId.Users) - .updateOne( - { email: user.email }, - { $set: { resendContactId: res.data.id } }, - ); + const id = (await db.findObject(CollectionId.Users, { email: user.email })) + ?._id; + if (!id) { + console.error("User not found in database", user.email); + return; + } + + db.updateObjectById(CollectionId.Users, new ObjectId(id), { + resendContactId: res.data.id, + }); } async emailDevelopers(subject: string, message: string) { diff --git a/lib/TheBlueAlliance.ts b/lib/TheBlueAlliance.ts index aadb7527..deb03646 100644 --- a/lib/TheBlueAlliance.ts +++ b/lib/TheBlueAlliance.ts @@ -12,6 +12,7 @@ import { import { NotLinkedToTba } from "./client/ClientUtils"; import { GameId, defaultGameId } from "./client/GameId"; import { games } from "./games"; +import DbInterface from "./client/dbinterfaces/DbInterface"; export namespace TheBlueAlliance { export interface SimpleTeam { @@ -209,7 +210,7 @@ export namespace TheBlueAlliance { export class Interface { req: Request; - db: Promise; + db: Promise; competitionPairings: CompetitonNameIdPair[] = []; diff --git a/lib/client/dbinterfaces/CachedDbInterface.ts b/lib/client/dbinterfaces/CachedDbInterface.ts new file mode 100644 index 00000000..027bfe54 --- /dev/null +++ b/lib/client/dbinterfaces/CachedDbInterface.ts @@ -0,0 +1,54 @@ +import { ObjectId } from "bson"; +import CollectionId, { CollectionIdToType } from "@/lib/client/CollectionId"; +import DbInterface, { + WithStringOrObjectIdId, +} from "@/lib/client/dbinterfaces/DbInterface"; +import { default as BaseCachedDbInterface } from "mongo-anywhere/CachedDbInterface"; + +export default class CachedDbInterface + extends BaseCachedDbInterface> + implements DbInterface +{ + init(): Promise { + return super.init(Object.values(CollectionId)); + } + addObject>( + collection: TId, + object: WithStringOrObjectIdId, + ): Promise { + return super.addObject(collection, object); + } + deleteObjectById(collection: CollectionId, id: ObjectId): Promise { + return super.deleteObjectById(collection, id); + } + updateObjectById< + TId extends CollectionId, + TObj extends CollectionIdToType, + >(collection: TId, id: ObjectId, newValues: Partial): Promise { + return super.updateObjectById(collection, id, newValues); + } + findObjectById< + TId extends CollectionId, + TObj extends CollectionIdToType, + >(collection: TId, id: ObjectId): Promise { + return super.findObjectById(collection, id); + } + findObject>( + collection: TId, + query: object, + ): Promise { + return super.findObject(collection, query); + } + findObjects>( + collection: TId, + query: object, + ): Promise { + return super.findObjects(collection, query); + } + countObjects( + collection: CollectionId, + query: object, + ): Promise { + return super.countObjects(collection, query); + } +} diff --git a/package-lock.json b/package-lock.json index 2b45e283..d4c9b8d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sj3", - "version": "1.1.21", + "version": "1.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sj3", - "version": "1.1.21", + "version": "1.2.1", "license": "CC BY-NC-SA 4.0", "dependencies": { "dependencies": "^0.0.1", @@ -26,7 +26,7 @@ "jose": "^5.9.6", "levenary": "^1.1.1", "minimongo": "^6.19.0", - "mongo-anywhere": "^1.0.21", + "mongo-anywhere": "^1.1.1", "mongodb": "^5.0.0", "next": "^15.1.6", "next-auth": "^4.24.11", @@ -3588,6 +3588,14 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -7870,13 +7878,14 @@ } }, "node_modules/mongo-anywhere": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/mongo-anywhere/-/mongo-anywhere-1.0.23.tgz", - "integrity": "sha512-n6N/fLRb2mylontUuxU4C57SFjULCqCXUUyeNbITRU2jZ8SH9brpZn3woEDwQ0DYZ/GXLHYcDqn2esZkAZTnKQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/mongo-anywhere/-/mongo-anywhere-1.1.1.tgz", + "integrity": "sha512-5sXvJO1iSVB6nNzWdjKWvJxw6Kkd5WO6UqFXspXv51xKGlTmalYrrEv52tvmSOoT4zg+Z2wItnvxhvF+9wDllw==", "dependencies": { "bson": "^5.0.0", "minimongo": "^6.19.0", - "mongodb": "^5.0.0" + "mongodb": "^5.0.0", + "node-cache": "^5.1.2" } }, "node_modules/mongodb": { @@ -8097,6 +8106,17 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "dependencies": { + "clone": "2.x" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", diff --git a/package.json b/package.json index 82f9ae39..85b5a9e0 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "jose": "^5.9.6", "levenary": "^1.1.1", "minimongo": "^6.19.0", - "mongo-anywhere": "^1.0.21", + "mongo-anywhere": "^1.1.1", "mongodb": "^5.0.0", "next": "^15.1.6", "next-auth": "^4.24.11", From 29fd2b0620c73a7432442cf7cc10f350b32512c6 Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Sun, 16 Feb 2025 17:35:23 -0500 Subject: [PATCH 17/27] Add cache stats page and fix pit reports --- lib/api/ClientApi.ts | 14 ++++++++ .../[competitonSlug]/pit/[...pitreportId].tsx | 2 +- pages/dev/cache.tsx | 34 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 pages/dev/cache.tsx diff --git a/lib/api/ClientApi.ts b/lib/api/ClientApi.ts index 55a1084e..532f68fb 100644 --- a/lib/api/ClientApi.ts +++ b/lib/api/ClientApi.ts @@ -2303,4 +2303,18 @@ export default class ClientApi extends NextApiTemplate { res.status(200).send(responseObj); }, }); + + getCacheStats = createNextRoute< + [], + object | undefined, + ApiDependencies, + void + >({ + isAuthorized: AccessLevels.IfDeveloper, + handler: async (req, res, {}, authData, args) => { + if (!global.cache) return res.status(200).send(undefined); + const stats = global.cache.getStats(); + return res.status(200).send(stats); + }, + }); } diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pit/[...pitreportId].tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pit/[...pitreportId].tsx index fa932608..2020e256 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pit/[...pitreportId].tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pit/[...pitreportId].tsx @@ -330,7 +330,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { return { props: { - pitReport: SerializeDatabaseObject(pitreport), + pitReport: makeObjSerializeable(SerializeDatabaseObject(pitreport)), layout: makeObjSerializeable(game.pitReportLayout), teamNumber: resolved.team?.number, compName: resolved.competition?.name, diff --git a/pages/dev/cache.tsx b/pages/dev/cache.tsx new file mode 100644 index 00000000..c1a03877 --- /dev/null +++ b/pages/dev/cache.tsx @@ -0,0 +1,34 @@ +import Container from "@/components/Container"; +import ClientApi from "@/lib/api/ClientApi"; +import useInterval from "@/lib/client/useInterval"; +import { useCallback, useState } from "react"; + +const api = new ClientApi(); + +export default function Cache() { + const [cacheStats, setCacheStats] = useState(); + + const fetchCache = useCallback(async () => { + const stats = await api.getCacheStats(); + setCacheStats(stats); + }, []); + useInterval(fetchCache, 1000); + + return ( + +

Cache

+ {cacheStats ? ( + Object.entries(cacheStats).map(([key, value]) => ( +
+ {key}: {value} +
+ )) + ) : ( +
No cache.
+ )} +
+ ); +} From 991a333056d0e40a88264115dbb8581860fa812b Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Sun, 16 Feb 2025 17:38:26 -0500 Subject: [PATCH 18/27] Format numbers in cache stats --- pages/dev/cache.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/dev/cache.tsx b/pages/dev/cache.tsx index c1a03877..fc5bee8a 100644 --- a/pages/dev/cache.tsx +++ b/pages/dev/cache.tsx @@ -23,7 +23,7 @@ export default function Cache() { {cacheStats ? ( Object.entries(cacheStats).map(([key, value]) => (
- {key}: {value} + {key}: {(+value).toLocaleString()}
)) ) : ( From 3d3bf3140110f6e4d2171d65be5cd8c788d642fd Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Sun, 16 Feb 2025 22:39:31 -0500 Subject: [PATCH 19/27] Fix team loading in stats page --- lib/MongoDB.ts | 1 + lib/api/ClientApi.ts | 14 ++++ package-lock.json | 8 +-- package.json | 2 +- .../[seasonSlug]/[competitonSlug]/stats.tsx | 11 ++- pages/dev/cache.tsx | 72 ++++++++++++++++++- 6 files changed, 98 insertions(+), 10 deletions(-) diff --git a/lib/MongoDB.ts b/lib/MongoDB.ts index eab6f292..1d1cbd1c 100644 --- a/lib/MongoDB.ts +++ b/lib/MongoDB.ts @@ -35,6 +35,7 @@ export async function getDatabase(): Promise { const dbInterface = new CachedDbInterface( new MongoDBInterface(clientPromise), { stdTTL: 120, useClones: false }, + true, ); await dbInterface.init(); global.interface = dbInterface; diff --git a/lib/api/ClientApi.ts b/lib/api/ClientApi.ts index 532f68fb..03c2ea5e 100644 --- a/lib/api/ClientApi.ts +++ b/lib/api/ClientApi.ts @@ -2317,4 +2317,18 @@ export default class ClientApi extends NextApiTemplate { return res.status(200).send(stats); }, }); + + getCachedValue = createNextRoute< + [string], + object | undefined, + ApiDependencies, + void + >({ + isAuthorized: AccessLevels.IfDeveloper, + handler: async (req, res, {}, authData, [key]) => { + if (!global.cache) return res.status(500).send({ error: "No cache" }); + const val = global.cache.get(key) as object | undefined; + return res.status(200).send(val); + }, + }); } diff --git a/package-lock.json b/package-lock.json index d4c9b8d8..c504491f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "jose": "^5.9.6", "levenary": "^1.1.1", "minimongo": "^6.19.0", - "mongo-anywhere": "^1.1.1", + "mongo-anywhere": "^1.1.2", "mongodb": "^5.0.0", "next": "^15.1.6", "next-auth": "^4.24.11", @@ -7878,9 +7878,9 @@ } }, "node_modules/mongo-anywhere": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/mongo-anywhere/-/mongo-anywhere-1.1.1.tgz", - "integrity": "sha512-5sXvJO1iSVB6nNzWdjKWvJxw6Kkd5WO6UqFXspXv51xKGlTmalYrrEv52tvmSOoT4zg+Z2wItnvxhvF+9wDllw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/mongo-anywhere/-/mongo-anywhere-1.1.2.tgz", + "integrity": "sha512-poV4+UHC6Zeq6KRqctL+c5p0ioQqjGm0t8nXVXVaHLzerCZQqkDt2LiVbb1nQmKfHrnSXKhxAig9zZaAvQ2vdA==", "dependencies": { "bson": "^5.0.0", "minimongo": "^6.19.0", diff --git a/package.json b/package.json index 85b5a9e0..1a445b78 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "jose": "^5.9.6", "levenary": "^1.1.1", "minimongo": "^6.19.0", - "mongo-anywhere": "^1.1.1", + "mongo-anywhere": "^1.1.2", "mongodb": "^5.0.0", "next": "^15.1.6", "next-auth": "^4.24.11", diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx index 047de81c..8562de71 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx @@ -193,9 +193,13 @@ export const getServerSideProps: GetServerSideProps = async (context) => { submitted: true, }); - const pitReports = await db.findObjects(CollectionId.PitReports, { - _id: { $in: resolved.competition?.pitReports }, - }); + const pitReports = !resolved.competition + ? [] + : await db.findObjects(CollectionId.PitReports, { + _id: { + $in: resolved.competition.pitReports.map((id) => new ObjectId(id)), + }, + }); const subjectiveReports = await db.findObjects( CollectionId.SubjectiveReports, @@ -208,6 +212,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { CollectionId.Picklists, new ObjectId(resolved.competition?.picklist), ); + console.log("Picklists", picklists); return { props: { diff --git a/pages/dev/cache.tsx b/pages/dev/cache.tsx index fc5bee8a..a2700c50 100644 --- a/pages/dev/cache.tsx +++ b/pages/dev/cache.tsx @@ -1,18 +1,37 @@ import Container from "@/components/Container"; import ClientApi from "@/lib/api/ClientApi"; +import CollectionId from "@/lib/client/CollectionId"; import useInterval from "@/lib/client/useInterval"; +import { getCacheKey } from "mongo-anywhere/CachedDbInterface"; import { useCallback, useState } from "react"; const api = new ClientApi(); export default function Cache() { const [cacheStats, setCacheStats] = useState(); + const [cachedVals, setCachedVals] = useState< + { key: string; val: object | undefined }[] + >([]); - const fetchCache = useCallback(async () => { + const fetchCacheStats = useCallback(async () => { const stats = await api.getCacheStats(); setCacheStats(stats); }, []); - useInterval(fetchCache, 1000); + useInterval(fetchCacheStats, 1000); + + async function fetchCachedVal() { + const method = (document.getElementById("method") as HTMLSelectElement) + .value as "findOne" | "findMultiple" | "count"; + const collection = ( + document.getElementById("collection") as HTMLSelectElement + ).value as CollectionId; + const query = (document.getElementById("query") as HTMLInputElement).value; + + const key = getCacheKey(method, collection, query); + + const val = await api.getCachedValue(key); + setCachedVals((old) => [{ key, val }, ...old]); // Keep the most recent at the top + } return ( No cache. )} +
+
+ + + + +
+ {cachedVals.map(({ key, val }) => ( +
+

{key}

+

{val ? JSON.stringify(val) : val}

+
+ ))} +
); } From 8b29cebee83b92dd962aaafa913a0aa1486d9504 Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Sun, 16 Feb 2025 22:45:42 -0500 Subject: [PATCH 20/27] Fix picklists overwriting before they load --- components/stats/Picklist.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/stats/Picklist.tsx b/components/stats/Picklist.tsx index 939a366f..d12d3b2e 100644 --- a/components/stats/Picklist.tsx +++ b/components/stats/Picklist.tsx @@ -20,6 +20,8 @@ import { const SHOW_PICKLISTS_ON_TEAM_CARDS = false; const SHOW_CARD_IDS = false; +const api = new ClientApi(); + function TeamCard(props: { entry: PicklistEntry; draggable: boolean; @@ -297,8 +299,6 @@ export function TeamList(props: { ); } -const api = new ClientApi(); - export default function PicklistScreen(props: { teams: number[]; reports: Report[]; @@ -321,10 +321,10 @@ export default function PicklistScreen(props: { const teams = props.teams.map((team) => ({ number: team })); // Save picklists - useEffect( - () => savePicklistGroup(props.picklist._id, picklists, strikethroughs, api), - [props.picklist._id, picklists, strikethroughs], - ); + useEffect(() => { + if (loadingPicklists !== LoadState.Loaded) return; + savePicklistGroup(props.picklist._id, picklists, strikethroughs, api); + }, [props.picklist._id, picklists, strikethroughs]); const updatePicklist = useCallback( (picklist: Picklist) => { From cf7020b39a7f05f1f5af4a8d64144d09740200d8 Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Sun, 16 Feb 2025 22:55:58 -0500 Subject: [PATCH 21/27] Fix pit report comment removal in scouter management --- .../[seasonSlug]/[competitonSlug]/scouters.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx index 78ab9dc0..938e0726 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx @@ -24,7 +24,7 @@ export default function Scouters(props: { const team = props.team; const comp = props.competition; - const { session, status } = useCurrentSession(); + const { session } = useCurrentSession(); const isManager = session?.user?._id ? team?.owners.includes(session.user?._id) : false; @@ -141,9 +141,9 @@ export default function Scouters(props: { setReports((reports) => { if (!reports) return reports; - const { _id, ...updated } = reports[comment.dbId]; + const { _id, ...old } = reports[comment.dbId]; promise = api.updateReport( - { data: { ...updated.data, comments: "" } }, + { data: { ...old.data, comments: "" } }, comment.dbId, ); @@ -154,7 +154,12 @@ export default function Scouters(props: { } function removePitComment(comment: Comment) { - return api.updatePitreport(comment.dbId, { comments: "" }); + const { _id, ...old } = data.pitReports.find( + (r) => r._id === comment.dbId, + )!; + return api.updatePitreport(comment.dbId, { + data: { ...old.data, comments: "" }, + }); } function removeSubjectiveComment(comment: Comment) { From 880a12715bf36631939183c72423da3cf0f9a206 Mon Sep 17 00:00:00 2001 From: Gearbox Bot Date: Mon, 17 Feb 2025 03:57:28 +0000 Subject: [PATCH 22/27] 1.2.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c504491f..962384b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sj3", - "version": "1.2.1", + "version": "1.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sj3", - "version": "1.2.1", + "version": "1.2.2", "license": "CC BY-NC-SA 4.0", "dependencies": { "dependencies": "^0.0.1", diff --git a/package.json b/package.json index 1a445b78..fc52e30d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sj3", - "version": "1.2.1", + "version": "1.2.2", "private": true, "repository": "https://github.com/Decatur-Robotics/Gearbox", "license": "CC BY-NC-SA 4.0", From cb141177a32b9414776d6db6d6ae0ab7ab6615ce Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Sun, 16 Feb 2025 23:00:13 -0500 Subject: [PATCH 23/27] Complete save picklists deps array --- components/stats/Picklist.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/stats/Picklist.tsx b/components/stats/Picklist.tsx index d12d3b2e..502114c8 100644 --- a/components/stats/Picklist.tsx +++ b/components/stats/Picklist.tsx @@ -324,7 +324,13 @@ export default function PicklistScreen(props: { useEffect(() => { if (loadingPicklists !== LoadState.Loaded) return; savePicklistGroup(props.picklist._id, picklists, strikethroughs, api); - }, [props.picklist._id, picklists, strikethroughs]); + }, [ + props.picklist._id, + picklists, + strikethroughs, + LoadState.Loaded, + loadingPicklists, + ]); const updatePicklist = useCallback( (picklist: Picklist) => { From 78c4261cb0fa1b5f47dba83c447417c450cbdcb8 Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Sun, 16 Feb 2025 23:03:17 -0500 Subject: [PATCH 24/27] Disable verbose logging for cache --- lib/MongoDB.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/MongoDB.ts b/lib/MongoDB.ts index 1d1cbd1c..eab6f292 100644 --- a/lib/MongoDB.ts +++ b/lib/MongoDB.ts @@ -35,7 +35,6 @@ export async function getDatabase(): Promise { const dbInterface = new CachedDbInterface( new MongoDBInterface(clientPromise), { stdTTL: 120, useClones: false }, - true, ); await dbInterface.init(); global.interface = dbInterface; From 13ca31a035ba421685500718f4519f85a228eb56 Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Mon, 17 Feb 2025 16:03:11 -0500 Subject: [PATCH 25/27] Increased cache TTL --- lib/MongoDB.ts | 3 ++- lib/client/dbinterfaces/CachedDbInterface.ts | 12 ++++++++++++ package-lock.json | 8 ++++---- package.json | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/MongoDB.ts b/lib/MongoDB.ts index eab6f292..f7343f11 100644 --- a/lib/MongoDB.ts +++ b/lib/MongoDB.ts @@ -6,6 +6,7 @@ import DbInterface, { } from "./client/dbinterfaces/DbInterface"; import { default as BaseMongoDbInterface } from "mongo-anywhere/MongoDbInterface"; import CachedDbInterface from "./client/dbinterfaces/CachedDbInterface"; +import { cacheOptions } from "./client/dbinterfaces/CachedDbInterface"; if (!process.env.MONGODB_URI) { // Necessary to allow connections from files running outside of Next @@ -34,7 +35,7 @@ export async function getDatabase(): Promise { await clientPromise; const dbInterface = new CachedDbInterface( new MongoDBInterface(clientPromise), - { stdTTL: 120, useClones: false }, + cacheOptions, ); await dbInterface.init(); global.interface = dbInterface; diff --git a/lib/client/dbinterfaces/CachedDbInterface.ts b/lib/client/dbinterfaces/CachedDbInterface.ts index 027bfe54..8cd487ac 100644 --- a/lib/client/dbinterfaces/CachedDbInterface.ts +++ b/lib/client/dbinterfaces/CachedDbInterface.ts @@ -4,6 +4,13 @@ import DbInterface, { WithStringOrObjectIdId, } from "@/lib/client/dbinterfaces/DbInterface"; import { default as BaseCachedDbInterface } from "mongo-anywhere/CachedDbInterface"; +import NodeCache from "node-cache"; +import { CacheOperation } from "mongo-anywhere/CachedDbInterface"; + +export const cacheOptions: NodeCache.Options = { + stdTTL: 10 * 60, + useClones: false, +}; export default class CachedDbInterface extends BaseCachedDbInterface> @@ -12,6 +19,11 @@ export default class CachedDbInterface init(): Promise { return super.init(Object.values(CollectionId)); } + getTtl(operation: CacheOperation, collectionId: CollectionId): number { + if (operation === "count") return 3 * 60 * 60; + + return cacheOptions.stdTTL!; + } addObject>( collection: TId, object: WithStringOrObjectIdId, diff --git a/package-lock.json b/package-lock.json index 962384b9..1eeff82a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "jose": "^5.9.6", "levenary": "^1.1.1", "minimongo": "^6.19.0", - "mongo-anywhere": "^1.1.2", + "mongo-anywhere": "^1.1.5", "mongodb": "^5.0.0", "next": "^15.1.6", "next-auth": "^4.24.11", @@ -7878,9 +7878,9 @@ } }, "node_modules/mongo-anywhere": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/mongo-anywhere/-/mongo-anywhere-1.1.2.tgz", - "integrity": "sha512-poV4+UHC6Zeq6KRqctL+c5p0ioQqjGm0t8nXVXVaHLzerCZQqkDt2LiVbb1nQmKfHrnSXKhxAig9zZaAvQ2vdA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/mongo-anywhere/-/mongo-anywhere-1.1.5.tgz", + "integrity": "sha512-9qWUSv7kS2oD/MPLLV5Y4AjhTFE7VTkhdDWXsG9eeddOgmRHzRrKX5IV+C/ryDcRoJwcMMIIehnD96RsOse25g==", "dependencies": { "bson": "^5.0.0", "minimongo": "^6.19.0", diff --git a/package.json b/package.json index fc52e30d..cb47b22d 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "jose": "^5.9.6", "levenary": "^1.1.1", "minimongo": "^6.19.0", - "mongo-anywhere": "^1.1.2", + "mongo-anywhere": "^1.1.5", "mongodb": "^5.0.0", "next": "^15.1.6", "next-auth": "^4.24.11", From 1fc4345876991f9b81d1f0ff54f5c25cea4d2055 Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Mon, 17 Feb 2025 21:33:44 -0500 Subject: [PATCH 26/27] Add dev index, fix camel case for serializeDatabaseObject[s] --- lib/UrlResolver.ts | 14 +-- .../[competitonSlug]/pit/[...pitreportId].tsx | 4 +- .../[competitonSlug]/pitstats.tsx | 4 +- .../[competitonSlug]/scouters.tsx | 6 +- .../[seasonSlug]/[competitonSlug]/stats.tsx | 12 +-- pages/[teamSlug]/[seasonSlug]/index.tsx | 4 +- pages/[teamSlug]/createSeason.tsx | 4 +- pages/[teamSlug]/index.tsx | 10 +- pages/dev/index.tsx | 95 +++++++++++++++++++ pages/profile.tsx | 4 +- 10 files changed, 126 insertions(+), 31 deletions(-) create mode 100644 pages/dev/index.tsx diff --git a/lib/UrlResolver.ts b/lib/UrlResolver.ts index 6670398b..d9279736 100644 --- a/lib/UrlResolver.ts +++ b/lib/UrlResolver.ts @@ -29,7 +29,7 @@ export interface ResolvedUrlData { * @param object - Any `Object` with a `_id` property * @returns - The same object, but with `_id` set as a string */ -export function SerializeDatabaseObject(object: any): any { +export function serializeDatabaseObject(object: any): any { if (!object) { return null; } @@ -46,8 +46,8 @@ export function SerializeDatabaseObject(object: any): any { return object; } -export function SerializeDatabaseObjects(objectArray: any[]): any[] { - return objectArray.map((obj) => SerializeDatabaseObject(obj)); +export function serializeDatabaseObjects(objectArray: any[]): any[] { + return objectArray.map((obj) => serializeDatabaseObject(obj)); } /** @@ -115,10 +115,10 @@ export default async function UrlResolver( // find these slugs, and convert them to a JSON safe condition // if they dont exist, simply return nothing const data: ResolvedUrlData = { - team: SerializeDatabaseObject(await promises[0]), - season: SerializeDatabaseObject(await promises[1]), - competition: SerializeDatabaseObject(await promises[2]), - report: SerializeDatabaseObject(await promises[3]), + team: serializeDatabaseObject(await promises[0]), + season: serializeDatabaseObject(await promises[1]), + competition: serializeDatabaseObject(await promises[2]), + report: serializeDatabaseObject(await promises[3]), }; return data; } catch (error) { diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pit/[...pitreportId].tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pit/[...pitreportId].tsx index 2020e256..cefe76a4 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pit/[...pitreportId].tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pit/[...pitreportId].tsx @@ -19,7 +19,7 @@ import { GameId } from "@/lib/client/GameId"; import CollectionId from "@/lib/client/CollectionId"; import { games } from "@/lib/games"; import { getDatabase } from "@/lib/MongoDB"; -import UrlResolver, { SerializeDatabaseObject } from "@/lib/UrlResolver"; +import UrlResolver, { serializeDatabaseObject } from "@/lib/UrlResolver"; import { ObjectId } from "bson"; import { GetServerSideProps } from "next"; import Flex from "@/components/Flex"; @@ -330,7 +330,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { return { props: { - pitReport: makeObjSerializeable(SerializeDatabaseObject(pitreport)), + pitReport: makeObjSerializeable(serializeDatabaseObject(pitreport)), layout: makeObjSerializeable(game.pitReportLayout), teamNumber: resolved.team?.number, compName: resolved.competition?.name, diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pitstats.tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pitstats.tsx index 14f36293..e3b30af0 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pitstats.tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pitstats.tsx @@ -7,7 +7,7 @@ import { QuantData, Report, } from "@/lib/Types"; -import { SerializeDatabaseObject } from "@/lib/UrlResolver"; +import { serializeDatabaseObject } from "@/lib/UrlResolver"; import { GetServerSideProps } from "next"; import { BsGearFill } from "react-icons/bs"; @@ -493,6 +493,6 @@ export const getServerSideProps: GetServerSideProps = async (context) => { }); return { - props: { competition: SerializeDatabaseObject(comp) }, + props: { competition: serializeDatabaseObject(comp) }, }; }; diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx index 938e0726..58ad1c3c 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx @@ -8,7 +8,7 @@ import { Report, SubjectiveReport, } from "@/lib/Types"; -import { SerializeDatabaseObject } from "@/lib/UrlResolver"; +import { serializeDatabaseObject } from "@/lib/UrlResolver"; import ClientApi from "@/lib/api/ClientApi"; import CollectionId from "@/lib/client/CollectionId"; import { useCurrentSession } from "@/lib/client/useCurrentSession"; @@ -508,8 +508,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => { return { props: { - team: SerializeDatabaseObject(team), - competition: SerializeDatabaseObject(comp), + team: serializeDatabaseObject(team), + competition: serializeDatabaseObject(comp), }, }; }; diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx index 8562de71..1a588ba2 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx @@ -14,8 +14,8 @@ import { games } from "@/lib/games"; import CollectionId from "@/lib/client/CollectionId"; import { getDatabase } from "@/lib/MongoDB"; import UrlResolver, { - SerializeDatabaseObjects, - SerializeDatabaseObject, + serializeDatabaseObjects, + serializeDatabaseObject, } from "@/lib/UrlResolver"; import { ObjectId } from "bson"; import { GetServerSideProps } from "next"; @@ -216,10 +216,10 @@ export const getServerSideProps: GetServerSideProps = async (context) => { return { props: { - reports: SerializeDatabaseObjects(reports), - pitReports: SerializeDatabaseObjects(pitReports), - subjectiveReports: SerializeDatabaseObjects(subjectiveReports), - picklists: SerializeDatabaseObject(picklists), + reports: serializeDatabaseObjects(reports), + pitReports: serializeDatabaseObjects(pitReports), + subjectiveReports: serializeDatabaseObjects(subjectiveReports), + picklists: serializeDatabaseObject(picklists), competition: resolved.competition, }, }; diff --git a/pages/[teamSlug]/[seasonSlug]/index.tsx b/pages/[teamSlug]/[seasonSlug]/index.tsx index 0dc9b2ea..42a9a29b 100644 --- a/pages/[teamSlug]/[seasonSlug]/index.tsx +++ b/pages/[teamSlug]/[seasonSlug]/index.tsx @@ -1,5 +1,5 @@ import ClientApi from "@/lib/api/ClientApi"; -import UrlResolver, { SerializeDatabaseObjects } from "@/lib/UrlResolver"; +import UrlResolver, { serializeDatabaseObjects } from "@/lib/UrlResolver"; import { GetServerSideProps } from "next"; import { Competition, Season, Team } from "@/lib/Types"; import Container from "@/components/Container"; @@ -106,7 +106,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { props: { team: team, season: season, - competitions: SerializeDatabaseObjects(comp), + competitions: serializeDatabaseObjects(comp), }, }; }; diff --git a/pages/[teamSlug]/createSeason.tsx b/pages/[teamSlug]/createSeason.tsx index 37caf01a..7b9e303d 100644 --- a/pages/[teamSlug]/createSeason.tsx +++ b/pages/[teamSlug]/createSeason.tsx @@ -1,5 +1,5 @@ import { League, Season, Team } from "../../lib/Types"; -import UrlResolver, { SerializeDatabaseObjects } from "@/lib/UrlResolver"; +import UrlResolver, { serializeDatabaseObjects } from "@/lib/UrlResolver"; import { GetServerSideProps } from "next"; import Container from "@/components/Container"; import { getDatabase } from "@/lib/MongoDB"; @@ -112,7 +112,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { return { props: { team: resolved.team, - existingSeasons: SerializeDatabaseObjects(existingSeasons), + existingSeasons: serializeDatabaseObjects(existingSeasons), }, }; }; diff --git a/pages/[teamSlug]/index.tsx b/pages/[teamSlug]/index.tsx index 111042fa..cb1babc9 100644 --- a/pages/[teamSlug]/index.tsx +++ b/pages/[teamSlug]/index.tsx @@ -1,6 +1,6 @@ import UrlResolver, { - SerializeDatabaseObject, - SerializeDatabaseObjects, + serializeDatabaseObject, + serializeDatabaseObjects, } from "@/lib/UrlResolver"; import { GetServerSideProps } from "next"; import { useEffect, useState } from "react"; @@ -646,9 +646,9 @@ export const getServerSideProps: GetServerSideProps = async (context) => { props: { team: resolved.team, users: makeObjSerializeable(users), - currentCompetition: SerializeDatabaseObject(comp), - currentSeason: SerializeDatabaseObject(currentSeason), - pastSeasons: SerializeDatabaseObjects(seasons), + currentCompetition: serializeDatabaseObject(comp), + currentSeason: serializeDatabaseObject(currentSeason), + pastSeasons: serializeDatabaseObjects(seasons), }, }; }; diff --git a/pages/dev/index.tsx b/pages/dev/index.tsx new file mode 100644 index 00000000..092c7530 --- /dev/null +++ b/pages/dev/index.tsx @@ -0,0 +1,95 @@ +import Container from "@/components/Container"; +import { AuthenticationOptions } from "@/lib/Auth"; +import { User } from "@/lib/Types"; +import { serializeDatabaseObject } from "@/lib/UrlResolver"; +import { isDeveloper } from "@/lib/Utils"; +import { GetServerSideProps } from "next"; +import { getServerSession } from "next-auth"; +import Link from "next/link"; +import { makeObjSerializeable } from "../../lib/client/ClientUtils"; + +export default function DevIndex({ user }: { user: User }) { + return ( + +

Developer Dashboard

+

Hello, {user.name}.

+

Dev Pages

+
    +
  • + + Cache Stats and Lookup + +
  • +
  • + + Leveling Visualizer + +
  • +
  • + + LocalStorageDb Tester + +
  • +
  • + + QR Pamphlet Viewer + +
  • +
  • + + Speedtest + +
  • +
  • + + User Analytics + +
  • +
+
+ ); +} + +export const getServerSideProps: GetServerSideProps = async (context) => { + const session = await getServerSession( + context.req, + context.res, + AuthenticationOptions, + ); + + if (!session?.user || !isDeveloper(session.user.email!)) { + return { + redirect: { + destination: "/api/auth/login", + permanent: false, + }, + }; + } + + return { + props: { + user: makeObjSerializeable(serializeDatabaseObject(session.user)), + }, + }; +}; diff --git a/pages/profile.tsx b/pages/profile.tsx index 9cae10d8..d4e0f46c 100644 --- a/pages/profile.tsx +++ b/pages/profile.tsx @@ -14,7 +14,7 @@ import { FaPlus } from "react-icons/fa"; import { getDatabase } from "@/lib/MongoDB"; import CollectionId from "@/lib/client/CollectionId"; import { GetServerSideProps } from "next"; -import { SerializeDatabaseObject } from "@/lib/UrlResolver"; +import { serializeDatabaseObject } from "@/lib/UrlResolver"; import TeamCard from "@/components/TeamCard"; import { UpdateModal } from "@/components/UpdateModal"; import { Analytics } from "@/lib/client/Analytics"; @@ -240,7 +240,7 @@ export default function Profile(props: { teamList: Team[] }) { export const getServerSideProps: GetServerSideProps = async (context) => { const db = await getDatabase(); const teams = await db.findObjects(CollectionId.Teams, {}); - const serializedTeams = teams.map((team) => SerializeDatabaseObject(team)); + const serializedTeams = teams.map((team) => serializeDatabaseObject(team)); return { props: { teamList: serializedTeams }, From caa1731faf339a6336c699ef396e2e5ba447c820 Mon Sep 17 00:00:00 2001 From: renatodellosso Date: Mon, 17 Feb 2025 21:38:28 -0500 Subject: [PATCH 27/27] Fix build issue with missing SMTP password --- lib/ResendUtils.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/ResendUtils.ts b/lib/ResendUtils.ts index 106b1f79..a2a0f468 100644 --- a/lib/ResendUtils.ts +++ b/lib/ResendUtils.ts @@ -5,14 +5,18 @@ import { User } from "./Types"; import CollectionId from "./client/CollectionId"; import { ObjectId } from "bson"; -const resend = new Resend(process.env.SMTP_PASSWORD); - export interface ResendInterface { createContact: (rawUser: NextAuthUser) => Promise; emailDevelopers: (subject: string, message: string) => void; } export class ResendUtils implements ResendInterface { + private static resend: Resend; + + constructor() { + ResendUtils.resend ??= new Resend(process.env.SMTP_PASSWORD); + } + async createContact(rawUser: NextAuthUser) { const user = rawUser as User; @@ -27,7 +31,7 @@ export class ResendUtils implements ResendInterface { const nameParts = user.name?.split(" "); - const res = await resend.contacts.create({ + const res = await ResendUtils.resend.contacts.create({ email: user.email, firstName: nameParts[0], lastName: nameParts.length > 1 ? nameParts[1] : "", @@ -60,7 +64,7 @@ export class ResendUtils implements ResendInterface { return; } - resend.emails.send({ + ResendUtils.resend.emails.send({ from: "Gearbox Server ", to: JSON.parse(process.env.DEVELOPER_EMAILS), // Environment variables are always strings, so we need to parse it subject,