Skip to content

Commit

Permalink
Settings hackathon (#370)
Browse files Browse the repository at this point in the history
* enable intellisence for css via typescript-plugin-css-modules

* big brain moment with types. refactor fn to return ThemedClass type instead of generic string

* fix import styles errors that arose thanks to typescript-plugin-css-modules

* implement model and api for hackathon-settings

* implement hackathon settings to view/change important dates

* fix codeql vuln

* install dayjs bc antd wants it :(

* why do timezones even exist zzzzzz
  • Loading branch information
zineanteoh authored Oct 7, 2023
1 parent bd7f81c commit 002d196
Show file tree
Hide file tree
Showing 13 changed files with 583 additions and 52 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"deepscan.enable": true
"deepscan.enable": true,
"typescript.tsdk": "node_modules/typescript/lib"
}
2 changes: 1 addition & 1 deletion components/Organizer/OrganizerDash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function OrganizerDash() {
if (!userData) return <Skeleton />;

return (
<div className={styles[getThemedClass('organizerMain', baseTheme)]}>
<div>
<div className={styles[getThemedClass('organizerHeader', baseTheme)]}>
<h1 className={styles[getThemedClass('organizerTitle', baseTheme)]}>Organizer Dashboard</h1>
<div className={styles[getThemedClass('organizerHeaderEmail', baseTheme)]}>
Expand Down
118 changes: 118 additions & 0 deletions components/Organizer/SettingsTab/HackathonSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { Button, DatePicker, Space } from 'antd';
import dayjs from 'dayjs';
import React, { useContext, useEffect, useState } from 'react';
import { handleSubmitFailure, handleSubmitSuccess } from '../../../lib/helpers';
import { getBaseColor, ThemeContext } from '../../../theme/themeProvider';
import { HackathonSettingsData } from '../../../types/database';

const HackathonSettings = () => {
const { baseTheme } = useContext(ThemeContext);
const [hackathonSetting, setHackathonSetting] = useState<HackathonSettingsData | undefined>(undefined);
const [loading, setLoading] = useState<boolean>(false);
const [statusMessage, setStatusMessage] = useState<string>('');

// fetch hackathon settings on load at /api/hackathon-settings
useEffect(() => {
const fetchHackathonSettings = async () => {
const res = await fetch('/api/hackathon-settings');
if (res.ok) {
const settings = await res.json();
setHackathonSetting(settings as HackathonSettingsData);
setLoading(false);
}
};
setLoading(true);
fetchHackathonSettings();
}, []);

// handle save button
const handleSave = async () => {
if (
window.confirm(
'IMPORTANT: \nAre you sure you wish to save these dates?\nMake sure you know what you are doing!'
)
) {
const res = await fetch('/api/hackathon-settings', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(hackathonSetting),
});
if (res.ok) {
const settings = await res.json();
setHackathonSetting(settings as HackathonSettingsData);
setStatusMessage('Successfully saved to database!');
handleSubmitSuccess('Successfully saved to database!');
} else {
setStatusMessage('Failed to save to database!');
handleSubmitFailure('Failed to save to database!');
}
}
};

const handleHackathonChange = (_: any, dateStrings: [string, string]) => {
const newHackathonStart = dayjs(dateStrings[0], 'MM-DD-YYYY hh:mm A').format('MM/DD/YYYY hh:mm A');
const newHackathonEnd = dayjs(dateStrings[1], { utc: true }).format('MM/DD/YYYY hh:mm A');
console.log('Changing Hackathon: ', newHackathonStart);
console.log('Changing Hackathon: ', newHackathonEnd);
setHackathonSetting(prev => {
if (prev)
return {
...prev,
HACKATHON_START: newHackathonStart,
HACKATHON_END: newHackathonEnd,
};
return undefined;
});
setStatusMessage('Changes not saved yet.');
};

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');
setHackathonSetting(prev => {
if (prev)
return {
...prev,
JUDGING_START: newJudgingStart,
JUDGING_END: newJudgingEnd,
};
return undefined;
});
setStatusMessage('Changes not saved yet.');
};

if (loading) return <div>Loading...</div>;

return (
<div style={{ color: getBaseColor(baseTheme) }}>
<div>Hackathon Start & End</div>
<DatePicker.RangePicker
format="MM/DD/YYYY hh:mm A"
showTime={{ format: 'hh:mm A' }}
onChange={handleHackathonChange}
defaultValue={[dayjs(hackathonSetting?.HACKATHON_START), dayjs(hackathonSetting?.HACKATHON_END)]}
/>

<br />

<div>Judging Start & End</div>
<DatePicker.RangePicker
format="MM/DD/YYYY hh:mm A"
showTime={{ format: 'hh:mm A' }}
onChange={handleJudgingChange}
defaultValue={[dayjs(hackathonSetting?.JUDGING_START), dayjs(hackathonSetting?.JUDGING_END)]}
/>

<br />
<br />

<Button onClick={handleSave}>Save dates to database</Button>

{statusMessage && <div>{statusMessage}</div>}
</div>
);
};

export default HackathonSettings;
9 changes: 8 additions & 1 deletion components/Organizer/SettingsTab/SettingsTab.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import HackathonSettings from './HackathonSettings';
import ThemeControl from './ThemeControl';

const SettingsTab = () => {
return <ThemeControl />;
return (
<>
<HackathonSettings />
<hr />
<ThemeControl />
</>
);
};

export default SettingsTab;
38 changes: 2 additions & 36 deletions components/hacker/HackerDash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,27 +231,21 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Button>
</div>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>First Name</p>}
name="firstName"
rules={[{ required: true, message: 'Please input your first name!' }]}>
<Input className={styles.Input} />
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>Last Name</p>}
name="lastName"
rules={[{ required: true, message: 'Please input your last name!' }]}>
<Input className={styles.Input} />
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>Preferred Name</p>}
name="preferredName">
<Form.Item label={<p className={styles.Label}>Preferred Name</p>} name="preferredName">
<Input className={styles.Input} />
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>Gender</p>}
name="gender"
rules={[{ required: true, message: 'Please select an option!' }]}>
Expand All @@ -263,35 +257,30 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Radio.Group>
</Form.Item>
<Form.Item
className={styles.FormItem}
name="dateOfBirth"
label={<p className={styles.Label}>Date of Birth</p>}
rules={[{ required: true, message: 'Please select your date of birth!' }]}>
<DatePicker placeholder="MM-DD-YYYY" format="MM-DD-YYYY" />
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>Phone Number</p>}
name="phoneNumber"
rules={[{ required: true, message: 'Please input your phone number!' }]}>
<Input className={styles.Input} />
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>School</p>}
name="school"
rules={[{ required: true, message: 'Please input your school!' }]}>
<Input className={styles.Input} />
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>Major</p>}
name="major"
rules={[{ required: true, message: 'Please input your major!' }]}>
<Input className={styles.Input} />
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>Graduation Year</p>}
name="graduationYear"
rules={[{ required: true, message: 'Please select your graduation year!' }]}>
Expand All @@ -304,37 +293,30 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Radio.Group>
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>Address Line 1</p>}
name="address1"
rules={[{ required: true, message: 'Please input your address!' }]}>
<Input className={styles.Input} />
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>Address Line 2</p>}
name="address2">
<Form.Item label={<p className={styles.Label}>Address Line 2</p>} name="address2">
<Input className={styles.Input} />
</Form.Item>

