+
+
+
+
+
+
+
+ );
+};
+
+export default AdminView;
diff --git a/src/views/HousingLottery/index.jsx b/src/views/HousingLottery/index.jsx
new file mode 100644
index 0000000000..05d16a5229
--- /dev/null
+++ b/src/views/HousingLottery/index.jsx
@@ -0,0 +1,77 @@
+import GordonLimitedAvailability from 'components/GordonLimitedAvailability';
+import GordonOffline from 'components/GordonOffline';
+import GordonUnauthenticated from 'components/GordonUnauthenticated';
+import GordonLoader from 'components/Loader';
+import { useAuthGroups, useUser } from 'hooks';
+import useNetworkStatus from 'hooks/useNetworkStatus';
+import StudentView from './studentView';
+import AdminView from './adminView';
+
+//Imports for application period closed view
+import { Card, CardContent, Grid, Button, Link } from '@mui/material';
+// eslint-disable-next-line no-unused-vars
+import { useEffect, useState } from 'react'; // eslint disabled because it doesn't recognise type imports that ARE used in JSDoc comments
+import { AuthGroup } from 'services/auth';
+import styles from './HousingLottery.module.css';
+
+const HousingLottery = () => {
+ const [loading, setLoading] = useState(true);
+ const { profile, loading: loadingProfile } = useUser();
+ const [isUserStudent, setIsUserStudent] = useState(false);
+ const isHousingAdmin = useAuthGroups(AuthGroup.HousingAdmin);
+ const isOnline = useNetworkStatus();
+
+ useEffect(() => {
+ const loadPage = async () => {
+ setLoading(true);
+ try {
+ setIsUserStudent(profile.PersonType.includes('stu'));
+ } catch {
+ setIsUserStudent(false);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ if (profile) {
+ loadPage();
+ } else {
+ // Clear out component's person-specific state when authenticated becomes false
+ // (i.e. user logs out) so that it isn't preserved falsely for the next user
+ setIsUserStudent(false);
+ setLoading(false);
+ }
+ }, [profile]);
+
+ if (loading || loadingProfile) {
+ return ;
+ } else if (!profile) {
+ // The user is not logged in
+ return ;
+ } else if (isOnline) {
+ if (isHousingAdmin) {
+ return (
+
+
+
+ );
+ } else if (isUserStudent) {
+ return (
+
+
+
+ );
+ } else {
+ return (
+
+ );
+ }
+ } else {
+ return ;
+ }
+};
+
+export default HousingLottery;
diff --git a/src/views/HousingLottery/studentView/Agreements/index.jsx b/src/views/HousingLottery/studentView/Agreements/index.jsx
new file mode 100644
index 0000000000..fc58770803
--- /dev/null
+++ b/src/views/HousingLottery/studentView/Agreements/index.jsx
@@ -0,0 +1,154 @@
+import {
+ Card,
+ CardContent,
+ CardHeader,
+ Checkbox,
+ Divider,
+ FormControl,
+ FormControlLabel,
+ FormGroup,
+ FormHelperText,
+ FormLabel,
+} from '@mui/material/';
+import { Fragment, useEffect, useState } from 'react';
+import housing from 'services/housing';
+// @TODO CSSMODULES - outside directory
+import styles from '../../HousingLottery.module.css';
+
+/**
+ * Renders a card displaying the housing application instructions
+ *
+ * @param {Object} props The React component props
+ * @param {boolean | string} props.deleting Status of delete operation
+ * @param {Function} props.onChange Callback for change of the checkbox state
+ * @returns {JSX.Element} JSX Element for the instructions card
+ */
+const Agreements = ({ deleting, onChange }) => {
+ const [checkboxes, setCheckboxes] = useState([]);
+
+ const loadAgreements = async () => {
+ const currentYear = new Date().getFullYear();
+ const selectionDate = await housing.getApartmentSelectionDate();
+
+ const newCheckboxes = [
+ {
+ checked: false,
+ label: 'Each individual on the application has agreed to be on the application',
+ },
+ {
+ checked: false,
+ label:
+ 'We understand that if someone on this application has not agreed to be on the application, our application will be disqualified',
+ },
+ {
+ checked: false,
+ label: 'Each individual on this application appears ONLY on this application',
+ },
+ {
+ checked: false,
+ label:
+ "We understand that if an individual on this application also appears on another group's application, our application could be disqualified",
+ },
+ {
+ checked: false,
+ label: `Any individual on this application who has been on disciplinary probation at any point during the ${
+ currentYear - 1
+ }-${currentYear} academic year has been approved to apply by the Dean of Student Life or Assistant Dean of Student Life`,
+ },
+ {
+ checked: false,
+ label: `Each individual on this application intends to register as a full-time student by ${selectionDate}`,
+ },
+ {
+ checked: false,
+ label: `We understand that if any member of our application fails to register as a full-time student by ${selectionDate}, our application could be disqualified`,
+ },
+ {
+ checked: false,
+ label:
+ 'We have read and understand all of the information and guidelines listed in the Instructions section',
+ },
+ {
+ checked: false,
+ label:
+ 'We certify that all information provided on this application is accurate, to the best of our knowledge',
+ },
+ {
+ checked: false,
+ label:
+ 'We agree to host other students in our apartment during the winter break recess, in accordance with the policy outlined in the student handbook',
+ },
+ ];
+
+ setCheckboxes(newCheckboxes);
+ };
+
+ useEffect(() => loadAgreements(), []);
+
+ useEffect(() => deleting === 'success' && loadAgreements(), [deleting]);
+
+ const handleChange = (event, index) => {
+ setCheckboxes((prevCheckboxes) => {
+ let newCheckboxes = prevCheckboxes.map((prevCheckbox, j) =>
+ j === index ? { ...prevCheckbox, checked: event.target.checked } : prevCheckbox,
+ );
+ onChange(newCheckboxes.every((checkbox) => checkbox.checked));
+ return newCheckboxes;
+ });
+ };
+
+ const AgreementChecklistItem = ({ checked, index, label, onChange }) => (
+
+ onChange(event, index)}
+ name={'agreement-' + 1}
+ />
+ }
+ label={label}
+ key={index}
+ />
+
+
+ );
+
+ const error = checkboxes.some((checkbox) => !checkbox.checked);
+
+ return (
+
+
+
+
+ {error && (
+
+ Use the checkboxes next to each statement to indicate your group's understanding
+ and/or affirmative answer. Failure to complete this section will result in the
+ disqualification of the application.
+
+ )}
+
+
+ {checkboxes.map((checkbox, index) => (
+ handleChange(event, index)}
+ />
+ ))}
+
+
+ You must read and complete this section before you will be allowed to submit this
+ application
+
+
+
+
+ );
+};
+
+export default Agreements;
diff --git a/src/views/HousingLottery/studentView/Instructions/index.jsx b/src/views/HousingLottery/studentView/Instructions/index.jsx
new file mode 100644
index 0000000000..b9b96d17d1
--- /dev/null
+++ b/src/views/HousingLottery/studentView/Instructions/index.jsx
@@ -0,0 +1,126 @@
+import React from 'react';
+import {
+ Typography,
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Link,
+} from '@mui/material/';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import styles from '../../HousingLottery.module.css';
+
+const Instructions = () => {
+ return (
+
+
+
+ Instructions (As easy as 1, 2, 3!)
+
+
+ 1. Review FAQs
+
+
+ 2. Access questionnaire
+
+
+ 3. Complete questionnaire by providing name(s), email(s), and housing preferences by Friday, Apr. 21 at noon.
+
+
+
+ }
+ aria-controls="housing-lottery-faq-content"
+ id="housing-lottery-faq-header"
+ >
+ Housing Lottery FAQ
+
+
+
+ 1. Who should complete the questionnaire?
+ Anyone who has not yet secured housing for the Fall 2022 semester should participate in the lottery by completing the questionnaire.
+
+
+ 2. What happens if I do not complete the questionnaire by the deadline?
+ The questionnaire will remain open and can be completed after the deadline.
+ All late submissions will be placed on a housing waitlist and given a placement in July, as fall semester availability is much clearer by that time.
+
+
+ 3. How can I learn more about my housing options?
+ Please visit our residence halls
+ for information about our residence halls. Room cost information can be obtained by visiting
+ room costs .
+
+
+ 4. How will I learn my room assignment?
+ You will receive your room assignment via email no later than Friday, Apr.28.
+
+
+ 5. Do I need to be registered for classes to complete the questionnaire?
+ No, you should complete the questionnaire even if you are not yet registered for classes.
+ However, registration will be taken into consideration during the assigning process. See question #13.
+
+
+ 6.What if I complete the questionnaire, and then want to change my preferences?
+ Simply complete the questionnaire again. You can complete the questionnaire as many times as needed prior to the deadline.
+ Only your most recent submission will be taken into consideration.
+
+
+ 7. Do I need a roommate to complete the questionnaire?
+ No, you do not need a roommate to complete the questionnaire and receive a housing assignment. If you participate in the housing lottery without a roommate, you will be assigned a roommate. That assignment will be communicated via email.
+
+
+ 8. I have a roommate (or two or three). Should we all complete separate questionnaires?
+ No. If you are applying as a group of two, three, or four, you should only complete one questionnaire. Each group will receive one lottery number, regardless of group size. The person listed first on the questionnaire will serve as the contact person for your group.
+
+
+ 9. Can I request to room with an incoming student?
+ Yes, just include “(incoming)” after listing their name on the questionnaire.
+
+
+ 10. What about suite mates?
+ There is a space to indicate suite mate requests on the questionnaire.
+
+
+ 11. Are single rooms available during the lottery?
+ No, single rooms were assigned during the Special Accommodations process.
+
+
+ 12. Can I request a specific room during the lottery?
+ Yes, there is a space on the form to indicate a specific room request. We will do our best to accommodate requests, but there is no guarantee.
+
+
+ 13. How will lottery order be determined?
+ Lottery order will be determined class-by-class (based on current academic standing), using a randomizer.
+ For example, if 40 current seniors register for the lottery, the names of those 40 seniors will be inputted, and assigning will begin with the lowest number.
+ Current juniors, current sophomores, and current freshman will follow, in that order.
+ Important note: the aforementioned process assumes that a student is registered for Fall 2023 classes and is not on chapel probation.
+ All students, regardless of class, who are not registered for classes by the beginning of the assignment process will be grouped together, given lottery numbers, and assigned after the current freshman class.
+ If one member of a group is not registered for classes, the entire group will be considered as unregistered. Students on chapel probation will be included with the unregistered group.
+
+
+ 14. I am a sophomore, and my roommate is a senior. With which class will our questionnaire be reviewed?
+ In situations where group members have different academic standings, the group will be considered to have the standing of the member with the highest standing.
+ To use the example from the question, a sophomore + senior group would be considered to have senior standing.
+
+
+ 15. Where can I learn my class standing?
+ You can visit 360.gordon.edu to learn your class standing.
+
+
+ 16. What if I want housing other than what I receive?
+ It is our goal to accommodate every preference, but we are unfortunately unable to do so.
+ If you want to request a different location after receiving your assignment, you can email Housing on or after July 1 to request that.
+
+
+ 17. How can I receive confirmation that my questionnaire has been received?
+ If you would like confirmation that your questionnaire has been received, you will need to select the “Send me an email receipt of my responses” box at the bottom of the questionnaire.
+ No other confirmation will be provided.
+
+
+
+
+ );
+};
+
+export default Instructions;
diff --git a/src/views/HousingLottery/studentView/PreferenceBox/index.jsx b/src/views/HousingLottery/studentView/PreferenceBox/index.jsx
new file mode 100644
index 0000000000..93bd1fe9ea
--- /dev/null
+++ b/src/views/HousingLottery/studentView/PreferenceBox/index.jsx
@@ -0,0 +1,115 @@
+import { useEffect, useState } from 'react';
+import {
+ Table,
+ TableBody,
+ TableContainer,
+ Typography,
+ FormControl,
+ FormControlLabel,
+ Input,
+ Button,
+ RadioGroup,
+ Card,
+ CardContent,
+ CardHeader,
+ Radio,
+ Grid,
+} from '@mui/material';
+import housingService from 'services/housing';
+import housing from 'services/housing';
+import styles from '../../HousingLottery.module.css';
+
+const Preference = ({ onPreferenceChange }) => {
+ const [preferences, setPreferences] = useState(['', '']); // Store preferences as an array
+ const [morningOrNight, setMorningOrNight] = useState(''); // Store the selected morning or night
+ const [loudOrQuiet, setLoudOrQuiet] = useState(''); // Store the selected loud or quiet
+
+ useEffect(() => {
+ // Check for stored preferences in localStorage
+ const storedPreferences = localStorage.getItem('userPreferences');
+ if (storedPreferences) {
+ const { morningOrNight, loudOrQuiet } = JSON.parse(storedPreferences);
+ setMorningOrNight(morningOrNight || '');
+ setLoudOrQuiet(loudOrQuiet || '');
+ }
+ }, []);
+
+ const handleMorningOrNightChange = (event) => {
+ const newMorningOrNight = event.target.value;
+ let newList = [...preferences];
+ newList[0] = newMorningOrNight;
+ setMorningOrNight(newMorningOrNight);
+ setPreferences(newList);
+ onPreferenceChange(newList);
+ };
+
+ const handleLoudOrQuietChange = (event) => {
+ const newLoudOrQuiet = event.target.value;
+ let newList = [...preferences];
+ newList[1] = newLoudOrQuiet;
+ setLoudOrQuiet(newLoudOrQuiet);
+ setPreferences(newList);
+ onPreferenceChange(newList);
+ };
+
+ useEffect(() => {
+ // Save preferences to local storage
+ const storedPreferences = JSON.stringify({ morningOrNight, loudOrQuiet });
+ localStorage.setItem('userPreferences', storedPreferences);
+ }, [morningOrNight, loudOrQuiet]);
+
+ useEffect(() => {
+ // Check if both morningOrNight and loudOrQuiet are empty, clear localStorage
+ if (!morningOrNight && !loudOrQuiet) {
+ localStorage.removeItem('userPreferences');
+ }
+ }, [morningOrNight, loudOrQuiet]);
+
+ useEffect(() => {
+ // Clear selected radio buttons when the page is refreshed
+ if (!localStorage.getItem('userPreferences')) {
+ setMorningOrNight('');
+ setLoudOrQuiet('');
+ }
+ }, []);
+
+ return (
+
+
+
+
+
+