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

835 frosh olympics page #847

Merged
merged 17 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
c75f9ce
create frosh olympiks event schema
ashleyleal Aug 14, 2024
521ec77
add registeredOlympiksEvents field to Frosh schema w/ reference to Ol…
ashleyleal Aug 14, 2024
c45c77e
Front-end skeleton for frosh olympics
Wozunubi Aug 17, 2024
839db2c
Merge branch '835-frosh-olympiks-page-backend' into 835-frosh-olympic…
ashleyleal Aug 18, 2024
37f960b
create data file for olympiks schedule, add mse sf pit -bathroom-side…
ashleyleal Aug 19, 2024
83ce149
added all discipline schedules to data file
gaurikam2003 Aug 20, 2024
997b0fe
configured olympiks page to show discipline specific schedules and fi…
gaurikam2003 Aug 20, 2024
f4c3a68
edit olympiks event schema to better match data
ashleyleal Aug 21, 2024
f1e279f
made some updates to frosh olympiks schedule
gaurikam2003 Aug 22, 2024
e33ad8a
Merge branch '835-frosh-olympics-page' of https://github.com/UofT-Fro…
gaurikam2003 Aug 22, 2024
13a4725
added protection to make sure that only registered frosh can access f…
gaurikam2003 Aug 22, 2024
654c337
frontend olympiks event signup handling
ashleyleal Aug 22, 2024
2d2d7af
fix snackbar import in olympiks schedule
ashleyleal Aug 22, 2024
f112f36
implement olympiks endpoints and controller
ashleyleal Aug 22, 2024
42c95d5
remove event signups for now and add discipline to olympiks page header
ashleyleal Aug 22, 2024
50325bd
added sign up links and removed blank activity descriptions
gaurikam2003 Aug 25, 2024
2118186
Merge branch 'dev' into 835-frosh-olympics-page
gaurikam2003 Aug 27, 2024
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/misc/torch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3,276 changes: 3,276 additions & 0 deletions client/src/assets/olympiksSchedule/data.jsx

Large diffs are not rendered by default.

235 changes: 235 additions & 0 deletions client/src/components/schedule/ScheduleOlympiks/ScheduleOlympiks.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import React, { useState, useEffect, useContext } from 'react';
import { useSelector } from 'react-redux';
import { userSelector } from '../../../state/user/userSlice';
import PropTypes from 'prop-types';
import { ButtonSelector } from '../../buttonSelector/buttonSelector/ButtonSelector';
import { SingleAccordion } from '../../text/Accordion/SingleAccordion/SingleAccordion';
import { SnackbarContext } from '../../../util/SnackbarProvider';
import './ScheduleOlympiks.scss';
import { dataMSE } from '../../../assets/olympiksSchedule/data';
import location from '../../../assets/misc/location.png';
import { DarkModeContext } from '../../../util/DarkModeProvider';
import LilyDesign from '../../../assets/schedule/lily.svg';
import { getDisciplineOlympikSchedule } from '../../../pages/Profile/functions';
import axios from 'axios';

function getDaysSchedule() {
return Object.keys(dataMSE);
}

const ScheduleComponent = () => {
const today = new Date();
const options = { weekday: 'long', month: 'long', day: 'numeric' };
const todayString = today.toLocaleDateString('en-US', options).replace(',', '');
const { user } = useSelector(userSelector);
const [discipline, setDiscipline] = useState(user?.discipline);
const scheduleData = getDisciplineOlympikSchedule(discipline);
const { setSnackbar } = useContext(SnackbarContext);

let count = 0;
for (let day of getDaysSchedule()) {
if (day === todayString) {
break;
}
count++;
}
if (count >= Object.keys(scheduleData).length) {
count = 0;
}
const [selectedLocationIndex, setSelectedLocationIndex] = useState(count);
const [closeAll, setCloseAll] = useState(false);
const buttonList = Object.keys(scheduleData).map((location) => {
return { name: location };
});

return (
<div className="schedule-container">
<div className="schedule-left-container desktop-only">
<img src={LilyDesign} alt="Lily Design" className="lily-design" />
</div>
<div className="schedule-middle-container">
<div className="mobile-only">
<ButtonSelector
buttonList={buttonList}
activeIndex={selectedLocationIndex}
setActiveIndex={(index) => {
setSelectedLocationIndex(index);
setCloseAll(!closeAll);
}}
style={{
maxWidth: '250px',
marginTop: '0px',
marginBottom: '10px',
padding: '11px 15px',
minWidth: '110px',
}}
/>
</div>
<div className="schedule-container-dates desktop-only">
{Object.keys(scheduleData).map((day, index) => {
const [dayOfWeek, ...locationParts] = day.split(' - ');
const location = locationParts.join(' ');

return (
<div className="schedule-container-left" key={index}>
<div
style={{
top: index === 0 ? '42.5px' : 'unset',
height: index === Object.keys(scheduleData).length - 1 ? '42.5px' : '',
}}
></div>
<div
className={`schedule-container-dates-container ${
selectedLocationIndex === index
? 'schedule-container-dates-container-selected'
: ''
}`}
onClick={() => {
setSelectedLocationIndex(index);
setCloseAll(!closeAll);
}}
>
<h1>{dayOfWeek.toUpperCase()}</h1>
<h2>{location.toUpperCase()}</h2>
</div>
</div>
);
})}
</div>
</div>
<div className="schedule-right-container">
<div style={{ width: '100%' }}>
{scheduleData[Object.keys(scheduleData)[selectedLocationIndex]].map((activity, index) => {
return (
<ScheduleComponentAccordion key={index} scheduleDay={activity} closeAll={closeAll} />
);
})}
</div>
</div>
</div>
);
};