<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>City</p>}
name={'city'}
rules={[{ required: true, message: 'Please input your city!' }]}>
<Input className={styles.Input} />
</Form.Item>

<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>State</p>}
name={'state'}
rules={[{ required: true, message: 'Please input your state!' }]}>
<Input className={styles.Input} />
</Form.Item>

<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>ZIP Code</p>}
name={'zip'}
rules={[
Expand All @@ -355,20 +337,17 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Form.Item>

<Form.Item
className={styles.FormItem}
name="race"
label={<p className={styles.Label}>Race</p>}
rules={[{ required: true, message: 'Please select at least one option!' }]}>
<Checkbox.Group className={styles.TextWhite} options={race} />
</Form.Item>
<Form.Item
className={styles.FormItem}
name="dietaryRestrictions"
label={<p className={styles.Label}>Dietary Restrictions</p>}>
<Checkbox.Group className={styles.TextWhite} options={dietaryRestrictions} />
</Form.Item>
<Form.Item
className={styles.FormItem}
name="accommodationNeeds"
label={<p className={styles.Label}>Accommodation needs</p>}>
<Input
Expand All @@ -377,7 +356,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
/>
</Form.Item>
<Form.Item
className={styles.FormItem}
name="firstTime"
label={<p className={styles.Label}>First-time hacker?</p>}
rules={[{ required: true, message: 'Please select an option!' }]}
Expand All @@ -388,7 +366,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Radio.Group>
</Form.Item>
<Form.Item
className={styles.FormItem}
name="whyAttend"
label={<p className={styles.Label}>Why would you like to attend VandyHacks?</p>}
rules={[
Expand All @@ -404,7 +381,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
/>
</Form.Item>
<Form.Item
className={styles.FormItem}
name="techIndustry"
label={
<p className={styles.Label}>
Expand All @@ -419,7 +395,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
/>
</Form.Item>
<Form.Item
className={styles.FormItem}
name="techStack"
label={
<p className={styles.Label}>Which tech stack, if any, are you familiar with?</p>
Expand All @@ -432,7 +407,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
/>
</Form.Item>
<Form.Item
className={styles.FormItem}
name="passion"
label={<p className={styles.Label}>What are you passionate about?</p>}
rules={[
Expand All @@ -445,14 +419,12 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
/>
</Form.Item>
<Form.Item
className={styles.FormItem}
name="motivation"
label={<p className={styles.Label}>What do you hope to gain from VandyHacks?</p>}
rules={[{ required: true, message: 'Please select at least one option!' }]}>
<Checkbox.Group className={styles.TextWhite} options={motivation} />
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>Shirt Size</p>}
name="shirtSize"
rules={[{ required: true, message: 'Please select your shirt size!' }]}>
Expand All @@ -466,7 +438,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Radio.Group>
</Form.Item>
<Form.Item
className={styles.FormItem}
label={<p className={styles.Label}>Résumé</p>}
rules={[
{
Expand Down Expand Up @@ -504,7 +475,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Upload>
</Form.Item>
<Form.Item
className={styles.FormItem}
label={
<p className={styles.Label}>
Would you like to apply for travel reimbursements?
Expand All @@ -518,7 +488,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Radio.Group>
</Form.Item>
<Form.Item
className={styles.FormItem}
name="overnight"
label={
<p className={styles.LabelOvernight}>Are you staying overnight in the venue?</p>
Expand All @@ -530,7 +499,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Radio.Group>
</Form.Item>
<Form.Item
className={styles.FormItem}
name="prizeEligibility"
label={
<p className={styles.LabelCitizen}>
Expand All @@ -546,7 +514,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Radio.Group>
</Form.Item>
<Form.Item
className={styles.FormItem}
label={
<p className={styles.LabelContact}>
Would you like to be contacted about volunteering at the event?
Expand All @@ -561,7 +528,6 @@ export default function HackerDash({ userApplicationStatus, setUserApplicationSt
</Form.Item>
<br />
<Form.Item
className={styles.FormItem}
valuePropName="checked"
name="agreement1"
rules={[
Expand Down
2 changes: 1 addition & 1 deletion components/judges/JudgeDash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export default function JudgeDash() {
};

return (
<div className={styles[getThemedClass('judgeMain', baseTheme)]}>
<div>
<div className={styles[getThemedClass('judgeHeader', baseTheme)]}>
<h1 className={styles[getThemedClass('judgeTitle', baseTheme)]}>Judging Dashboard</h1>
<div className={styles[getThemedClass('judgeHeaderEmail', baseTheme)]}>
Expand Down
11 changes: 11 additions & 0 deletions models/hackathon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import mongoose, { Schema } from 'mongoose';

const HackathonSchema = new Schema({
HACKATHON_START: { type: String, required: true }, // MM/DD/YYYY hh:mm A
HACKATHON_END: { type: String, required: true }, // MM/DD/YYYY hh:mm A
JUDGING_START: { type: String, required: true }, // MM/DD/YYYY hh:mm A
JUDGING_END: { type: String, required: true }, // MM/DD/YYYY hh:mm A
});

// prevent recompilation of model if it already exists
export default mongoose.models.Hackathon || mongoose.model('Hackathon', HackathonSchema);
Loading

1 comment on commit 002d196

@vercel
Copy link

@vercel vercel bot commented on 002d196 Oct 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.