diff --git a/index.html b/index.html index 114de7b..c4cb2f7 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + AISS Schedule Planner diff --git a/public/MoreInfoEvent.svg b/public/MoreInfoEvent.svg new file mode 100644 index 0000000..bbf9f5f --- /dev/null +++ b/public/MoreInfoEvent.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/MoreInfoTable.svg b/public/MoreInfoTable.svg new file mode 100644 index 0000000..5e81160 --- /dev/null +++ b/public/MoreInfoTable.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm.jsx b/src/components/AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm.jsx index cc104e1..171dfbe 100644 --- a/src/components/AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm.jsx +++ b/src/components/AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm.jsx @@ -34,25 +34,27 @@ import PlannedEvent, { convertTimeToMinutes } from '../Planner/PlannedEvent'; import RemoveTimelineEventModal from '../Planner/RemoveTimelineEventModal'; const schema = yup.object({ - startTime: yup.string().required('Start time is required').test('is-after-7-am', 'Start time cannot be earlier than 7 AM', function(startTime) { - return startTime && startTime >= "07:00"; + startTime: yup.string().required('Start time is required').test('is-after-7-am', 'Start time cannot be earlier than 7 AM', function(startTime) { + return startTime && startTime >= "07:00"; + }), + endTime: yup.string() + .required('End time is required') + .test('is-after', 'End time must be after start time', function(endTime) { + const startTime = this.parent.startTime; + return startTime && endTime && startTime < endTime; + }).test('is-before-11-pm', 'End time must be earlier than 11 PM', function(endTime) { + return endTime && endTime <= "23:00"; }), - endTime: yup.string() - .required('End time is required') - .test('is-after', 'End time must be after start time', function(endTime) { - const startTime = this.parent.startTime; - return startTime && endTime && startTime < endTime; - }).test('is-before-11-pm', 'End time must be earlier than 11 PM', function(endTime) { - return endTime && endTime <= "23:00"; - }), - host: yup.string().max(50, 'Host exceeds 50 character limit').default('').nullable(), - title: yup.string().required('Title Required').max(50, 'Title exceeds 50 character limit'), - description: yup - .string() - .max(256, 'Description exceeds 256 character limit') - .default('') - .nullable(), - tentative: yup.boolean() + subject: yup.array().min(1, 'Please select at least one subject').required(), + eventType: yup.array().min(1, 'Please select at least one event type').required(), + host: yup.string().max(50, 'Host exceeds 50 character limit').default('').nullable(), + title: yup.string().required('Title Required').max(50, 'Title exceeds 50 character limit'), + description: yup + .string() + .max(256, 'Description exceeds 256 character limit') + .default('') + .nullable(), + tentative: yup.boolean() }); const AddEventToPublishedScheduleForm = ({ closeForm }) => { @@ -104,7 +106,14 @@ const AddEventToPublishedScheduleForm = ({ closeForm }) => { } else { setPlannedEvents(plannedEvents.filter(e => e.id != -1)); } - }, [formData]) + }, [formData]); + + useEffect(() => { + setValue('season', filterValues.season); + setValue('year', filterValues.year); + setValue('subject', filterValues.subject); + setValue('eventType', filterValues.eventType); + }, [filterValues]); const toast = useToast(); const { @@ -360,8 +369,8 @@ const AddEventToPublishedScheduleForm = ({ closeForm }) => { {/* SUBJECT */} - - Subject + + Subject * { badgeColor="#E8D7FF" width="28vw" /> + + {errors.subject && errors.subject.message} + {/* EVENT TYPE */} - - Event Type + + Event Type * { badgeColor="#CFDCFF" width="28vw" /> + + {errors.eventType && errors.eventType.message} + diff --git a/src/components/Catalog/CreateEventForm/CreateEventForm.jsx b/src/components/Catalog/CreateEventForm/CreateEventForm.jsx index 7c6f68f..4a2ad9f 100644 --- a/src/components/Catalog/CreateEventForm/CreateEventForm.jsx +++ b/src/components/Catalog/CreateEventForm/CreateEventForm.jsx @@ -23,6 +23,7 @@ import { } from '../../Catalog/SearchFilter/filterOptions'; import useSearchFilters from '../../Catalog/SearchFilter/useSearchFilters'; import Dropdown from '../../Dropdown/Dropdown'; +import { useEffect } from 'react'; const schema = yup.object({ host: yup.string().max(50, 'Host exceeds 50 character limit').default('').nullable(), @@ -32,11 +33,14 @@ const schema = yup.object({ .max(256, 'Description exceeds 256 character limit') .default('') .nullable(), + subject: yup.array().min(1, 'Please select at least one subject').required(), + eventType: yup.array().min(1, 'Please select at least one event type').required(), }); const CreateEventForm = ({ eventData, setDataShouldRevalidate, closeModal }) => { const toast = useToast(); const { + setValue, handleSubmit, register, reset, @@ -48,6 +52,7 @@ const CreateEventForm = ({ eventData, setDataShouldRevalidate, closeModal }) => const submitData = async data => { const { host, title, description } = data; + console.log(data); const season = filterValues.season; const eventType = filterValues.eventType; const year = filterValues.year; @@ -111,6 +116,13 @@ const CreateEventForm = ({ eventData, setDataShouldRevalidate, closeModal }) => const { filters, filterValues } = useSearchFilters(); const [seasonFilter, yearFilter, subjectFilter, eventFilter] = filters; + useEffect(() => { + setValue('season', filterValues.season); + setValue('year', filterValues.year); + setValue('subject', filterValues.subject); + setValue('eventType', filterValues.eventType); + }, [filterValues]); + return ( @@ -170,8 +182,8 @@ const CreateEventForm = ({ eventData, setDataShouldRevalidate, closeModal }) => {/* SUBJECT */} - - Subject + + Subject * defaults={eventData && eventData.subject} badgeColor="#E8D7FF" /> + + {errors.subject && errors.subject.message} + {/* EVENT TYPE */} - - Event Type + + Event Type * defaults={eventData && eventData.eventType} badgeColor="#CFDCFF" /> + + {errors.eventType && errors.eventType.message} + diff --git a/src/components/Catalog/SearchFilter/filterOptions.js b/src/components/Catalog/SearchFilter/filterOptions.js index f32aefd..3f9e709 100644 --- a/src/components/Catalog/SearchFilter/filterOptions.js +++ b/src/components/Catalog/SearchFilter/filterOptions.js @@ -14,6 +14,9 @@ const subjectOptions = [ { value: 'engineering', name: 'Engineering' }, { value: 'math', name: 'Math' }, { value: 'college readiness', name: 'College Readiness' }, + { value: 'soft skills', name: 'Soft Skills' }, + { value: 'writing', name: 'Writing' }, + { value: 'other', name: 'Other' }, ]; const eventOptions = [ { value: 'guest speaker', name: 'Guest Speaker' }, diff --git a/src/components/Events/PublishedScheduleTable.jsx b/src/components/Events/PublishedScheduleTable.jsx index a8c0a29..83426d0 100644 --- a/src/components/Events/PublishedScheduleTable.jsx +++ b/src/components/Events/PublishedScheduleTable.jsx @@ -1,44 +1,10 @@ -import { NPOBackend } from '../../utils/auth_utils.js'; import Events from './Events.jsx'; import EventInfo from './EventInfo.jsx'; import PropTypes from 'prop-types'; -import { useEffect, useState } from 'react'; -import { Table, Thead, Tbody, Tr, Th, Td, TableContainer, Box, IconButton, useDisclosure } from '@chakra-ui/react'; -import AddDayModal from '../../pages/PublishedSchedule/AddDayModal.jsx' -import StatModal from '../../pages/PublishedSchedule/StatisticsModal.jsx'; -import { AddIcon } from '@chakra-ui/icons'; -import { IoStatsChart } from "react-icons/io5"; -import { useAuthContext } from '../../common/AuthContext.jsx'; -import AUTH_ROLES from '../../utils/auth_config.js'; -const { ADMIN_ROLE } = AUTH_ROLES.AUTH_ROLES; +import { Table, Thead, Tbody, Tr, Th, Td, TableContainer } from '@chakra-ui/react'; -const PublishedScheduleTable = ({ season, allSeasons, dataShouldRevalidate, setShouldDataRevalidate}) => { - const {currentUser} = useAuthContext(); - const [eventsInDay, setEventsInDay] = useState([]); - const seasonType = season.split(' ')[0]; - const seasonYear = season.split(' ')[1]; - const { isOpen: isOpenDay, onOpen: onOpenDay, onClose: onCloseDay } = useDisclosure(); - const { isOpen: isOpenStats, onOpen: onOpenStats, onClose: onCloseStats } = useDisclosure(); - - const renderTable = async () => { - const { data } = await NPOBackend.get( - `/published-schedule/season?season=${seasonType}&year=${seasonYear}`, - ); - setEventsInDay(data); - }; - - useEffect(() => { - renderTable(); - }, [seasonType, seasonYear]); - - useEffect(() => { - if (dataShouldRevalidate) { - renderTable(); - setShouldDataRevalidate(false); - } - }, [dataShouldRevalidate, renderTable]) - +const PublishedScheduleTable = ({ eventData, setShouldDataRevalidate}) => { const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; const formatDate = date => { @@ -57,89 +23,42 @@ const PublishedScheduleTable = ({ season, allSeasons, dataShouldRevalidate, setS } return ( - - {currentUser.type === ADMIN_ROLE && - <> - } - > - Stats - - - } - > - Create - - - } - - - { season && allSeasons && - - } - - - - - - - + +
InfoEvent Schedule
+ + + + + + + + {eventData.map(item => ( + + + - - - {eventsInDay.map(item => ( - - - - - ))} - -
InfoEvent Schedule
+ + + +
- - - -
-
-
+ ))} + + + ); }; PublishedScheduleTable.propTypes = { - season: PropTypes.string.isRequired, - allSeasons: PropTypes.array.isRequired, - dataShouldRevalidate: PropTypes.bool.isRequired, + eventData: PropTypes.array.isRequired, setShouldDataRevalidate: PropTypes.func.isRequired, }; diff --git a/src/components/Notifications/Notifications.jsx b/src/components/Notifications/Notifications.jsx index 88d493c..655dec5 100644 --- a/src/components/Notifications/Notifications.jsx +++ b/src/components/Notifications/Notifications.jsx @@ -1,5 +1,5 @@ import { Table, Tbody, Tr, Td, TableContainer } from '@chakra-ui/react'; -import { useEffect, useState, useMemo } from 'react'; +import { useEffect, useState, useMemo, useRef } from 'react'; import { NPOBackend } from '../../utils/auth_utils'; import { AccountNotificationBlock, EventNotificationBlock } from './NotificationElement'; import AccountNotification from './AccountNotification'; @@ -21,13 +21,13 @@ const Notifications = () => { const [approveAfterTimer, setApproveAfterTimer] = useState(false); const [declineAfterTimer, setDeclineAfterTimer] = useState(false); const [idToRemove, setidToRemove] = useState(undefined); - let timeoutId = undefined; + const timeoutIds = useRef({}); const today = useMemo(() => new Date(), []); - const undoChanges = () => { - clearTimeout(timeoutId); - timeoutId = undefined; + const undoChanges = id => { + clearTimeout(timeoutIds.current[id]); + delete timeoutIds.current[id]; }; const approveAccount = async id => { @@ -41,7 +41,7 @@ const Notifications = () => { setidToRemove(id); setApproveAfterTimer(true); }, 5000); // 5 second delay - timeoutId = timeId; + timeoutIds.current[id] = timeId; }; const declineAccount = async id => { @@ -55,7 +55,7 @@ const Notifications = () => { setidToRemove(id); setDeclineAfterTimer(true); }, 5000); // 5 second delay - timeoutId = timeId; + timeoutIds.current[id] = timeId; }; const removeNotificationEntry = key => { @@ -98,7 +98,7 @@ const Notifications = () => { async () => { await declineAccount(id); }, - undoChanges, + () => undoChanges(id), ); accountsMap.set(formattedDateString, notificationBlock); }); diff --git a/src/components/Planner/PlannerEvents/PlannerEvents.jsx b/src/components/Planner/PlannerEvents/PlannerEvents.jsx index 2a83ab1..45b4c4d 100644 --- a/src/components/Planner/PlannerEvents/PlannerEvents.jsx +++ b/src/components/Planner/PlannerEvents/PlannerEvents.jsx @@ -1,57 +1,22 @@ -import { useState, useEffect, useContext } from 'react'; +import { useState, useContext } from 'react'; import s from '../PlannerLayout.module.css'; -import { Text, Button, Heading, Box, IconButton, HStack, Flex, useDisclosure } from '@chakra-ui/react'; -import { AddIcon, EditIcon } from '@chakra-ui/icons'; +import { Text, Button, Heading, Box, Flex, useDisclosure } from '@chakra-ui/react'; +import { AddIcon } from '@chakra-ui/icons'; import Catalog from '../../../pages/Catalog/Catalog'; import PropTypes from 'prop-types'; import { PlannerContext } from '../PlannerContext'; -import { NPOBackend } from '../../../utils/auth_utils'; -import AddDayModal from '../../../pages/PublishedSchedule/AddDayModal'; import AddEventToPublishedScheduleForm from '../../AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm'; import EmptyDayModal from '../EmptyDayModal'; const PlannerEvents = ({ onClose }) => { const [isAddingEvent, setIsAddingEvent] = useState(false); - const [dateHeader, setDateHeader] = useState(''); - const [dayData, setDayData] = useState({}); - const { isOpen: isOpenDay, onOpen: onOpenDay, onClose: onCloseDay } = useDisclosure(); const { isOpen: isOpenEmptyDay, onOpen: onOpenEmptyDay, onClose: onCloseEmptyDay } = useDisclosure(); - const { plannedEventsContext, dayId, editContext, currEventContext } = useContext(PlannerContext); + const { plannedEventsContext, editContext, currEventContext } = useContext(PlannerContext); const [isEdit, setIsEdit] = editContext; const setCurrEvent = currEventContext[1]; // fix? - const [dataShouldRevalidate, setShouldDataRevalidate] = useState(false); const plannedEvents = plannedEventsContext[0]; - const getUTCDate = (eventDate) => { - const utcDate = new Date(eventDate); - return new Date(utcDate.getTime() + utcDate.getTimezoneOffset() * 60000); - } - - const getDayData = async () => { - try { - const response = await NPOBackend.get(`/day/${dayId}`); - const responseData = response.data[0]; - const [datePart] = responseData.eventDate.split('T'); - const dateObj = getUTCDate(responseData.eventDate); - setDateHeader(dateObj.toLocaleDateString({ year: 'numeric', month: 'short', day: '2-digit' })); - setDayData({id: responseData.id, eventDate: datePart, location: responseData.location, details: responseData.notes}); - } catch (error) { - console.error(error); - } - }; - - useEffect(() => { - getDayData(); - }, [dayId]); - - useEffect(() => { - if (dataShouldRevalidate) { - getDayData(); - setShouldDataRevalidate(false); - } - }, [dataShouldRevalidate]) - const handleCreateNewEvent = () => { setCurrEvent({}); togglePSForm(); @@ -82,17 +47,6 @@ const PlannerEvents = ({ onClose }) => { }