diff --git a/src/components/AddDayForm/AddDayForm.jsx b/src/components/AddDayForm/AddDayForm.jsx index 1313b4a..c9d88f4 100644 --- a/src/components/AddDayForm/AddDayForm.jsx +++ b/src/components/AddDayForm/AddDayForm.jsx @@ -19,10 +19,10 @@ import { NPOBackend } from '../../utils/auth_utils'; const schema = yup.object({ date: yup.date().nullable().transform((curr, orig) => orig === '' ? null : curr).required('Date required'), - location: yup.string().required('Location required').max(50, 'Location exceeds 50 character limit'), + location: yup.string().required('Location required').max(100, 'Location exceeds 100 character limit'), details: yup .string() - .max(50, 'Details exceeds 50 character limit'), + .max(200, 'Details exceeds 200 character limit'), }); const AddDayForm = ({ onClose, onOpen, setDayId, dayData, setShouldDataRevalidate }) => { diff --git a/src/components/AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm.jsx b/src/components/AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm.jsx index ea6398d..5e8d3b7 100644 --- a/src/components/AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm.jsx +++ b/src/components/AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm.jsx @@ -12,7 +12,9 @@ import { Heading, Flex, Text, - Stack + HStack, + useDisclosure, + Spacer } from '@chakra-ui/react'; import { yupResolver } from '@hookform/resolvers/yup'; import { useForm } from 'react-hook-form'; @@ -30,14 +32,19 @@ import Dropdown from '../Dropdown/Dropdown'; import PropTypes from 'prop-types'; import { PlannerContext } from '../Planner/PlannerContext'; import PlannedEvent, { convertTimeToMinutes } from '../Planner/PlannedEvent'; +import RemoveTimelineEventModal from '../Planner/RemoveTimelineEventModal'; const schema = yup.object({ - startTime: yup.string().required('Start time is required'), + 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"; }), 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'), @@ -58,6 +65,7 @@ const AddEventToPublishedScheduleForm = ({ closeForm }) => { const [seasonFilter, yearFilter, subjectFilter, eventFilter] = filters; const [checkboxVal, setCheckboxVal] = useState(undefined); const [formData, setFormData] = useState({...eventData}); + const { isOpen: isRemoveOpen, onOpen: onRemoveOpen, onClose: onRemoveClose } = useDisclosure(); useEffect(() => { if (Object.keys(eventData).length === 0) { @@ -85,7 +93,7 @@ const AddEventToPublishedScheduleForm = ({ closeForm }) => { }, [eventData]); useEffect(() => { - if (formData.startTime && formData.endTime && formData.startTime < formData.endTime) { + if (formData.startTime && formData.endTime && formData.startTime < formData.endTime && formData.startTime >= "07:00" && formData.endTime <= "23:00") { // if (isEdit) { // // setPlannedEvents([...plannedEvents.filter(e => e.id != -1 && e.id != eventData.id)]); // } @@ -264,7 +272,7 @@ const AddEventToPublishedScheduleForm = ({ closeForm }) => { return ( -
+ Event Information @@ -430,11 +438,16 @@ const AddEventToPublishedScheduleForm = ({ closeForm }) => { - - - -
+ + {isEdit && + + } + + + + +
); }; diff --git a/src/components/Catalog/DeleteEventModal/DeleteEventModal.jsx b/src/components/Catalog/DeleteEventModal/DeleteEventModal.jsx index 056018b..45b7534 100644 --- a/src/components/Catalog/DeleteEventModal/DeleteEventModal.jsx +++ b/src/components/Catalog/DeleteEventModal/DeleteEventModal.jsx @@ -23,7 +23,7 @@ const DeleteEventModal = ({ isOpen, onClose, deleteItemId, setDataShouldRevalida }; return ( - + Remove Event? diff --git a/src/components/Events/DailyEvent.jsx b/src/components/Events/DailyEvent.jsx index d5e60e1..436dc07 100644 --- a/src/components/Events/DailyEvent.jsx +++ b/src/components/Events/DailyEvent.jsx @@ -1,73 +1,70 @@ import PropTypes from 'prop-types'; import { Box, Flex, Text, Grid, Button, Spacer, Badge, VStack } from '@chakra-ui/react'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { NPOBackend } from '../../utils/auth_utils.js'; import AUTH_ROLES from '../../utils/auth_config.js'; import { useAuthContext } from '../../common/AuthContext.jsx'; const { USER_ROLE } = AUTH_ROLES.AUTH_ROLES; -const DailyEvent = ({ id, startTime, endTime, eventTitle, confirmed, description, eventId }) => { - const [confirmEvent, setConfirmEvent] = useState(confirmed); +const DailyEvent = ({ event }) => { + const [confirmEvent, setConfirmEvent] = useState(event.confirmed); const [cohort, setCohort] = useState(undefined); const [cohortBadgeColor, setCohortBadgeColor] = useState(undefined); const [cohortTextColor, setCohortTextColor] = useState(undefined); const [cohortBorderColor, setCohortBorderColor] = useState(undefined); const [cohortBorder, setCohortBorder] = useState(undefined); const {currentUser} = useAuthContext(); + const [eventData, setEventData] = useState(event); - let border_color = '#2B93D1'; - let background_color = '#F7FAFC'; - if (!confirmEvent) { - border_color = '#F69052'; - background_color = '#FEF1DC'; - } + useEffect(() => { + setEventData(event); - const getCohort = async () => { - if (eventId) { - try { - const { data } = await NPOBackend.get(`/catalog/${eventId}`); - let year = data[0].year; - if( year.length == 2) - { - setCohort('Both'); - setCohortBadgeColor("#FFFFFF"); - setCohortTextColor("#4A5568"); - setCohortBorderColor("#4A5568"); - setCohortBorder("outline"); - } - else if(year[0] == 'junior') - { - setCohort('Junior'); - setCohortBadgeColor("#CBD5E0"); - setCohortTextColor("#171923"); - setCohortBorderColor("#CBD5E0"); - setCohortBorder("simple"); - } - else { - setCohort('Senior'); - setCohortBadgeColor("#4A5568"); - setCohortTextColor("#FFFFFF"); - setCohortBorderColor("#4A5568"); - setCohortBorder("simple"); - } - } catch (error) { - console.error('Error fetching data:', error); + if (event.year) { + if( event.year.length == 2) + { + setCohort('Both'); + setCohortBadgeColor("#FFFFFF"); + setCohortTextColor("#4A5568"); + setCohortBorderColor("#4A5568"); + setCohortBorder("outline"); + } + else if(event.year[0] == 'junior') + { + setCohort('Junior'); + setCohortBadgeColor("#CBD5E0"); + setCohortTextColor("#171923"); + setCohortBorderColor("#CBD5E0"); + setCohortBorder("simple"); + } + else if(event.year[0] == 'senior') + { + setCohort('Senior'); + setCohortBadgeColor("#4A5568"); + setCohortTextColor("#FFFFFF"); + setCohortBorderColor("#4A5568"); + setCohortBorder("simple"); } } - } + }, [event]); - getCohort(); + const formatDate = (date) => { + let time = date.split(":"); + let hours = time[0]; + let mins = time[1]; + let ampm = hours >= 12 ? 'pm' : 'am'; + hours = hours % 12; + hours = hours ? hours : 12; + return `${hours}:${mins} ${ampm}`; + } const handleConfirm = async () => { const date = new Date(); try { - let response; - response = await NPOBackend.put(`/published-schedule/${id}`, { + await NPOBackend.put(`/published-schedule/${eventData.id}`, { confirmed: true, confirmedOn: date, } ); - console.log(response); setConfirmEvent(true); } catch (error) { console.error('Error fetching data:', error); @@ -76,28 +73,28 @@ const DailyEvent = ({ id, startTime, endTime, eventTitle, confirmed, description return ( - {startTime} - {endTime} + {formatDate(eventData.startTime)} - {formatDate(eventData.endTime)} - {eventTitle} - {description} + {eventData.title} + {eventData.description} - - {!confirmEvent && ( - - -
-
- -
-
- )} + {!confirmEvent && ( + + +
+
+ +
+
+ )}
@@ -138,13 +135,7 @@ const DailyEvent = ({ id, startTime, endTime, eventTitle, confirmed, description }; DailyEvent.propTypes = { - id: PropTypes.number.isRequired, - startTime: PropTypes.string.isRequired, - endTime: PropTypes.string.isRequired, - eventTitle: PropTypes.string.isRequired, - description: PropTypes.string, - confirmed: PropTypes.bool.isRequired, - eventId: PropTypes.number.isRequired, + event: PropTypes.object.isRequired, }; export default DailyEvent; diff --git a/src/components/Events/Events.jsx b/src/components/Events/Events.jsx index f1df953..e4975b5 100644 --- a/src/components/Events/Events.jsx +++ b/src/components/Events/Events.jsx @@ -2,21 +2,8 @@ import DailyEvent from './DailyEvent.jsx'; import PropTypes from 'prop-types'; import { Grid } from '@chakra-ui/react'; - const Events = ({ eventData }) => { - // console.log("Event Data", eventData); - - const formatDate = (date) => { - let time = date.split(":"); - let hours = time[0]; - let mins = time[1]; - let ampm = hours >= 12 ? 'pm' : 'am'; - hours = hours % 12; - hours = hours ? hours : 12; - return `${hours}:${mins} ${ampm}`; - } - let maxId = eventData.reduce((maxVal, event) => Math.max(maxVal, event.id), -Infinity)+1; eventData.sort((a, b) => { @@ -49,7 +36,6 @@ const Events = ({ eventData }) => { const timeDiff = convertTimeToMin(startHour, startMin) - convertTimeToMin(endHour, endMin); if (currEnd < nextStart && timeDiff >= 5) { - console.log("break"); eventDataWithBreaks.push({ id: maxId, startTime: currentEvent.endTime, @@ -63,19 +49,13 @@ const Events = ({ eventData }) => { if (eventData.length > 1) { eventDataWithBreaks.push(eventData[eventData.length - 1]); } - // console.log(eventDataWithBreaks); + return ( {eventDataWithBreaks.map(item => ( ))} diff --git a/src/components/Events/PublishedScheduleTable.jsx b/src/components/Events/PublishedScheduleTable.jsx index f6ec432..bc82adf 100644 --- a/src/components/Events/PublishedScheduleTable.jsx +++ b/src/components/Events/PublishedScheduleTable.jsx @@ -38,7 +38,7 @@ const PublishedScheduleTable = ({ season, allSeasons }) => { renderTable(); setShouldDataRevalidate(false); } - }, [dataShouldRevalidate]) + }, [dataShouldRevalidate, renderTable]) const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; diff --git a/src/components/Navbar/Navbar.jsx b/src/components/Navbar/Navbar.jsx index d53032a..b14f347 100644 --- a/src/components/Navbar/Navbar.jsx +++ b/src/components/Navbar/Navbar.jsx @@ -34,7 +34,7 @@ const Navbar = ({ hasLoaded, isAdmin }) => { ); }; - if (!hasLoaded) { + if (!hasLoaded || location.pathname == '/login') { return null; } diff --git a/src/components/Planner/EmptyDayModal.jsx b/src/components/Planner/EmptyDayModal.jsx new file mode 100644 index 0000000..60a48f4 --- /dev/null +++ b/src/components/Planner/EmptyDayModal.jsx @@ -0,0 +1,51 @@ +import PropTypes from 'prop-types'; +import { + Button, + Modal, + ModalBody, + ModalContent, + ModalOverlay, + ModalHeader, + ModalCloseButton, + ModalFooter, +} from '@chakra-ui/react'; +import { NPOBackend } from '../../utils/auth_utils'; +import { useContext } from 'react'; +import { PlannerContext } from './PlannerContext'; + +const EmptyDayModal = ({ isOpen, onClose, onClosePlanner }) => { + const { dayId } = useContext(PlannerContext); + + const handleConfirmDelete = async () => { + try { + await NPOBackend.delete(`/day/${dayId}`); + onClose(); + onClosePlanner(); + } catch (error) { + console.error(error); + } + }; + + return ( + + + + Warning + + No events have been added, this day will not be saved. + + + + + + + ); +}; + +EmptyDayModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + onClosePlanner: PropTypes.func.isRequired, +}; + +export default EmptyDayModal; diff --git a/src/components/Planner/PlannedEvent.js b/src/components/Planner/PlannedEvent.js index e69d2be..966307b 100644 --- a/src/components/Planner/PlannedEvent.js +++ b/src/components/Planner/PlannedEvent.js @@ -1,7 +1,7 @@ import { MINUTES_PER_HOUR } from './chrono'; export default class PlannedEvent { - static EARLIEST_HOUR = 6; // 6:00 AM (6:00 military) + static EARLIEST_HOUR = 7; // 7:00 AM (7:00 military) static LATEST_HOUR = 22; // 10:00 PM (22:00 military time) id; // string name; // string diff --git a/src/components/Planner/PlannerEvents/PlannerEvents.jsx b/src/components/Planner/PlannerEvents/PlannerEvents.jsx index 83e5210..59c5da3 100644 --- a/src/components/Planner/PlannerEvents/PlannerEvents.jsx +++ b/src/components/Planner/PlannerEvents/PlannerEvents.jsx @@ -6,8 +6,9 @@ import Catalog from '../../../pages/Catalog/Catalog'; import PropTypes from 'prop-types'; import { PlannerContext } from '../PlannerContext'; import { NPOBackend } from '../../../utils/auth_utils'; -import AddEventToPublishedScheduleForm from '../../AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm'; import AddDayModal from '../../../pages/PublishedSchedule/AddDayModal'; +import AddEventToPublishedScheduleForm from '../../AddEventToPublishedScheduleForm/AddEventToPublishedScheduleForm'; +import EmptyDayModal from '../EmptyDayModal'; const PlannerEvents = ({ onClose }) => { const [isAddingEvent, setIsAddingEvent] = useState(false); @@ -15,6 +16,7 @@ const PlannerEvents = ({ onClose }) => { 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 [isEdit, setIsEdit] = editContext; @@ -66,23 +68,19 @@ const PlannerEvents = ({ onClose }) => { const closeModal = async () => { // delete day if empty if (plannedEvents.length === 0) { - try { - console.log('deleting day!!') - await NPOBackend.delete(`/day/${dayId}`); - } catch (error) { - console.error(error); - } + onOpenEmptyDay(); + } else { + onClose(); } - onClose(); } return (
{/* {overlayIsVisible && } */}
- + {(isAddingEvent || isEdit) && + + } +
); diff --git a/src/components/Planner/PlannerLayout.module.css b/src/components/Planner/PlannerLayout.module.css index 0799c47..8b2698e 100644 --- a/src/components/Planner/PlannerLayout.module.css +++ b/src/components/Planner/PlannerLayout.module.css @@ -52,7 +52,7 @@ } .grid-hour-container { - min-height: 64px; + min-height: 128px; position: relative; border-top: 2px solid var(--chakra-colors-blackAlpha-400); } diff --git a/src/components/Planner/PlannerTimeline/PlannerTimeline.jsx b/src/components/Planner/PlannerTimeline/PlannerTimeline.jsx index 6a4f659..47a982e 100644 --- a/src/components/Planner/PlannerTimeline/PlannerTimeline.jsx +++ b/src/components/Planner/PlannerTimeline/PlannerTimeline.jsx @@ -1,24 +1,18 @@ import s from '../PlannerLayout.module.css'; -import { useMemo, useContext, useEffect, useState } from 'react'; +import { useMemo, useContext, useEffect } from 'react'; import { generateTimestamps, minutesInFormattedTime } from '../chrono'; -import { Badge, Text, Box, IconButton, HStack, useDisclosure } from '@chakra-ui/react'; -import { DeleteIcon, EditIcon } from '@chakra-ui/icons'; +import { Badge, Text, Box, HStack } from '@chakra-ui/react'; import { PlannerContext } from '../PlannerContext'; import PlannedEvent, { convertTimeToMinutes } from '../PlannedEvent'; import { NPOBackend } from '../../../utils/auth_utils'; -// import PropTypes from 'prop-types'; -import RemoveTimelineEventModal from '../RemoveTimelineEventModal'; + const PlannerTimeline = () => { const { plannedEventsContext, dayId, currEventContext, editContext } = useContext(PlannerContext); - const { isOpen: isRemoveOpen, onOpen: onRemoveOpen, onClose: onRemoveClose } = useDisclosure(); const [plannedEvents, setPlannedEvents] = plannedEventsContext; const [eventData, setCurrEvent] = currEventContext; const [isEdit, setIsEdit] = editContext; - const [eventHover, setEventHover] = useState(-2); - const [deleteItemId, setDeleteItemId] = useState(-1); - //const [addedEvents, setAddedEvents] = useState([]); const addedEvents = []; @@ -113,9 +107,14 @@ const PlannerTimeline = () => { const formattedEndTime = minutesInFormattedTime(endTime); let border_color = '#2B93D1'; + let text_color = '#1A202C'; + let hover_text_color = '#171923'; + let hover_tentative_text_color = '#652B19'; let background_color = '#BEE3F8'; - let border_style = 'none none none solid'; - const border_width = '2px 2px 2px 10px' + let hover_background_color = '#90CDF4'; + let hover_tentative_background_color = '#FBD38D'; + let border_style = 'solid solid solid solid'; + const border_width = '1px 1px 1px 10px' if (isTentative) { border_color = '#F69052'; @@ -135,11 +134,15 @@ const PlannerTimeline = () => { setEventHover(id)} - onMouseLeave={() => setEventHover(-2)} + onClick={() => startEditAndSetCurrEventId(id)} > @@ -153,32 +156,11 @@ const PlannerTimeline = () => { {hostName} - {id == eventHover && - - } - onClick={() => startEditAndSetCurrEventId(id)} - size="sm" - /> - } - onClick={() => { - setDeleteItemId(id); - onRemoveOpen(); - }} - size="sm" - /> - {/* */} - - } ); })} - ); diff --git a/src/components/Planner/RemoveTimelineEventModal.jsx b/src/components/Planner/RemoveTimelineEventModal.jsx index 16860b0..a52d846 100644 --- a/src/components/Planner/RemoveTimelineEventModal.jsx +++ b/src/components/Planner/RemoveTimelineEventModal.jsx @@ -14,10 +14,11 @@ import { NPOBackend } from '../../utils/auth_utils'; import { useContext } from 'react'; import { PlannerContext } from './PlannerContext'; -const RemoveTimelineEventModal = ({ isOpen, onClose, deleteItemId }) => { +const RemoveTimelineEventModal = ({ isOpen, onClose, deleteItemId, closeForm }) => { - const { plannedEventsContext } = useContext(PlannerContext); + const { plannedEventsContext, currEventContext } = useContext(PlannerContext); const [plannedEvents, setPlannedEvents] = plannedEventsContext; + const setCurrEvent = currEventContext[1]; const toast = useToast(); @@ -38,8 +39,10 @@ const RemoveTimelineEventModal = ({ isOpen, onClose, deleteItemId }) => { duration: 3000, isClosable: true, }); - // setDataShouldRevalidate(true); onClose(); + + setCurrEvent({}); + closeForm(); } catch (error) { console.error(error); } @@ -67,6 +70,7 @@ RemoveTimelineEventModal.propTypes = { isOpen: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, deleteItemId: PropTypes.number.isRequired, + closeForm: PropTypes.func, }; export default RemoveTimelineEventModal; diff --git a/src/pages/PublishedSchedule/PublishedSchedule.jsx b/src/pages/PublishedSchedule/PublishedSchedule.jsx index 82067c7..a0aad50 100644 --- a/src/pages/PublishedSchedule/PublishedSchedule.jsx +++ b/src/pages/PublishedSchedule/PublishedSchedule.jsx @@ -11,6 +11,7 @@ const PublishedSchedule = () => { const {currentUser} = useAuthContext(); const [allSeasons, setAllSeasons] = useState([]); const [selectedSeason, setSelectedSeason] = useState(''); + const [seasonHover, setSeasonHover] = useState(false); const getTodaySeason = () => { let today = new Date(); @@ -59,22 +60,25 @@ const PublishedSchedule = () => { return ( {selectedSeason != '' ? ( - + {selectedSeason} ) : ( - + {curSeason} )}