Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple QOL changes #59

Merged
merged 5 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added client/src/assets/alarm.mp3
Binary file not shown.
51 changes: 51 additions & 0 deletions client/src/components/InfoPopup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
interface InfoPopupProps {
/* State variable for open/closed */
enabled: boolean;

/* Function to modify the popup state variable */
setEnabled: React.Dispatch<React.SetStateAction<boolean>>;

/* Title Text */
title: string;

/* Submit Text */
submitText: string;

/* React children, corresponds to the body content */
children?: React.ReactNode;

/* If true, button is red */
red?: boolean;
}

const InfoPopup = (props: InfoPopupProps) => {
if (!props.enabled) {
return null;
}

return (
<>
<div
className="fixed left-0 top-0 z-20 w-screen h-screen bg-black/30"
onClick={() => props.setEnabled(false)}
></div>
<div className="bg-background fixed z-30 left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] py-6 px-10 w-1/3">
<h1 className="text-5xl font-bold mb-2 text-center">{props.title}</h1>
<p className="text-xl">{props.children}</p>
<div className="flex justify-center">
<button
className={
'text-white rounded-full px-4 py-2 mt-4 w-2/5 font-bold text-2xl hover:brightness-110 duration-200 ' +
(props.red ? 'bg-error' : 'bg-primary')
}
onClick={() => props.setEnabled(false)}
>
{props.submitText}
</button>
</div>
</div>
</>
);
};

