Skip to content

Commit 941ae60

Browse files
Merge pull request #457 from Decatur-Robotics/main
Updating offline mode
2 parents e872191 + 1fa4a42 commit 941ae60

28 files changed

Lines changed: 1101 additions & 368 deletions

.env.production

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
# We have to include these at build time, so this file is used to inject them into the build process.
33
NEXT_PUBLIC_API_URL=/api/
44
NEXT_PUBLIC_SLACK_CLIENT_ID=10831824934.7404945710466
5-
NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=G-1BFJYBDC76
5+
NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=G-1BFJYBDC76
6+
NEXT_PUBLIC_RECAPTCHA_KEY=6Le63OUqAAAAABxxDrbaU9OywDLLHqutVwbw7a9d

.env.test

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ DEVELOPER_EMAILS=["test@gmail.com"]
44

55
TOA_URL=https://example.com
66
TOA_APP_ID=123
7-
TOA_KEY=456
7+
TOA_KEY=456
8+
9+
DEFAULT_IMAGE=https://example.com/default.jpg

components/Container.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,6 @@ export default function Container(props: ContainerProps) {
7474
}, [eventSearch]);
7575

7676
useEffect(() => {
77-
if (window.location.href.includes("signin")) {
78-
console.log("triggered");
79-
location.reload();
80-
}
81-
8277
const loadTeams = async () => {
8378
if (!user) {
8479
return;
@@ -231,7 +226,7 @@ export default function Container(props: ContainerProps) {
231226
</Link>
232227
) : (
233228
<a
234-
href={"/api/auth/signin"}
229+
href={"/signin"}
235230
rel="noopener noreferrer"
236231
target="_blank"
237232
>
@@ -268,7 +263,7 @@ export default function Container(props: ContainerProps) {
268263
<h2 className="card-title">Wait a minute...</h2>
269264
<p>You need to sign in first!</p>
270265
<div className="card-actions justify-end">
271-
<Link href={"/api/auth/signin"}>
266+
<Link href={"/signin"}>
272267
<button className="btn btn-primary">Sign In</button>
273268
</Link>
274269
</div>

components/competition/InsightsAndSettingsCard.tsx

Lines changed: 96 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,136 @@
1-
import { NotLinkedToTba } from "@/lib/client/ClientUtils";
1+
import { download, NotLinkedToTba } from "@/lib/client/ClientUtils";
22
import { defaultGameId } from "@/lib/client/GameId";
33
import { Round } from "@/lib/client/StatsMath";
44
import { games } from "@/lib/games";
5-
import { Competition, Pitreport, Report, Team } from "@/lib/Types";
5+
import { Competition, MatchType, Pitreport, Report, Team } from "@/lib/Types";
66
import Link from "next/link";
7-
import { ChangeEvent } from "react";
7+
import { ChangeEvent, useState } from "react";
88
import { BsGearFill, BsClipboard2Check } from "react-icons/bs";
99
import { FaSync, FaBinoculars, FaUserCheck, FaDatabase } from "react-icons/fa";
1010
import { FaUserGroup } from "react-icons/fa6";
11+
import ClientApi from "@/lib/api/ClientApi";
12+
13+
const api = new ClientApi();
1114

1215
export default function InsightsAndSettingsCard(props: {
13-
showSettings: boolean;
14-
setShowSettings: (value: boolean) => void;
1516
isManager: boolean | undefined;
1617
comp: Competition | undefined;
1718
reloadCompetition: () => void;
1819
assignScouters: () => void;
19-
exportAsCsv: () => void;
20-
exportPending: boolean;
2120
showSubmittedMatches: boolean;
2221
toggleShowSubmittedMatches: () => void;
2322
assigningMatches: boolean;
2423
regeneratePitReports: () => void;
25-
newCompName: string | undefined;
26-
setNewCompName: (value: string) => void;
27-
newCompTbaId: string | undefined;
28-
setNewCompTbaId: (value: string) => void;
29-
saveCompChanges: () => void;
30-
redAlliance: number[];
31-
setRedAlliance: (value: number[]) => void;
32-
blueAlliance: number[];
33-
setBlueAlliance: (value: number[]) => void;
34-
matchNumber: number | undefined;
35-
setMatchNumber: (value: number) => void;
36-
createMatch: () => void;
37-
teamToAdd: number;
38-
setTeamToAdd: (value: number) => void;
39-
addTeam: () => void;
4024
submittedReports: number | undefined;
4125
reports: Report[];
4226
loadingScoutStats: boolean;
4327
pitreports: Pitreport[];
4428
submittedPitreports: number | undefined;
45-
togglePublicData: (e: ChangeEvent<HTMLInputElement>) => void;
4629
seasonSlug: string | undefined;
4730
team: Team | undefined;
48-
allianceIndices: number[];
4931
}) {
32+
const [showSettings, setShowSettings] = useState(false);
5033
const {
51-
showSettings,
52-
setShowSettings,
5334
isManager,
5435
comp,
5536
reloadCompetition,
5637
assignScouters,
57-
exportAsCsv,
58-
exportPending,
5938
showSubmittedMatches,
6039
toggleShowSubmittedMatches,
6140
assigningMatches,
62-
newCompName,
63-
setNewCompName,
64-
newCompTbaId,
65-
setNewCompTbaId,
66-
saveCompChanges,
67-
redAlliance,
68-
setRedAlliance,
69-
blueAlliance,
70-
setBlueAlliance,
71-
matchNumber,
72-
setMatchNumber,
73-
createMatch,
74-
teamToAdd,
75-
setTeamToAdd,
76-
addTeam,
7741
submittedReports,
7842
reports,
7943
loadingScoutStats,
8044
pitreports,
8145
submittedPitreports,
82-
togglePublicData,
8346
seasonSlug,
8447
team,
85-
allianceIndices,
8648
} = props;
49+
const [newCompName, setNewCompName] = useState(comp?.name);
50+
const [newCompTbaId, setNewCompTbaId] = useState(comp?.tbaId);
51+
const [exportPending, setExportPending] = useState(false);
52+
const [teamToAdd, setTeamToAdd] = useState<number>(0);
53+
const [blueAlliance, setBlueAlliance] = useState<number[]>([]);
54+
const [redAlliance, setRedAlliance] = useState<number[]>([]);
55+
const [matchNumber, setMatchNumber] = useState<number | undefined>(undefined);
56+
57+
const exportAsCsv = async () => {
58+
setExportPending(true);
59+
60+
const res = await api.exportCompAsCsv(comp?._id!).catch((e) => {
61+
console.error(e);
62+
return { csv: undefined };
63+
});
64+
65+
if (!res) {
66+
console.error("failed to export");
67+
}
68+
69+
if (res.csv) {
70+
download(`${comp?.name ?? "Competition"}.csv`, res.csv, "text/csv");
71+
} else {
72+
console.error("No CSV data returned from server");
73+
}
74+
75+
setExportPending(false);
76+
};
77+
78+
const createMatch = async () => {
79+
try {
80+
await api.createMatch(
81+
comp?._id!,
82+
Number(matchNumber),
83+
0,
84+
MatchType.Qualifying,
85+
blueAlliance as number[],
86+
redAlliance as number[],
87+
);
88+
} catch (e) {
89+
console.error(e);
90+
}
91+
92+
location.reload();
93+
};
94+
95+
const allianceIndices: number[] = [];
96+
for (let i = 0; i < games[comp?.gameId ?? defaultGameId].allianceSize; i++) {
97+
allianceIndices.push(i);
98+
}
99+
100+
async function saveCompChanges() {
101+
// Check if tbaId is valid
102+
if (!comp?.tbaId || !comp?.name || !comp?._id) return;
103+
104+
let tbaId = newCompTbaId;
105+
const autoFillData = await api.competitionAutofill(tbaId ?? "");
106+
if (
107+
!autoFillData?.name &&
108+
!confirm(`Invalid TBA ID: ${tbaId}. Save changes anyway?`)
109+
)
110+
return;
111+
112+
await api.updateCompNameAndTbaId(
113+
comp?._id,
114+
newCompName ?? "Unnamed",
115+
tbaId ?? NotLinkedToTba,
116+
);
117+
location.reload();
118+
}
119+
120+
function togglePublicData(e: ChangeEvent<HTMLInputElement>) {
121+
if (!comp?._id) return;
122+
api.setCompPublicData(comp?._id, e.target.checked);
123+
}
124+
125+
function addTeam() {
126+
console.log("Adding pit report for team", teamToAdd);
127+
if (!teamToAdd || teamToAdd < 1 || !comp?._id) return;
128+
129+
api
130+
.createPitReportForTeam(teamToAdd, comp?._id)
131+
// We can't just pass location.reload, it will throw "illegal invocation." I don't know why. -Renato
132+
.finally(() => location.reload());
133+
}
87134

88135
return (
89136
<div className="w-full card bg-base-200 shadow-xl">
@@ -367,7 +414,7 @@ export default function InsightsAndSettingsCard(props: {
367414
? (+(
368415
Round(submittedReports / reports.length) * 100
369416
)).toFixed(0)
370-
: "?"}
417+
: "0"}
371418
%
372419
</div>
373420
<div className="stat-desc"></div>
@@ -394,7 +441,7 @@ export default function InsightsAndSettingsCard(props: {
394441
? (+(
395442
Round(submittedReports / reports.length) * 100
396443
)).toFixed(0)
397-
: "?"}
444+
: "0"}
398445
%
399446
</div>
400447
<div className="stat-desc">
@@ -412,12 +459,9 @@ export default function InsightsAndSettingsCard(props: {
412459
<FaUserGroup size={40}></FaUserGroup>
413460
</div>
414461
<div className="stat-value text-primary">
415-
{!submittedPitreports && submittedPitreports !== 0
416-
? "?"
417-
: submittedPitreports}
418-
/
462+
{!submittedPitreports ? "0" : submittedPitreports}/
419463
{!pitreports || pitreports.length === 0
420-
? "?"
464+
? "0"
421465
: pitreports.length}
422466
</div>
423467
</div>

components/competition/MatchScheduleCard.tsx

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { MdErrorOutline } from "react-icons/md";
1414
import Avatar from "../Avatar";
1515
import Loading from "../Loading";
1616
import { AdvancedSession } from "@/lib/client/useCurrentSession";
17+
import { useEffect } from "react";
1718

1819
export default function MatchScheduleCard(props: {
1920
team: Team | undefined;
@@ -62,11 +63,14 @@ export default function MatchScheduleCard(props: {
6263
showSubmittedMatches,
6364
} = props;
6465

65-
const displayedMatches = showSubmittedMatches
66-
? matches
67-
: matches.filter((match) =>
68-
match.reports.some((reportId) => !reportsById[reportId]?.submitted),
69-
);
66+
const unsubmittedMatches: Match[] = [];
67+
68+
for (const match of matches) {
69+
if (match.reports.some((reportId) => !reportsById[reportId]?.submitted))
70+
unsubmittedMatches.push(match);
71+
}
72+
73+
const displayedMatches = showSubmittedMatches ? matches : unsubmittedMatches;
7074

7175
return (
7276
<div className="w-full card bg-base-200 shadow-xl ">
@@ -80,38 +84,24 @@ export default function MatchScheduleCard(props: {
8084
)}
8185
</h1>
8286
{isManager &&
83-
matchesAssigned === false &&
84-
Object.keys(usersById).length >= 6 ? (
85-
matchesAssigned !== undefined ? (
87+
matchesAssigned === false &&
88+
Object.keys(usersById).length >= 6 &&
89+
(!assigningMatches ? (
8690
<div className="opacity-100 font-bold text-warning flex flex-row items-center justify-start space-x-3">
87-
<div>
88-
{!assigningMatches
89-
? "Matches are not assigned"
90-
: "Assigning matches"}
91-
</div>
92-
{!assigningMatches ? (
93-
<button
94-
className={
95-
"btn btn-primary btn-sm " +
96-
(assigningMatches ? "disabled" : "")
97-
}
98-
onClick={assignScouters}
99-
>
100-
Assign Matches
101-
</button>
102-
) : (
103-
<BsGearFill
104-
size={30}
105-
className="animate-spin-slow text-white"
106-
/>
107-
)}
91+
<div>Matches are not assigned.</div>
92+
<button
93+
className={
94+
"btn btn-primary btn-sm " +
95+
(assigningMatches ? "disabled" : "")
96+
}
97+
onClick={assignScouters}
98+
>
99+
Assign Matches
100+
</button>
108101
</div>
109102
) : (
110103
<progress className="progress w-full" />
111-
)
112-
) : (
113-
<></>
114-
)}
104+
))}
115105
<div className="divider my-0"></div>
116106
{loadingMatches || loadingReports || loadingUsers ? (
117107
<div className="w-full flex flex-col items-center justify-center">

environment.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ declare global {
4949

5050
DEVELOPER_EMAILS: string;
5151

52+
NEXT_PUBLIC_RECAPTCHA_KEY: string;
53+
RECAPTCHA_SECRET: string;
54+
5255
NODE_ENV: "development" | "production";
5356
}
5457
}

0 commit comments

Comments
 (0)