Skip to content

Commit

Permalink
Judge dashboard change and debug (#501)
Browse files Browse the repository at this point in the history
* change judging and organizer logic and ui

* remove all groups from dropdown in judging page

* remove time constraint of judging

* debug the judging dashboard, change the get judgingsession method, add have judged property to judging session dto

* typo

* let organizers to set time for one judge to score one team, changed the corresponding backend and database

* fix type error

* fix the bug: we should clear all the elements in judging session whenever we update the settings

* fix bugs in hacker page

---------

Co-authored-by: Isaac Liu <[email protected]>
  • Loading branch information
JihengLi and xnscdev authored Sep 17, 2024
1 parent 7247341 commit 293f7f5
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 141 deletions.
49 changes: 25 additions & 24 deletions components/Organizer/ScheduleTab/ScheduleTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,26 @@ const ScheduleTab = () => {
const [timesJudged, setTimesJudged] = useState<number>(1);
const [maxTimesJudged, setMaxTimesJudged] = useState<number>(0);
const [potentialSchedule, setPotentialSchedule] = useState<JudgingSessionData[] | undefined>(undefined);
const [hackathonSettings, setHackathonSettings] = useState<HackathonSettingsData | undefined>(undefined);

const { baseTheme } = useContext(ThemeContext);

// Get hackathon settings
useEffect(() => {
const fetchHackathonSettings = async () => {
const res = await fetch('/api/hackathon-settings');
if (res.ok) {
const settings = await res.json();
setHackathonSettings(settings as HackathonSettingsData);
}
};
fetchHackathonSettings();
}, []);

const TimeForJudgeToScoreOneTeam = parseInt(hackathonSettings?.JUDGING_TIME_PER_TEAM as string, 10);
const JudgingDuration = hackathonSettings?.JUDGING_DURATION as number;
const MaximumNumberOfTeamsOneJudgeCanFinishInJudgingTime = JudgingDuration / TimeForJudgeToScoreOneTeam;

// Get judging sessions
const { data: judgingSessions, error: judgingSessionsError } = useCustomSWR<JudgingSessionData[]>({
url: '/api/judging-sessions',
Expand All @@ -37,13 +54,6 @@ const ScheduleTab = () => {
errorMessage: 'Failed to get list of teams.',
});

// Get hackathon settings
const { data: hackathonSettings, error: hackathonError } = useCustomSWR<HackathonSettingsData>({
url: '/api/hackathon-settings',
method: RequestType.GET,
errorMessage: 'Failed to get hackathon times.',
});

// Confirm potential schedule
const handleConfirmPotentialSchedules = (potentialSchedule: JudgingSessionData[] | undefined) => {
// Exit early if we don't have data yet
Expand All @@ -67,33 +77,23 @@ const ScheduleTab = () => {
let judgingTimes = generateTimes(
new Date(hackathonSettings?.JUDGING_START as string),
new Date(hackathonSettings?.JUDGING_END as string),
10
TimeForJudgeToScoreOneTeam
);
setPotentialSchedule(matchTeams(teams, judges, judgingTimes, timesJudged));
};

useEffect(() => {
if (!teamsData || !judgesData) return;
setMaxTimesJudged(Math.floor((judgesData?.length * 12) / teamsData?.length));
}, [teamsData, judgesData]);
setMaxTimesJudged(
Math.floor((judgesData?.length * MaximumNumberOfTeamsOneJudgeCanFinishInJudgingTime) / teamsData?.length)
);
}, [teamsData, judgesData, hackathonSettings]);

useEffect(() => {
// Exit early if we don't have data yet
if (!judgingSessions) return;

// Sort judging sessions by time
const time = new Date('2022-10-23T11:00:00').getTime();

// Set the data after filtering it by time
setPotentialSchedule(
judgingSessions.filter(judgingSession => {
let time = new Date(judgingSession.time as string);
return (
new Date(hackathonSettings?.JUDGING_START as string) <= time &&
time <= new Date(hackathonSettings?.JUDGING_END as string)
);
})
);
// Set the data
setPotentialSchedule(judgingSessions);
}, [judgingSessions, hackathonSettings]);

// Combine all the loading, null, and error states
Expand Down Expand Up @@ -162,6 +162,7 @@ const ScheduleTab = () => {
handleChange={function (value: SetStateAction<string>): void {
throw new Error('Function not implemented.');
}}
TimeForJudgeToScoreOneTeam={TimeForJudgeToScoreOneTeam}
sessionTimeStart={new Date(hackathonSettings?.JUDGING_START as string)}
sessionTimeEnd={new Date(hackathonSettings?.JUDGING_END as string)}
/>
Expand Down
33 changes: 28 additions & 5 deletions components/Organizer/SettingsTab/HackathonSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, DatePicker, Select, Space } from 'antd';
import { Button, DatePicker, Input, Select, Space } from 'antd';
import dayjs from 'dayjs';
import React, { useContext, useEffect, useState } from 'react';
import { handleSubmitFailure, handleSubmitSuccess } from '../../../lib/helpers';
Expand Down Expand Up @@ -89,6 +89,7 @@ const HackathonSettings = () => {
setHackathonSetting(settings as HackathonSettingsData);
setStatusMessage('Successfully saved to database!');
handleSubmitSuccess('Successfully saved to database!');
window.location.reload();
} else {
setStatusMessage('Failed to save to database!');
handleSubmitFailure('Failed to save to database!');
Expand All @@ -114,14 +115,16 @@ const HackathonSettings = () => {
};

const handleJudgingChange = (_: any, dateStrings: [string, string]) => {
const newJudgingStart = dayjs(dateStrings[0], { utc: true }).format('MM/DD/YYYY hh:mm A');
const newJudgingEnd = dayjs(dateStrings[1], { utc: true }).format('MM/DD/YYYY hh:mm A');
const newJudgingStart = dayjs(dateStrings[0], { utc: true });
const newJudgingEnd = dayjs(dateStrings[1], { utc: true });
const judgingDuration = newJudgingEnd.diff(newJudgingStart, 'minute');
setHackathonSetting(prev => {
if (prev)
return {
...prev,
JUDGING_START: newJudgingStart,
JUDGING_END: newJudgingEnd,
JUDGING_START: newJudgingStart.format('MM/DD/YYYY hh:mm A'),
JUDGING_END: newJudgingEnd.format('MM/DD/YYYY hh:mm A'),
JUDGING_DURATION: judgingDuration,
};
return undefined;
});
Expand Down Expand Up @@ -161,6 +164,26 @@ const HackathonSettings = () => {
defaultValue={[dayjs(hackathonSetting?.JUDGING_START), dayjs(hackathonSetting?.JUDGING_END)]}
/>

<br />

<div>Time for Judge to Score One Team (minutes):</div>
<Input
value={hackathonSetting?.JUDGING_TIME_PER_TEAM}
onChange={e => {
setHackathonSetting(prev => {
if (prev) {
return {
...prev,
JUDGING_TIME_PER_TEAM: e.target.value,
};
}
return prev;
});
}}
placeholder="Enter time in minutes"
style={{ width: 138 }}
/>

<br />
<br />

Expand Down
2 changes: 1 addition & 1 deletion components/hacker/HackerDash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
<div style={{ padding: '20px' }}>
<Header user={user} signOut={signOut} setting={setting as HackathonSettingsData} />

<TeamManagement />
{/* <TeamManagement /> */}
<JudgingSchedule judgingSessionData={judgingSessionData} />
<Leaderboard />

Expand Down
8 changes: 3 additions & 5 deletions components/hacker/JudgingSchedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,23 @@ const JudgingSchedule = ({ judgingSessionData }: JudgingScheduleProps) => {
return (
<div className={styles.Container}>
Judging Schedule
<div className={styles.Description}>
You will be assigned a table and judge for judging. Please be at your table at the time indicated below.
</div>
<div className={styles.Description}>You will be assigned a table and judge for judging.</div>
{judgingSessionData?.length === 0 ? (
<div className={styles.Placeholder}>Schedule will show up here when hacking ends!</div>
) : (
<div className={styles.TableContainer}>
<table>
<thead>
<tr>
<th>Time</th>
{/* <th>Time</th> */}
<th>Table</th>
<th>Judge</th>
</tr>
</thead>
<tbody>
{judgingSessionData?.map(entry => (
<tr key={entry.time.toString()}>
<td>{renderJudgingTime(entry.time.toString())}</td>
{/* <td>{renderJudgingTime(entry.time.toString())}</td> */}
<td>{entry.team.locationNum}</td>
<td>{entry.judge.name}</td>
</tr>
Expand Down
52 changes: 2 additions & 50 deletions components/judges/JudgeDash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ async function handleSubmit(
mutate('/api/teams');
mutate('/api/judging-form');
handleSubmitSuccess(`Successfully ${isNewForm ? 'submitted' : 'updated'}!`);
if (isNewForm) window.location.reload();
setIsNewForm(false);
} else {
handleSubmitFailure(await res.text());
Expand All @@ -54,8 +55,6 @@ async function handleSubmit(
export default function JudgeDash() {
const { data: session, status } = useSession();
const [teamID, setTeamID] = useState('');
const [currentScheduleItem, setCurrentScheduleItem] = useState<JudgingSessionData | undefined>(undefined);
const [nextScheduleItem, setNextScheduleItem] = useState<JudgingSessionData | undefined>(undefined);
const [isNewForm, setIsNewForm] = useState(false);
const [nextIndex, setNextIndex] = useState(-1);
const { mutate } = useSWRConfig();
Expand Down Expand Up @@ -132,51 +131,6 @@ export default function JudgeDash() {
return (await res.json()) as JudgingSessionData[];
});

// Initialize state if data was just received
useEffect(() => {
if (nextIndex === -1 && scheduleData) {
const now = Date.now();
let index = scheduleData.findIndex(el => now < new Date(el.time as string).getTime());
if (index === -1) index = scheduleData.length;
let currentlyGoingTime = new Date(scheduleData[index - 1]?.time as string).getTime() + judgingLength;
setNextScheduleItem(scheduleData[index]);
setCurrentScheduleItem(now < currentlyGoingTime ? scheduleData[index - 1] : undefined);
setNextIndex(index);
}
}, [scheduleData, nextIndex, judgingLength]);

// Loop to manage current schedule state
useEffect(() => {
const interval = setInterval(() => {
const now = Date.now();
if (scheduleData && nextIndex > -1) {
// Data has been received and state is initialized
let time2 = new Date(scheduleData[scheduleData.length - 1]?.time as string).getTime() + judgingLength;
if (now <= time2) {
// Not yet done judging
let time3 = new Date((currentScheduleItem?.time as string) || 0).getTime() + judgingLength;

if (
nextIndex < scheduleData.length &&
now >= new Date((nextScheduleItem?.time as string) || 0).getTime()
) {
// Next event should be current
setNextScheduleItem(scheduleData[nextIndex + 1]);
setCurrentScheduleItem(scheduleData[nextIndex]);
setNextIndex(nextIndex + 1);
} else if (now > time3) {
// Next event has not yet arrived but current event is over
setCurrentScheduleItem(undefined);
}
} else {
// Done judging
setCurrentScheduleItem(undefined);
}
}
}, 1000);
return () => clearInterval(interval);
});

const handleTeamChange: Dispatch<SetStateAction<string>> = e => {
setTeamID(e);
setTimeout(() => {
Expand Down Expand Up @@ -204,9 +158,7 @@ export default function JudgeDash() {
</div>
</div>
</div>
{scheduleData && (
<JudgeSchedule data={scheduleData} cutoffIndex={nextIndex} handleChange={handleTeamChange} />
)}
{scheduleData && <JudgeSchedule data={scheduleData} handleChange={handleTeamChange} />}
<br />
<br />
{teamsData && <TeamSelect teamsData={teamsData} currentTeamID={teamID} handleChange={handleTeamChange} />}
Expand Down
7 changes: 0 additions & 7 deletions components/judges/TeamSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,6 @@ export default function TeamSelect(props: TeamSelectProps) {
))}
</OptGroup>
)}
<OptGroup label="All Teams">
{teamsData.map(team => (
<Option value={team._id} key={`${team._id}ALL`}>
{team.haveJudged ? withCheckMark(team.name) : team.name}
</Option>
))}
</OptGroup>
</Select>
</Space>
);
Expand Down
Loading

0 comments on commit 293f7f5

Please sign in to comment.