export default InfoPopup;
6 changes: 3 additions & 3 deletions client/src/components/admin/AdminStatsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,16 @@ const AdminStatsPanel = () => {
<PauseButton paused={paused} setPaused={setPaused} clock={time} />
<div className="flex justify-evenly basis-2/5">
<AdminStat name="Projects" value={stats.projects} />
<AdminStat name="Seen" value={stats.seen} />
<AdminStat name="Votes" value={stats.votes} />
<AdminStat name="Average Seen" value={stats.avg_seen} />
<AdminStat name="Average Votes" value={stats.avg_votes} />
</div>
<AdminStat
name="Judging Time"
value={msToTime(time)}
className={'basis-1/5' + (paused ? ' text-error' : '')}
/>
<div className="flex justify-evenly basis-2/5">
<AdminStat name="Average Mu" value={stats.avg_mu} />
<AdminStat name="Highest Mu" value={stats.max_mu} />
<AdminStat name="Average Sigma^2" value={stats.avg_sigma} />
<AdminStat name="Judges" value={stats.judges} />
</div>
Expand Down
103 changes: 92 additions & 11 deletions client/src/pages/admin/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const AdminSettings = () => {
const [reassignPopup, setReassignPopup] = useState(false);
const [groupChecked, setGroupChecked] = useState(false);
const [groups, setGroups] = useState('');
const [judgingTimer, setJudgingTimer] = useState('');
const [loading, setLoading] = useState(true);

async function getOptions() {
Expand All @@ -25,6 +26,15 @@ const AdminSettings = () => {
const groupStr = res.data?.groups?.map((g) => `${g.start} ${g.end}`).join('\n');
setGroups(groupStr || '');

// Calculate judging timer MM:SS
const timer = res.data?.judging_timer;
if (timer) {
const minutes = Math.floor(timer / 60);
const seconds = timer % 60;
const timerStr = `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
setJudgingTimer(timerStr);
}

setLoading(false);
}

Expand Down Expand Up @@ -57,7 +67,32 @@ const AdminSettings = () => {
getOptions();
};

const exportData = async (type: string) => {
const updateTimer = async () => {
// Convert judging timer to time
const [minutes, seconds] = judgingTimer.split(':');
const timer = parseInt(minutes) * 60 + parseInt(seconds);
if (isNaN(timer)) {
alert('Invalid timer format!');
return;
}
if (timer <= 0) {
alert('Timer must be greater than 0!');
return;
}

const res = await postRequest<OkResponse>('/admin/timer', 'admin', {
judging_timer: timer,
});
if (res.status !== 200 || res.data?.ok !== 1) {
errorAlert(res);
return;
}

alert('Timer updated!');
getOptions();
};

const exportCsv = async (type: string) => {
const res = await fetch(`${process.env.REACT_APP_JURY_URL}/admin/export/${type}`, {
method: 'GET',
headers: createHeaders('admin', false),
Expand All @@ -67,12 +102,29 @@ const AdminSettings = () => {
alert('Error exporting data: ' + res.statusText);
return;
}

const blob = await res.blob();

saveToFile(await res.blob() as Blob, type, 'csv');
};

const exportByChallenge = async () => {
const res = await fetch(`${process.env.REACT_APP_JURY_URL}/admin/export/challenges`, {
method: 'GET',
headers: createHeaders('admin', false),
});

if (res.status !== 200) {
alert('Error exporting data: ' + res.statusText);
return;
}

saveToFile(await res.blob() as Blob, 'challenge-projects', 'zip');
};

const saveToFile = (blob: Blob, name: string, ext: string) => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.setAttribute('download', `${type}.csv`);
a.setAttribute('download', `${name}.${ext}`);
document.body.appendChild(a);
a.click();
a.remove();
Expand All @@ -84,8 +136,30 @@ const AdminSettings = () => {
<div className="flex flex-col items-start justify-center w-full px-8 py-4 md:px-16 md:py-8">
<h1 className="text-4xl font-bold">Settings</h1>
{/* TODO: Add other settings */}
<h2 className="text-xl mt-4">Project Numbers</h2>
<h3 className="text-lg font-bold">Reassign Project Numbers</h3>
<h2 className="text-4xl mt-8 mb-2 text-primary">Judging Timer</h2>
<h3 className="text-xl font-bold">Set Judging Timer</h3>
<p className="text-light">
Set how long judges have to view each project. This will reflect on the timer
that shows on the judging page.
</p>
<input
className="w-full h-14 px-4 text-2xl border-lightest border-2 rounded-sm focus:border-primary focus:border-4 focus:outline-none"
type="string"
placeholder="MM:SS"
value={judgingTimer}
onChange={(e) => {
setJudgingTimer(e.target.value);
}}
/>
<Button
type="primary"
onClick={updateTimer}
className="mt-4 w-auto md:w-auto px-4 py-2"
>
Update Timer
</Button>
<h2 className="text-4xl mt-8 mb-2 text-primary">Project Numbers</h2>
<h3 className="text-xl font-bold">Reassign Project Numbers</h3>
<p className="text-light">
Reassign all table numbers to the projects. This will keep the relative order
but reassign the table numbers starting from the first project.
Expand All @@ -99,7 +173,7 @@ const AdminSettings = () => {
>
Reassign
</Button>
<h3 className="text-lg font-bold mt-4">Table Groupings</h3>
<h3 className="text-xl font-bold mt-4">Table Groupings</h3>
<p className="text-light">
Check this box to use table groupings. This will force judges to stay in a
grouping for 3 rounds before moving on. This ideally should decrease the
Expand Down Expand Up @@ -135,14 +209,14 @@ const AdminSettings = () => {
>
Update Groupings
</Button>
<h2 className="text-xl mt-4">Export Data</h2>
<h2 className="text-4xl mt-8 mb-2 text-primary">Export Data</h2>
<h3 className="text-lg font-bold">Export Collections</h3>
<p className="text-light">Export each collection individually as a CSV download.</p>
<div className="flex">
<Button
type="primary"
onClick={() => {
exportData('judges');
exportCsv('judges');
}}
className="mt-4 w-auto md:w-auto px-4 py-2 mr-4"
>
Expand All @@ -151,12 +225,19 @@ const AdminSettings = () => {
<Button
type="primary"
onClick={() => {
exportData('projects');
exportCsv('projects');
}}
className="mt-4 w-auto md:w-auto px-4 py-2"
className="mt-4 w-auto md:w-auto px-4 py-2 mr-4"
>
Export Projects
</Button>
<Button
type="primary"
onClick={exportByChallenge}
className="mt-4 w-auto md:w-auto px-4 py-2"
>
Export by Challenges
</Button>
</div>
</div>
<Popup
Expand Down
Loading