export const ScheduleComponentAccordion = ({
scheduleDay,
closeAll,
setScheduleData,
selectedLocationIndex,
}) => {
const [isOpen, setIsOpen] = useState(true);
const { darkMode } = useContext(DarkModeContext);
const { user } = useSelector(userSelector);
const [discipline, setDiscipline] = useState(user?.discipline);
const scheduleData = getDisciplineOlympikSchedule(discipline);
const { setSnackbar } = useContext(SnackbarContext);

useEffect(() => {
setIsOpen(false);
}, [closeAll]);

let startTime = scheduleDay['Start Time'];
if (startTime.includes(':00 a1/p1')) {
startTime = startTime.replace(':00 a1/p1', '');
startTime = convertTime(startTime);
}
let endTime = scheduleDay['End Time'];
if (endTime.includes(':00 a1/p1')) {
endTime = endTime.replace(':00 a1/p1', '');
endTime = convertTime(endTime);
}

const handleClick = async () => {
try {
// check if there are available spots
const availableSpots =
scheduleDay[`max${user.discipline}`] - scheduleDay[`current${user.discipline}`];
if (availableSpots <= 0) {
setSnackbar('No spots available for your discipline.');
return;
}

// snd to backend to sign up
const res = await axios.post('/frosh/olympiks-signup', {
eventId: scheduleDay._id,
eventName: scheduleDay.activityName,
userId: user._id,
discipline: user.discipline,
});
console.log('Signed up successfully:', res.data);
setSnackbar('Signed up successfully!');

const updatedSpots = scheduleDay[`current${user.discipline}`] + 1; // update the local state and increase the count in the database

// update state to reflect the new spot count
setScheduleData((prevData) => ({
...prevData,
[selectedLocationIndex]: prevData[selectedLocationIndex].map((activity) =>
activity._id === scheduleDay._id
? { ...activity, [`current${user.discipline}`]: updatedSpots }
: activity,
),
}));

// update db to have the new spot count
await axios.put(`/frosh/olympiks-update/${scheduleDay._id}`, {
[`current${user.discipline}`]: updatedSpots,
});
} catch (error) {
console.error('Could not sign up:', error);
setSnackbar('Sign up failed. Please try again.');
}
};

const remainingSpots =
scheduleDay[`max${user.discipline}`] - scheduleDay[`current${user.discipline}`];

return (
<>
<div className="schedule-accordion">
<SingleAccordion
className={`schedule-background-${scheduleDay['Color']}`}
header={
<div className="schedule-accordion-header-container">
<div className="schedule-accordion-header">
<h1>{scheduleDay['Activity Name'].toUpperCase()}</h1>
</div>
<h2>{startTime + ' - ' + endTime}</h2>
</div>
}
setIsOpen={setIsOpen}
isOpen={isOpen}
canOpen={scheduleDay['Activity Description'] !== undefined}
>
<p dangerouslySetInnerHTML={{ __html: scheduleDay['Activity Description'] }} />
{/* <h3 className="schedule-accordion-spots">{remainingSpots} SPOTS REMAINING!</h3>

<button className="button button-secondary" onClick={handleClick}>
Sign Up!
</button> */}
</SingleAccordion>
</div>
</>
);
};

ScheduleComponentAccordion.propTypes = {
scheduleDay: PropTypes.object,
closeAll: PropTypes.bool,
setScheduleData: PropTypes.func,
selectedLocationIndex: PropTypes.number,
};

export { ScheduleComponent };

function convertTime(time) {
time = time.toString().match(/^([01]\d|2[0-3])(:)([0-5]\d)?$/) || [time];

if (time.length > 1) {
time = time.slice(1);
time[5] = +time[0] < 12 ? '  AM' : '  PM';
time[0] = +time[0] % 12 || 12;
} else {
return time + ' AM';
}
return time.join('');
}
Loading
Loading