From 368fe82c5525af0aa93d2ad01666bf6b17d35c60 Mon Sep 17 00:00:00 2001 From: Zen-cronic <83657429+Zen-cronic@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:28:23 -0500 Subject: [PATCH 1/4] feat: expose public events --- client/src/App.jsx | 82 +++--- client/src/app/api/apiSlice.js | 11 +- client/src/components/DashFooter.jsx | 2 +- client/src/components/DashHeader.jsx | 2 +- client/src/components/Layout.jsx | 14 +- client/src/components/PublicEvents.jsx | 21 ++ client/src/components/PublicFooter.jsx | 33 +++ client/src/components/PublicHeader.jsx | 3 +- client/src/components/PublicPage.jsx | 6 +- client/src/features/event/EventExcerpt.jsx | 18 +- client/src/features/event/EventPage.jsx | 3 +- client/src/features/event/eventsApiSlice.js | 7 +- .../src/features/event/filter/EventFilter.jsx | 24 +- .../features/event/search/EventSearchBar.jsx | 18 +- .../event/search/SearchedEventsList.jsx | 1 - client/src/features/event/sort/EventSort.jsx | 12 +- client/src/hooks/useAuth.jsx | 4 +- .../src/hooks/usePublicOrPrivateNavigate.jsx | 24 ++ server/src/config/corsOptions.js | 33 ++- server/src/controllers/eventsControllers.js | 261 +++--------------- server/src/helpers/includesSearchTerm.js | 16 +- .../src/middleware/errorHandlerMiddleware.js | 7 +- server/src/middleware/verifyJWT.js | 2 +- server/src/middleware/verifyRole.js | 37 ++- server/src/models/Event.js | 216 ++++----------- server/src/routes/eventsRoutes.js | 3 +- server/src/server.js | 10 +- 27 files changed, 342 insertions(+), 528 deletions(-) create mode 100644 client/src/components/PublicEvents.jsx create mode 100644 client/src/components/PublicFooter.jsx create mode 100644 client/src/hooks/usePublicOrPrivateNavigate.jsx diff --git a/client/src/App.jsx b/client/src/App.jsx index 00dafcd..6fc3e0a 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -30,68 +30,76 @@ import EventStats from "./features/event/EventStats"; import "react-toastify/dist/ReactToastify.css"; import "./App.css"; +import PublicEvents from "./components/PublicEvents"; const App = () => { return ( <Routes> - <Route path="/" element={<Layout />}> + <Route path="/" element={<Layout /> }> <Route index={true} element={<PublicPage />} /> - <Route path="/login" element={<Login />} /> + <Route element={<PrefetchEvents />}> + {/* browse events as a guest */} - <Route path="/register" element={<Register />} /> + <Route path="/events" element={ <EventHeader/>} > + <Route index={true} element={<PublicEvents />}/> + <Route path=":eventId" element={<EventPage />} /> + <Route path="search" element={<SearchedEventsList />} /> + <Route path="sort" element={<SortedEventsList />} /> + <Route path="filter" element={<FilteredEventList />} /> + </Route> + + <Route path="/login" element={<Login />} /> + + <Route path="/register" element={<Register />} /> - <Route element={<PersistLogin />}> - <Route element={<PrefetchEvents />}> + <Route element={<PersistLogin />}> + {/* <Route element={<PrefetchEvents />}> */} <Route path="/dash" element={<DashLayout />}> <Route element={<PrefetchVolunteers />}> <Route index={true} element={<Welcome />} /> - <Route path="events" element={<EventHeader />}> - <Route index={true} element={<EventList />} /> + <Route path="events" element={<EventHeader />}> + <Route index={true} element={<EventList />} /> - <Route path=":eventId"> - <Route index element={<EventPage />} /> + <Route path=":eventId"> + <Route index element={<EventPage />} /> - <Route element={<RoleBasedRoute allowedRole={"ADMIN"} />}> - <Route path="edit" element={<EditEvent />} /> - <Route path="stats" element={<EventStats />} /> - </Route> + <Route element={<RoleBasedRoute allowedRole={"ADMIN"} />}> + <Route path="edit" element={<EditEvent />} /> + <Route path="stats" element={<EventStats />} /> </Route> + </Route> - <Route path="new" element={<NewEventForm />} /> - <Route path="filter" element={<FilteredEventList />} /> + <Route path="new" element={<NewEventForm />} /> + <Route path="filter" element={<FilteredEventList />} /> - <Route path="sort" element={<SortedEventsList />} /> + <Route path="sort" element={<SortedEventsList />} /> - <Route path="search" element={<SearchedEventsList />} /> - </Route> + <Route path="search" element={<SearchedEventsList />} /> + </Route> - {/* prefetchVOluns here */} - {/* RoleBaseRoute for admin only */} - {/* <Route element={<RoleBaseRoute allowedRole={"ADMIN"}/>}> */} - <Route path="volunteers" element={<VolunteerHeader />}> - <Route element={<RoleBasedRoute allowedRole={"ADMIN"} />}> - {/* <Route element={<PrefetchVolunteers/>}> */} - <Route index={true} element={<VolunteersList />} /> + {/* prefetchVOluns here */} + {/* RoleBaseRoute for admin only */} + {/* <Route element={<RoleBaseRoute allowedRole={"ADMIN"}/>}> */} + <Route path="volunteers" element={<VolunteerHeader />}> + <Route element={<RoleBasedRoute allowedRole={"ADMIN"} />}> + <Route index={true} element={<VolunteersList />} /> - <Route path="search" element={<SearchedVolunList />} /> + <Route path="search" element={<SearchedVolunList />} /> - <Route path="sort" element={<SortedVolunList />} /> - {/* </Route> */} - </Route> + <Route path="sort" element={<SortedVolunList />} /> + </Route> - <Route - element={<RoleBasedRoute allowedRole={"VOLUNTEER"} />} - > - <Route path=":volunId"> - <Route index={true} element={<SingleVolunteerPage />} /> - <Route path="edit" element={<EditVolunteer />} /> - <Route path="pwd" element={<UpdatePassword />} /> - </Route> + <Route element={<RoleBasedRoute allowedRole={"VOLUNTEER"} />}> + <Route path=":volunId"> + <Route index={true} element={<SingleVolunteerPage />} /> + <Route path="edit" element={<EditVolunteer />} /> + <Route path="pwd" element={<UpdatePassword />} /> </Route> </Route> </Route> </Route> + </Route> </Route> </Route> </Route> diff --git a/client/src/app/api/apiSlice.js b/client/src/app/api/apiSlice.js index 565d407..b3599f9 100644 --- a/client/src/app/api/apiSlice.js +++ b/client/src/app/api/apiSlice.js @@ -1,7 +1,8 @@ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import { setCredentials } from '../../features/auth/authSlice'; -const apiBaseUrl =import.meta.env.VITE_API_PROD_URL || import.meta.env.VITE_API_DEV_URL +const apiBaseUrl =(import.meta.env.VITE_API_PROD_URL) || ( import.meta.env.VITE_API_DEV_URL) + const baseQuery = fetchBaseQuery({ baseUrl: apiBaseUrl, @@ -21,8 +22,16 @@ const baseQuery = fetchBaseQuery({ const baseQueryWithRefreshAuth = async (args, api, extraOptions) => { + const publicEndpoints = ['/events/search','/events/sort'] + + if (publicEndpoints.includes(args.endpoint) ) { + + return baseQuery(args, api, extraOptions); + } + let originalResult = await baseQuery(args, api, extraOptions) + //forbidden from verifyJWT if(originalResult?.error?.status === 403){ diff --git a/client/src/components/DashFooter.jsx b/client/src/components/DashFooter.jsx index adaec3e..8a6bc6a 100644 --- a/client/src/components/DashFooter.jsx +++ b/client/src/components/DashFooter.jsx @@ -38,7 +38,7 @@ const DashFooter = () => { content = ( <footer className='bg-secondary text-white position-relative bottom-0 w-100 h-auto'> - <Container className=' pt-4' > + <Container className=' pt-4 text-center' > <Row className='py-2'> <Col> diff --git a/client/src/components/DashHeader.jsx b/client/src/components/DashHeader.jsx index 01ec346..4562242 100644 --- a/client/src/components/DashHeader.jsx +++ b/client/src/components/DashHeader.jsx @@ -15,7 +15,7 @@ const DashHeader = () => { <Navbar.Toggle aria-controls="basic-navbar-nav"></Navbar.Toggle> <Navbar.Collapse id="basic-navbar-nav"> <Nav className="ms-auto header-nav"> - <LinkContainer to={"/dash/home"}> + <LinkContainer to={"/dash"}> <Nav.Link><FaHome/> Home</Nav.Link> </LinkContainer> <Logout /> diff --git a/client/src/components/Layout.jsx b/client/src/components/Layout.jsx index 177c5de..7fb5a9d 100644 --- a/client/src/components/Layout.jsx +++ b/client/src/components/Layout.jsx @@ -2,23 +2,27 @@ import { Outlet } from 'react-router-dom' import useAuth from '../hooks/useAuth' import PublicHeader from './PublicHeader' import { ToastContainer } from 'react-toastify' +import PublicFooter from './PublicFooter' const Layout = () => { const {volunId, role}= useAuth() - const displayPublicHeader = !volunId && !role + const displayPublic = !volunId && !role return ( - <> - {displayPublicHeader && <PublicHeader/>} + <body className='d-flex flex-column min-vh-100'> + {displayPublic && <PublicHeader/>} + <main className=' flex-grow-1'> <ToastContainer/> - <Outlet /> + <Outlet/> + </main> - </> + {displayPublic && <PublicFooter/>} + </body> ) } export default Layout \ No newline at end of file diff --git a/client/src/components/PublicEvents.jsx b/client/src/components/PublicEvents.jsx new file mode 100644 index 0000000..7f7f5ac --- /dev/null +++ b/client/src/components/PublicEvents.jsx @@ -0,0 +1,21 @@ +import React from 'react' +import EventHeader from '../features/event/EventHeader' +import EventList from '../features/event/EventList' +import useAuth from '../hooks/useAuth' + +const PublicEvents = () => { + + const authData = useAuth() + const content = ( + + <> + {/* <EventHeader/> */} + <EventList/> + </> + ) + + console.log("guest info: ", authData); + return content +} + +export default PublicEvents \ No newline at end of file diff --git a/client/src/components/PublicFooter.jsx b/client/src/components/PublicFooter.jsx new file mode 100644 index 0000000..3aae93c --- /dev/null +++ b/client/src/components/PublicFooter.jsx @@ -0,0 +1,33 @@ +import React from "react"; +import { Button, Col, Container, Row } from "react-bootstrap"; +import { Link } from "react-router-dom"; + +const PublicFooter = () => { + + const content = ( + <footer className=" bg-black text-white position-relative bottom-0 w-100 h-auto"> + <Container className=" pt-4 text-center"> + <Row className="py-2"> + <Col> + {/* <Button as={Link} to='/dash' variant='warning' > */} + <Button as={Link} to="/" variant="warning"> + Home + </Button> + </Col> + + {/* <Col> + <p>Hey! {username} | Status : {role}</p> + + </Col> */} + + <Col> + <p> Copyright © KZH 2023</p> + </Col> + </Row> + </Container> + </footer> + ); + return content; +}; + +export default PublicFooter; diff --git a/client/src/components/PublicHeader.jsx b/client/src/components/PublicHeader.jsx index a32a94c..5e4888e 100644 --- a/client/src/components/PublicHeader.jsx +++ b/client/src/components/PublicHeader.jsx @@ -1,7 +1,6 @@ import React from 'react' import { Container, Nav, Navbar } from 'react-bootstrap' import { FaRegistered, FaSignInAlt } from 'react-icons/fa' -import { Outlet } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' const PublicHeader = () => { @@ -10,7 +9,7 @@ const PublicHeader = () => { const headerContent = ( <header> - <Navbar bg="dark" variant="dark" collapseOnSelect expand='lg'> + <Navbar bg="dark" variant="dark" collapseOnSelect expand='lg' className=' py-3'> <Container> <Navbar.Brand href='/'>Home</Navbar.Brand> <Navbar.Toggle aria-controls='basic-navbar-nav'/> diff --git a/client/src/components/PublicPage.jsx b/client/src/components/PublicPage.jsx index fa55e4d..05148cc 100644 --- a/client/src/components/PublicPage.jsx +++ b/client/src/components/PublicPage.jsx @@ -12,7 +12,7 @@ const PublicPage = () => { <p className="text-center mb-4"> This space is for student volunteers at ... </p> - <div className="d-flex"> + <div className="d-flex mb-3"> <Button as={Link} variant="primary" to="/login" className=" me-3"> Sign In </Button> @@ -23,6 +23,10 @@ const PublicPage = () => { Register </Button> </div> + + <div className="d-flex"> + <Button as={Link} variant="warning" to="/events">Check out events!</Button> + </div> </Card> </Container> ); diff --git a/client/src/features/event/EventExcerpt.jsx b/client/src/features/event/EventExcerpt.jsx index 7965c23..66d7674 100644 --- a/client/src/features/event/EventExcerpt.jsx +++ b/client/src/features/event/EventExcerpt.jsx @@ -4,8 +4,9 @@ import React from "react"; import { useSelector } from "react-redux"; import { selectEventById } from './eventsApiSlice'; import { Button } from 'react-bootstrap'; -import {FaShareAlt} from 'react-icons/fa' +import {FaBook} from 'react-icons/fa' import FilterTagsDisplay from './filter/FilterTagsDisplay'; +import useAuth from '../../hooks/useAuth'; @@ -14,8 +15,10 @@ import FilterTagsDisplay from './filter/FilterTagsDisplay'; const EventExcerpt = ({ eventId, filterTags }) => { + const event = useSelector(state => (selectEventById(state, eventId))) + const {role, volunId} = useAuth() const navigate = useNavigate() @@ -29,7 +32,16 @@ const EventExcerpt = ({ eventId, filterTags }) => { else{ - const handleViewEvent = () => (navigate(`/dash/events/${event.id}`)) + + const handleViewEvent = () => { + + if(!role && !volunId){ + navigate(`/events/${event.id}`) + return + } + + + navigate(`/dash/events/${event.id}`)} content = ( <> @@ -42,7 +54,7 @@ const EventExcerpt = ({ eventId, filterTags }) => { onClick={handleViewEvent} variant='warning'> - <FaShareAlt/> + <FaBook/> Details </Button> </td> diff --git a/client/src/features/event/EventPage.jsx b/client/src/features/event/EventPage.jsx index dc0f042..6e7d69e 100644 --- a/client/src/features/event/EventPage.jsx +++ b/client/src/features/event/EventPage.jsx @@ -3,7 +3,6 @@ import { useSelector } from "react-redux"; import { useNavigate, useParams } from "react-router-dom"; import { selectEventById } from "./eventsApiSlice"; import useAuth from "../../hooks/useAuth"; -import EventShift from "./EventShift"; import { Container, Row, Col, Button, ListGroup } from "react-bootstrap"; import convertEventDisplayDate from "../../helpers/convertEventDisplayDate"; import EventShiftTable from "./EventShiftTable"; @@ -45,7 +44,7 @@ const EventPage = () => { }); const adminContent = - isAdmin && role === "ADMIN" ? ( + (isAdmin && role === "ADMIN") ? ( <Row className="my-2"> <Col> <Button type="button" onClick={handleEditEvent}> diff --git a/client/src/features/event/eventsApiSlice.js b/client/src/features/event/eventsApiSlice.js index 6734b01..28aecd8 100644 --- a/client/src/features/event/eventsApiSlice.js +++ b/client/src/features/event/eventsApiSlice.js @@ -5,6 +5,7 @@ import { setSearchedEvents, setSortedEvents, } from "./eventsSlice"; +import { fetchBaseQuery } from "@reduxjs/toolkit/dist/query"; export const eventsAdapter = createEntityAdapter(); @@ -120,10 +121,11 @@ export const eventsApiSlice = apiSlice.injectEndpoints({ postSearchedEvents: builder.query({ query: (searchTerm) => ({ - // url:'/events/search?q=', + url: `/events/search?q=${searchTerm}`, method: "POST", }), + async onQueryStarted(arg, { dispatch, queryFulfilled }) { try { @@ -219,18 +221,17 @@ const selectGetEventsData = createSelector( ); export const { - selectById: selectEventById, selectIds: selectEventsIds, selectAll: selectAllEvents, selectEntities: selectEventsEntities, - } = eventsAdapter.getSelectors( (state) => selectGetEventsData(state) ?? eventsInitialState ); export const { useGetEventsQuery, + useLazyPostFilteredEventsQuery, useLazyPostSortedEventsQuery, useLazyPostSearchedEventsQuery, diff --git a/client/src/features/event/filter/EventFilter.jsx b/client/src/features/event/filter/EventFilter.jsx index e7f9cf8..6a6b903 100644 --- a/client/src/features/event/filter/EventFilter.jsx +++ b/client/src/features/event/filter/EventFilter.jsx @@ -6,6 +6,7 @@ import DatePicker from "react-datepicker"; import format from 'date-fns/format'; import "react-datepicker/dist/react-datepicker.css"; import findingQueryTypes from '../../../config/findingQueryTypes'; +import { toast } from 'react-toastify'; const EventFilter = ({setFindingQuery}) => { @@ -119,25 +120,6 @@ const EventFilter = ({setFindingQuery}) => { if(!canFilter){ return } - // let filterKeysObj = {} - - // const filterKeysArr = [venue, isOpen, isUpcoming, date] - - // filterKeysArr.map((filterKey) => { - - // if(filterKey){ - - // let filterKeyStr = filterKey.toString() - - // if(typeof filterKey === 'boolean'){ - // console.log('filterKey is boolean: ', filterKey); - // filterKeyStr = Object.keys({filterKey}).pop() - // } - // Object.assign(filterKeysObj, {[filterKeyStr]: filterKey}) - - // } - - // }) //obj appch cuz js arr is non-associative const filterKeysObj = {venue, isOpen, isUpcoming, date} @@ -151,7 +133,7 @@ const EventFilter = ({setFindingQuery}) => { }) - console.log('filterKeysObj: ', filterKeysObj); + // console.log('filterKeysObj: ', filterKeysObj); try { @@ -160,12 +142,14 @@ const EventFilter = ({setFindingQuery}) => { await filterEvents(filterKeysObj, preferCacheValue).unwrap() navigate('/dash/events/filter') + setFindingQuery((prev) => ({ ...prev, findingQueryType: findingQueryTypes.FILTER, findingQueryVal: filterKeysObj, })); } catch (error) { + toast.error(error?.data?.message) console.log("Events Filter error: ", error); } diff --git a/client/src/features/event/search/EventSearchBar.jsx b/client/src/features/event/search/EventSearchBar.jsx index 5e712ca..8802a58 100644 --- a/client/src/features/event/search/EventSearchBar.jsx +++ b/client/src/features/event/search/EventSearchBar.jsx @@ -6,8 +6,12 @@ import { } from "../eventsApiSlice"; import { Button, Form } from "react-bootstrap"; import findingQueryTypes from "../../../config/findingQueryTypes"; +import useAuth from "../../../hooks/useAuth"; const EventSearchBar = ({ setFindingQuery }) => { + + const {role, volunId} = useAuth() + const [searchParams, setSearchParams] = useSearchParams(); const [searchQuery, setSearchQuery] = useState( @@ -29,18 +33,20 @@ const EventSearchBar = ({ setFindingQuery }) => { const preferCacheValue = false; const { data } = await searchEvent(searchQuery, preferCacheValue); - navigate("/dash/events/search?q=" + encodedSearchQuery); - // setVal(searchQuery) - // setVal({}) + if(!role && !volunId){ + navigate("/events/search?q=" + encodedSearchQuery) + } + else{ + navigate("/dash/events/search?q=" + encodedSearchQuery); - // const findingQuery = findingQueryTypes.SEARCH + } + setFindingQuery((prev) => ({ ...prev, findingQueryType: findingQueryTypes.SEARCH, findingQueryVal: searchQuery, })); - // {findingQuery: searchQuery}) - // setVal() + console.log("Searched events data: ", data); } catch (error) { diff --git a/client/src/features/event/search/SearchedEventsList.jsx b/client/src/features/event/search/SearchedEventsList.jsx index f492d60..5b8a7e8 100644 --- a/client/src/features/event/search/SearchedEventsList.jsx +++ b/client/src/features/event/search/SearchedEventsList.jsx @@ -1,7 +1,6 @@ import React from 'react' import { useSelector } from 'react-redux' import { selectSearchedEvents } from '../eventsSlice' -import EventExcerpt from '../EventExcerpt' import EventListLayout from '../EventListLayout' import SearchedEventExcerpt from './SearchedEventExcerpt' import { useSearchParams } from 'react-router-dom' diff --git a/client/src/features/event/sort/EventSort.jsx b/client/src/features/event/sort/EventSort.jsx index 6463a62..dd415ed 100644 --- a/client/src/features/event/sort/EventSort.jsx +++ b/client/src/features/event/sort/EventSort.jsx @@ -3,8 +3,11 @@ import { useLazyPostSortedEventsQuery } from "../eventsApiSlice"; import { useNavigate } from "react-router"; import { Button, Form } from "react-bootstrap"; import findingQueryTypes from "../../../config/findingQueryTypes"; +import useAuth from "../../../hooks/useAuth"; const EventSort = ({ setFindingQuery }) => { + + const {role, volunId} = useAuth() const [sortOption, setSortOption] = useState(""); const [sortEvents, { isLoading }] = useLazyPostSortedEventsQuery(); const navigate = useNavigate(); @@ -36,7 +39,14 @@ const EventSort = ({ setFindingQuery }) => { const preferCacheValue = true; // const {data} = await sortEvents({[sortOption]: true}, preferCacheValue) const { data } = await sortEvents(sortOption, preferCacheValue); - navigate("/dash/events/sort"); + + if(!role && !volunId){ + navigate("/events/sort") + } + else{ + navigate("/dash/events/sort"); + + } setFindingQuery((prev) => ({ ...prev, diff --git a/client/src/hooks/useAuth.jsx b/client/src/hooks/useAuth.jsx index 100dc24..56125d4 100644 --- a/client/src/hooks/useAuth.jsx +++ b/client/src/hooks/useAuth.jsx @@ -1,8 +1,9 @@ -import React from 'react' import { useSelector } from 'react-redux' import { selectCurrentToken } from '../features/auth/authSlice' import jwtDecode from 'jwt-decode' + +//if guest, all false/null const useAuth = () => { const currentToken = useSelector(selectCurrentToken) @@ -47,7 +48,6 @@ const useAuth = () => { return {volunId: '', role: '', isVolunteer, isAdmin} - // throw new Error('MUST have a token for useAuth hook') } export default useAuth \ No newline at end of file diff --git a/client/src/hooks/usePublicOrPrivateNavigate.jsx b/client/src/hooks/usePublicOrPrivateNavigate.jsx new file mode 100644 index 0000000..1ef8c66 --- /dev/null +++ b/client/src/hooks/usePublicOrPrivateNavigate.jsx @@ -0,0 +1,24 @@ +import React from 'react' +import useAuth from './useAuth' +import { useNavigate } from 'react-router-dom' + +const usePublicOrPrivateNavigate = (pathname) => { + + const authObj = useAuth() + + const navigate = useNavigate() + + return () =>{ + if(!(Object.values(authObj).every(val => val))){ + navigate(pathname) + } + + else { + navigate(`/dash${pathname}`) + } + + } + +} + +export default usePublicOrPrivateNavigate \ No newline at end of file diff --git a/server/src/config/corsOptions.js b/server/src/config/corsOptions.js index 70e0803..bfa01d7 100644 --- a/server/src/config/corsOptions.js +++ b/server/src/config/corsOptions.js @@ -1,19 +1,22 @@ - -const allowedOrigins = require('./allowedOrigins') +const allowedOrigins = require("./allowedOrigins"); const corsOptions = { - origin: (origin, callback) => { - if (allowedOrigins.indexOf(origin) !== -1 ) { - - //callback(err, origin (non-fx val)) - callback(null, true) - } else { - callback(new Error('Not allowed by CORS')) - } - }, - credentials: true, - optionsSuccessStatus: 200 -} + origin: (origin, callback) => { + + const allowedCondition = + process.env.NODE_ENV === "development" + ? allowedOrigins.indexOf(origin) !== -1 || !origin + : allowedOrigins.indexOf(origin) !== -1; -module.exports = corsOptions + if (allowedCondition) { + //callback(err, origin (non-fx val)) + callback(null, true); + } else { + callback(new Error("Not allowed by CORS")); + } + }, + credentials: true, + optionsSuccessStatus: 200, +}; +module.exports = corsOptions; diff --git a/server/src/controllers/eventsControllers.js b/server/src/controllers/eventsControllers.js index b7562af..fa9c9d6 100644 --- a/server/src/controllers/eventsControllers.js +++ b/server/src/controllers/eventsControllers.js @@ -1,6 +1,6 @@ const asyncHandler = require("express-async-handler"); - const Event = require("../models/Event"); + const requiredInputChecker = require("../helpers/requiredInputChecker"); const includesSearchTerm = require("../helpers/includesSearchTerm"); const { @@ -10,9 +10,10 @@ const { filteredTagsSort, filterEventsUpcomingShifts, } = require("../helpers/filterEventsHelper"); + const { sortEventsHelper } = require("../helpers/sortEventsHelper"); const filterNonDuplicate = require("../helpers/filterNonDuplicate"); -const { closestTo, isAfter, isEqual, compareAsc } = require("date-fns"); +const { compareAsc } = require("date-fns"); const objKeysIncludes = require("../helpers/objKeysIncludes"); const { FILTER_OPTIONS } = require("../config/filterOptions"); @@ -21,8 +22,8 @@ const elemObjIncludes = require("../helpers/elemObjIncludes"); const filterArrSortLoose = require("../helpers/filterArrSortLoose"); const sortUpcomingEventsDates = require("../helpers/sortUpcomingEventsDates"); + const createNewEvent = asyncHandler(async (req, res) => { - //set localTime const { eventName, eventVenue, eventDates, eventDescription, shifts } = req.body; @@ -30,7 +31,6 @@ const createNewEvent = asyncHandler(async (req, res) => { return res.status(400).json({ message: "All fields required" }); } - //NOT findById const duplicate = await Event.findOne({ eventName }).lean().exec(); if (duplicate) { @@ -39,13 +39,9 @@ const createNewEvent = asyncHandler(async (req, res) => { const newEvent = new Event({ ...req.body }); - //handled by middleware validators - // newEvent.localEventDate = convertLocalDateString(newEvent.eventDate) - + //pre-hook middleware await newEvent.save(); - //run middleware validators before saving - //data.newVolunteer in front from queryFulfilled or unwrap() res.json({ newEvent }); }); @@ -81,57 +77,45 @@ const searchEvents = asyncHandler(async (req, res) => { // /search/?q=:query const { q } = req.query; - //this only returns exact matches - // const allEvents = await Event.find({eventName: {$in: q}}) - const allEvents = await Event.find().lean(); // console.log(typeof q); const matchingEvents = allEvents - .filter( - (event) => - { - try { - return ( - includesSearchTerm(event.eventName, q) || - includesSearchTerm(event.eventDescription, q) - ); - } catch (error) { - console.error('an error from searchEvents:', error.message); - return false; // Exclude this event from the results - } + .filter((event) => { + try { + return ( + includesSearchTerm(event.eventName, q) || + includesSearchTerm(event.eventDescription, q) + ); + } catch (error) { + console.error("an error from searchEvents:", error.message); + return false; // Exclude this event from the results } - ) - //only "eventId" needed to look for each event in front + }) + .map((event) => ({ eventId: event._id, - })); res.json({ searchTerm: q, matchingEvents }); }); -//combine event sort const sortEvents = [ asyncHandler(async (req, res, next) => { //only 1 sort option at a time const [[sortOption, orderBool]] = Object.entries(req.body); - - //sort by aplhabetically, or open positions if (sortOption === SORT_OBJECT.SOONEST.sortOption) { - console.log("next() to handle sort event dates"); return next(); } - const sortedEvents = await sortEventsHelper(sortOption, orderBool) - //serizlisation - .then((events) => + const sortedEvents = await sortEventsHelper(sortOption, orderBool).then( + (events) => events.map((event) => ({ eventId: event._id, eventName: event.eventName, })) - ); + ); res.json({ sortedEvents }); }), @@ -140,68 +124,7 @@ const sortEvents = [ asyncHandler(async (req, res) => { const allEvents = await Event.find().lean().exec(); - //with recursion helper - - // const sortUpcomingEventDatesFx = (event, datesArr, invalidArr =[]) => { - - // const currentDate =new Date(Date.now()) - - // // const cmpDatesArray = datesArr.filter(date => invalidArr.includes(date)? false : true) - // const cmpDatesArray = datesArr.filter(date => ( - - // invalidArr.some(invalidDate => isEqual(invalidDate, date)) - // ? - // false - // : - // true - // )) - - // console.log('cmpDatesArray: ', cmpDatesArray); - // const closestToCurrentDate = closestTo(currentDate, cmpDatesArray) - - // console.log("clossestToCurrentDate: ", closestToCurrentDate); - - // //ether condi alone works - // if( - // closestToCurrentDate === undefined - // || - // !cmpDatesArray?.length - // ){ - - // return [] - // } - - // if(isAfter(closestToCurrentDate, currentDate)){ - - // console.log(closestToCurrentDate, " is after ", currentDate); - // // return [{eventDate: closestToCurrentDate, eventName: event?.eventName, eventId: event?._id - // // // return [{eventDate: closestToCurrentDate - - // // // eventId: event._id, eventName: event.eventName - // // }] - - // return [{eventDate: closestToCurrentDate, eventName: event?.eventName,eventId: event?._id}] - - // } - - // // console.log('reched HERE - b4 next fx call'); - - // invalidArr.push(closestToCurrentDate) - // return sortUpcomingEventDatesFx(event, datesArr, invalidArr) - - // // console.log('reched HERE - after fx called'); - - // } - - // const sortedUpcomingEventsDates = allEvents.flatMap(event => { - - // const result = sortUpcomingEventDatesFx(event, event.eventDates) - - // console.log("result: ", result); - - // return result - // } ) - + //with recursion logic const sortedUpcomingEventsDates = sortUpcomingEventsDates(allEvents); const sortedEvents = [...sortedUpcomingEventsDates].sort((a, b) => @@ -216,105 +139,6 @@ const sortEvents = [ }), ]; -//[] keep the _id of shifts as before -// const updateEventInfo = asyncHandler(async(req,res)=>{ - -// const {eventId, eventName, eventVenue, -// eventDates, eventDescription, shifts} = req.body - -// if(requiredInputChecker(req.body)){ -// return res.status(400).json({message: "All fields required"}) -// } - -// const existingEvent = await Event.findById(eventId).exec() - -// if(!existingEvent){ -// return res.status(400).json({message: "Event DNE for PUT event info"}) -// } - -// //eventName canNOT be the same as an exisintgEvent - ltr more rigorous srh algo -// const duplicate = await Event.findOne({eventName}).lean().exec() - -// if(duplicate && duplicate._id.toString() !== existingEvent.id){ - -// return res.status(409).json({message: "The renamed eventName already exists", duplicateEvent: duplicate}) -// } - -// // algorize this -// existingEvent.eventName = eventName -// existingEvent.eventVenue = eventVenue -// existingEvent.eventDates = eventDates -// existingEvent.eventDescription = eventDescription -// // existingEvent.shifts = shifts - -// //NOT a pojo, use toObject() to access keys/vals -// // Object.keys(req.body).map((key) => { - -// // //updating shifts handled elsewhere -// // if(key === 'shifts'){ -// // return null -// // } -// // const matchingEventKey = Object.keys(existingEvent.toObject()).find((eventKey)=> eventKey.includes(key)) - -// // if(matchingEventKey !== undefined){ - -// // existingEvent[matchingEventKey] = req.body[key] -// // } - -// // }) - -// const existingAllShifts = existingEvent.shifts - -// await Promise.all(shifts.map(async (returnedShift) => { - -// const existingShift =existingAllShifts.find(shift => shift._id.toString() === returnedShift?.shiftId) - -// console.log('existingShift found using returnedShfit from front: ', existingShift); -// if(existingShift){ - -// console.log('retunredShiftObj: ',returnedShift); -// console.log('returnedShiftObj shiftStart into Date: ',new Date(returnedShift.shiftStart)) -// // existingShift = {...existingShift, -// // shiftStart: returnedShift.shiftStart, -// // shiftEnd: returnedShift.shiftEnd, -// // shiftPositions: returnedShift.shiftPositions -// // } - -// // existingEvent.shifts - -// //the save pre-hooks are NOT activated -// const res = await Event.updateOne( -// { _id: existingEvent._id, -// "shifts._id": existingShift._id }, - -// { $set: { "shifts.$.shiftStart": new Date(returnedShift.shiftStart), -// "shifts.$.shiftEnd": new Date(returnedShift.shiftEnd), -// "shifts.$.shiftPositions": parseInt(returnedShift.shiftPositions), - -// } } - -// ) - -// console.log('res: ', res); - -// // await existingEvent.save() -// } - -// else{ -// existingEvent.shifts.push(returnedShift) - -// await existingEvent.save() -// } -// })) - -// const udpatedEvent =existingEvent - -// console.log('udpatedEvent: ', udpatedEvent); -// res.json({existingEvent}) - -// }) - -//with save() only const updateEventInfo = asyncHandler(async (req, res) => { const { eventId, @@ -335,7 +159,6 @@ const updateEventInfo = asyncHandler(async (req, res) => { return res.status(400).json({ message: "Event DNE for PUT event info" }); } - //eventName canNOT be the same as an exisintgEvent - ltr more rigorous srh algo const duplicate = await Event.findOne({ eventName }).lean().exec(); if (duplicate && duplicate._id.toString() !== existingEvent.id) { @@ -359,20 +182,19 @@ const updateEventInfo = asyncHandler(async (req, res) => { (shift) => shift._id.toString() === returnedShift?.shiftId ); - console.log( - "existingShift found using returnedShfit from front: ", - existingShift - ); + // console.log( + // "existingShift found using returnedShfit from front: ", + // existingShift + // ); + if (existingShift) { - console.log("retunredShiftObj: ", returnedShift); + // console.log("retunredShiftObj: ", returnedShift); existingShift.shiftStart = new Date(returnedShift.shiftStart); existingShift.shiftEnd = new Date(returnedShift.shiftEnd); existingShift.shiftPositions = parseInt(returnedShift.shiftPositions); } else { existingEvent.shifts.push(returnedShift); - - // await existingEvent.save() } }); @@ -442,7 +264,6 @@ const getSignedUpVolunteers = asyncHandler(async (req, res) => { const filterEvents = [ //venue filter asyncHandler(async (req, res, next) => { - // if(!Object.keys(req.body).includes(FILTER_OPTIONS.VENUE)){ if (!objKeysIncludes(req.body, FILTER_OPTIONS.VENUE)) { return next(); } @@ -504,10 +325,10 @@ const filterEvents = [ let idsWithTags = []; console.log("value of req.body for filterEvent: ", { ...req.body }); + Object.keys(req.body).map((filterKey) => { switch (filterKey) { case FILTER_OPTIONS.DATE: - // filteredResultsByKey[filterKey] = filteredDate filteredResultsByKey = { ...filteredResultsByKey, [filterKey]: filteredDate, @@ -515,7 +336,6 @@ const filterEvents = [ break; case FILTER_OPTIONS.IS_OPEN: - // filteredResultsByKey[filterKey] = filteredIsOpen filteredResultsByKey = { ...filteredResultsByKey, [filterKey]: filteredIsOpen, @@ -525,6 +345,7 @@ const filterEvents = [ case FILTER_OPTIONS.VENUE: // filteredResultsByKey[filterKey] = filteredVenue + filteredResultsByKey = { ...filteredResultsByKey, [filterKey]: filteredVenue, @@ -549,16 +370,11 @@ const filterEvents = [ filteredAllIds.forEach((id) => { Object.entries(filteredResultsByKey).forEach(([filterKey, result]) => { - const [_, filterKeyVal] = Object.entries(req.body).find( ([key, _]) => key === filterKey ); - // const [[a, filterKeyVal]] = Object.entries(req.body).find( - // ([key, val]) => key === filterKey - // ); - - console.log("filterKeyVal of each eventId: ", filterKeyVal); + // console.log("filterKeyVal of each eventId: ", filterKeyVal); if (result.includes(id)) { const isEventIdAlrExists = elemObjIncludes(idsWithTags, id); @@ -575,8 +391,6 @@ const filterEvents = [ { [filterKey]: filterKeyVal }, ], }; - - //return event DNR(return) every event } return event; @@ -584,15 +398,6 @@ const filterEvents = [ idsWithTags = bufferArr; } else { - //for better normalization in front - // idsWithTags = [...idsWithTags, { eventId: id, - // filterTags: [filterKey]}] - - //modify to - // idsWithTags = [...idsWithTags, { eventId: id, - // filterTags: [{[filterKey]: filterKeyVal }] }] - // idsWithTags.push({ eventId: id, filterTags: [filterKey] }); - idsWithTags.push({ eventId: id, filterTags: [{ [filterKey]: filterKeyVal }], @@ -605,9 +410,7 @@ const filterEvents = [ const sortedIdsWithTags = filteredTagsSort(idsWithTags); // console.log('idsWIthTags: ',idsWithTags); - console.log("sortedIdsWithTags: ", sortedIdsWithTags); - // console.table("sortedIdsWithTags: ", sortedIdsWithTags); - + // console.log("sortedIdsWithTags: ", sortedIdsWithTags); res.status(200).json({ filteredResultsByKey, @@ -626,10 +429,10 @@ module.exports = { getEventById, searchEvents, - sortEvents, + filterEvents, getSignedUpVolunteers, - filterEvents, + }; diff --git a/server/src/helpers/includesSearchTerm.js b/server/src/helpers/includesSearchTerm.js index 3444f0b..5614cdc 100644 --- a/server/src/helpers/includesSearchTerm.js +++ b/server/src/helpers/includesSearchTerm.js @@ -7,11 +7,11 @@ * @throws {Error} If searchIndex or searchTerm is not a string */ const includesSearchTerm = (searchIndex, searchTerm) => { - if (typeof searchIndex !== "string" || typeof searchTerm !== "string") { - throw new Error("Search Index OR search term not a string"); - } - - return searchIndex.toLowerCase().includes(searchTerm.toLowerCase()); - }; - - module.exports = includesSearchTerm; \ No newline at end of file + if (typeof searchIndex !== "string" || typeof searchTerm !== "string") { + throw new Error("Search Index OR search term not a string"); + } + + return searchIndex.toLowerCase().includes(searchTerm.toLowerCase()); +}; + +module.exports = includesSearchTerm; diff --git a/server/src/middleware/errorHandlerMiddleware.js b/server/src/middleware/errorHandlerMiddleware.js index 0e67dc0..649bd95 100644 --- a/server/src/middleware/errorHandlerMiddleware.js +++ b/server/src/middleware/errorHandlerMiddleware.js @@ -1,19 +1,16 @@ const errorHandler = (err, req, res, next) => { - // let statusCode = res.statusCode ? res.statusCode : 500; //if mongoose Error, res.statusCODE DNE, therefore defaults to 200, so if 200, make it 500 let statusCode = res.statusCode === 200 ? 500 : res.statusCode; let message = err.message; - console.log("status code and err.message: ", statusCode + " " + message); + // console.log("status code and err.message: ", statusCode + " " + message); res.status(statusCode); res.json({ message: message, stack: err.stack }); - // return res.json({ - // error: { status: statusCode, data: { message: message, stack: err.stack } }, - // }); + }; module.exports = { diff --git a/server/src/middleware/verifyJWT.js b/server/src/middleware/verifyJWT.js index e19dfdf..2f1c1d6 100644 --- a/server/src/middleware/verifyJWT.js +++ b/server/src/middleware/verifyJWT.js @@ -8,7 +8,7 @@ require('dotenv').config() const verifyJWT = (req, res, next) => { - const authHeaders = req.headers.authorization || req.headers.authorization + const authHeaders = req.headers.authorization || req.headers.Authorization if(!authHeaders?.startsWith('Bearer ')){ diff --git a/server/src/middleware/verifyRole.js b/server/src/middleware/verifyRole.js index fa50056..6a8369d 100644 --- a/server/src/middleware/verifyRole.js +++ b/server/src/middleware/verifyRole.js @@ -1,26 +1,21 @@ +const verifyRole = (allowedRole) => { + return (req, res, next) => { + if (!req?.role && !req?.volunId) { + return res.status(401).json({ message: `Unauthorized: must be a ${allowedRole}` }); + } -const verifyRole = (allowedRole)=> { - - return (req,res, next) => { - - if(!req?.role){ - return res.status(401).json({message: "UnAthorized - no role"}) - } - - - console.log("verifyRole, ", req.role, allowedRole) - - //rather than include, exact matching - const result = req.role.includes(allowedRole) - - - if(!result){ - return res.status(401).json({message: `Unathorized for ${req.role} role`}) + // console.log("verifyRole, ", req.role, allowedRole) - } + const result = req.role.includes(allowedRole); - next() + if (!result) { + return res + .status(401) + .json({ message: `Unathorized for ${req.role} role` }); } -} -module.exports = verifyRole \ No newline at end of file + next(); + }; +}; + +module.exports = verifyRole; diff --git a/server/src/models/Event.js b/server/src/models/Event.js index 32a86fe..d3c7810 100644 --- a/server/src/models/Event.js +++ b/server/src/models/Event.js @@ -1,4 +1,4 @@ -const { default: mongoose } = require("mongoose"); +const mongoose = require("mongoose"); const calculateShiftDuration = require("../helpers/model-helpers/calculateShiftDuration"); const { includedTimeInDate, @@ -36,16 +36,6 @@ const EventShiftSchema = new mongoose.Schema({ shiftPositions: { type: Number, required: true, - - // validate: { - - // validator: function(){ - - // return (this.shiftPositions > 0) - // }, - - // message: 'shiftPositions must be greater than 0 upon creation' - // } }, signedUpVolunteers: [ @@ -56,18 +46,54 @@ const EventShiftSchema = new mongoose.Schema({ ], }); -//validate shiftStart and shiftEnd times -EventShiftSchema.pre("validate", function (next) { - // if(!this.$parent().isNew - // && - // !this.$parent().isModified('shifts') - // ) { +const EventSchema = new mongoose.Schema( + { + eventName: { + type: String, + required: true, + }, + eventDescription: { + type: String, + required: true, + }, + + eventDates: { + type: [Date], + required: true, + + //y? + default: undefined, + }, + + localEventDates: { + type: [String], + }, + + shifts: { + type: [EventShiftSchema], + required: true, + validate: { + validator: function () { + return this.shifts.length; + }, + + message: "shifts Arr canNOT be an empty arr", + }, + }, - // console.log('parent doc is NOT new, therefore 1) pre-VALIDATE not called'); - // return next() + openPositions: { + type: Number, + }, - // } - // console.log(options.w); + eventVenue: { type: String, required: true }, + }, + { + timestamps: true, + } +); + +//validate shiftStart and shiftEnd times +EventShiftSchema.pre("validate", function (next) { console.log("1) pre-Validate for shiftStart and shiftEnd called"); if (!sameDateShift(this.shiftStart, this.shiftEnd)) { @@ -94,15 +120,6 @@ EventShiftSchema.pre("validate", function (next) { //pre for shiftDuration EventShiftSchema.pre("save", function (next) { - // if(!this.$parent().isNew - // && - // !this.$parent().isModified('shifts') - // ){ - - // console.log('parent doc is NOT new, therefore 2) pre-SAVE not called'); - // return next() - - // } console.log("2) pre-Save for shift DURATION"); this.shiftDuration = calculateShiftDuration(this.shiftStart, this.shiftEnd); @@ -116,20 +133,7 @@ EventShiftSchema.pre("save", function (next) { }); //create localShiftDates based on shiftStart and shiftEnd -//pre for localStart and localEnd EventShiftSchema.pre("save", function (next) { - //if NOT new - //AND if shifts arr NOT modified - // if(!this.$parent().isNew - // && - // !this.$parent().isModified('shifts') - // ){ - - // console.log('parent doc is NOT new, therefore 3) pre-SAVE not called'); - // return next() - - // } - console.log("3) pre localStart and LocalEnd eventSHIFTschema called"); this.localShiftStart = convertLocalDateString(this.shiftStart); @@ -138,6 +142,7 @@ EventShiftSchema.pre("save", function (next) { next(); }); + //[] check if can be 0 on update from EditEventSchema //pre for shiftPositions EventShiftSchema.pre("save", function (next) { @@ -167,65 +172,10 @@ EventShiftSchema.pre("save", function (next) { next(); }); -const EventSchema = new mongoose.Schema( - { - eventName: { - type: String, - required: true, - }, - eventDescription: { - type: String, - required: true, - }, - - eventDates: { - type: [Date], - required: true, - - //y? - default: undefined, - }, - - localEventDates: { - type: [String], - }, - - shifts: { - type: [EventShiftSchema], - required: true, - validate: { - validator: function () { - return this.shifts.length; - }, - - message: "shifts Arr canNOT be an empty arr", - }, - }, - - openPositions: { - type: Number, - }, - - eventVenue: { type: String, required: true }, - }, - { - timestamps: true, - } -); +//EventSchema hooks -//either save or validate hook works -//pre-VAlidate for shiftDates + eventDates +//pre-VAlidate: shiftDates included in eventDates EventSchema.pre("validate", function (next) { - // if(!this.isNew - // && - // !this.isModified('shifts') - // ){ - - // console.log('main doc is NOT new, therefore 4) pre-VALIDATE not called'); - // return next() - - // } - console.log("4) pre-VAlidate for shiftDates + eventDates validator"); this.shifts.map((shift) => { if ( @@ -233,22 +183,16 @@ EventSchema.pre("validate", function (next) { (date) => date.getDate() === shift.shiftStart.getDate() ) ) { - console.log("--- Error dates: ---"); - console.log("shiftInfo: ", shift._id, " | ", shift.shiftStart); + // console.log("--- Error dates: ---"); + // console.log("shiftInfo: ", shift._id, " | ", shift.shiftStart); throw new Error("shift Dates must be on at least one of the eventDates"); } }); - // const sortedDurations = this.shifts.sort((a,b) => { - - // return a.shiftDuration - b.shiftDuration - // }) - // console.log(sortedDurations); - next(); }); -//hook for removing duplicate eventDates obj and localEventDates str from arr +//pre-validate: remove duplicate eventDates obj and localEventDates str from arr EventSchema.pre("validate", function (next) { console.log( "9) pre-VALIDATE for removing duplicate eventDates obj and localEventDates str from arr" @@ -276,19 +220,9 @@ EventSchema.pre("validate", function (next) { next(); }); -//create localEventDates based on eventDates[] and check conversion using getDate() +//create localEventDates based on eventDates[] and check conversion using getDate() EventSchema.pre("save", function (next) { - // if(!this.isNew - // && - // !this.isModified('shifts') - // ){ - - // console.log('main doc is NOT new, therefore 5) pre-SAVE not called'); - // return next() - - // } - console.log("5) eventtSchema localDates called"); this.localEventDates = this.eventDates.map((utcDate) => @@ -296,10 +230,6 @@ EventSchema.pre("save", function (next) { ); for (let i = 0; i < this.eventDates.length; i++) { - // console.log("localEvent Str => Date: ",new Date(this.localEventDates[i])); - // console.log(new Date(this.localEventDates[i]).getDate() ); - // console.log((this.eventDates[i]).getDate()); - if ( new Date(this.localEventDates[i]).getDate() !== this.eventDates[i].getDate() @@ -307,12 +237,12 @@ EventSchema.pre("save", function (next) { throw new Error("localEventDate and eventDate must have the same date"); } } + next(); }); -//pre-save hook for openPositions based on shiftPosi +//pre-save: calculate openPositions based on shiftPosi EventSchema.pre("save", function (next) { - console.log("FROM 6) this.isModified('shifts')", this.isModified("shifts")); console.log( "6) pre-save hook for openPositions based on shiftPosi | called w each update in shiftPosi" ); @@ -321,27 +251,17 @@ EventSchema.pre("save", function (next) { const shiftPositionsObj = this.shifts.reduce((prev, current) => { const accumulated = prev.shiftPositions + current.shiftPositions; - // console.log('totalPositions progression: ', accumulated); return { shiftPositions: accumulated }; }); this.openPositions = shiftPositionsObj.shiftPositions; - console.log("total open posiontsn 6)", this.openPositions); + // console.log("total open posiontsn 6)", this.openPositions); next(); }); -//pre-save hook for duplicate shift time +//pre-save: prevent duplicate shift time EventSchema.pre("save", function (next) { - // if(!this.isNew - // && - // !this.isModified('shifts') - // ){ - - // console.log('main doc is NOT new NOR shifts isNOT modified, therefore 7) pre-SAVE not called'); - // return next() - - // } console.log("7)pre-SAVE for duplicate shift time but from EventSchema"); const bufferShiftsArr = this.shifts.slice(); @@ -363,25 +283,5 @@ EventSchema.pre("save", function (next) { next(); }); -// EventSchema.pre('updateOne',{document:false, query: true}, async function(next){ - -// console.log('8-B) EventSchema pre-updateOne hook called as QueryMiddleware'); - -// //getUpdate returns current update opeartions -// //the $set obj with updatedAt field -// const modifiedField = this.getUpdate().$set - -// console.log('8-B) modifiedField: ', modifiedField); - -// this.updateOne() -// const docToUpate = await this.model.findOne(this.getQuery()) - -// console.log('8-B) docToUpate: ', docToUpate); - -// // const updateInfo = await this.model.findOne(this.getUpdate()) -// // console.log('8-B) updateInfo: ', updateInfo); -// next() - -// }) module.exports = mongoose.model("events", EventSchema); diff --git a/server/src/routes/eventsRoutes.js b/server/src/routes/eventsRoutes.js index ee59f3e..690180d 100644 --- a/server/src/routes/eventsRoutes.js +++ b/server/src/routes/eventsRoutes.js @@ -5,15 +5,16 @@ const verifyRole = require("../middleware/verifyRole"); const router = express.Router(); + router .route("/") + .get(eventsControllers.getAllEvents) .post(verifyRole(ROLES.ADMIN), eventsControllers.createNewEvent) .put(verifyRole(ROLES.ADMIN), eventsControllers.updateEventInfo) .delete(verifyRole(ROLES.ADMIN), eventsControllers.deleteEvent); -//delete ltr // event/refresh - taskscheduling diff --git a/server/src/server.js b/server/src/server.js index 216d839..8a6bb48 100644 --- a/server/src/server.js +++ b/server/src/server.js @@ -14,12 +14,12 @@ const port = process.env.PORT || 3700 connectDB() -// Handle options credentials check - before cors -app.use(credentials); - app.use(express.json()) + +app.use(credentials); app.use(helmet()) app.use(cors(corsOptions)) + app.use(cookieParser()); app.use('/auth', require('./routes/authRoutes')) @@ -27,9 +27,11 @@ app.use('/refresh', require('./routes/refreshRoute')) app.use('/logout', require('./routes/logoutRoute')) app.use('/register', require('./routes/registerRoute')) +app.use('/events', require('./routes/eventsRoutes')) + app.use(verifyJWT) app.use('/users', require('./routes/usersRoutes')) -app.use('/events', require('./routes/eventsRoutes')) app.use(errorHandler) + app.listen(port, ()=> console.log(`PORT listening on ${process.env.PORT}`)) \ No newline at end of file From 3bca7d45f74d9f761e9f336f6b014e8884b9b374 Mon Sep 17 00:00:00 2001 From: Zen-cronic <83657429+Zen-cronic@users.noreply.github.com> Date: Wed, 22 Nov 2023 17:53:37 -0500 Subject: [PATCH 2/4] feat: custom hook & componentize public events --- client/src/App.jsx | 10 +- client/src/components/PublicEvents.jsx | 43 ++++---- client/src/components/PublicFooter.jsx | 5 +- client/src/components/PublicPage.jsx | 2 +- client/src/features/auth/PersistLogin.jsx | 2 +- client/src/features/event/DeleteEvent.jsx | 1 - client/src/features/event/EventExcerpt.jsx | 98 +++++++------------ client/src/features/event/EventHeader.jsx | 2 +- client/src/features/event/EventPage.jsx | 78 ++++++++++----- client/src/features/event/EventShiftTable.jsx | 2 +- .../src/features/event/filter/EventFilter.jsx | 7 +- .../event/filter/checkIsFilteredEventsPage.js | 11 +-- .../features/event/search/EventSearchBar.jsx | 17 +--- .../event/search/HighlightSearchResults.jsx | 3 +- .../event/search/SearchedEventExcerpt.jsx | 12 +-- .../src/hooks/useFindingQueryDisplayHook.jsx | 2 +- .../src/hooks/usePublicOrPrivateNavigate.jsx | 33 +++---- 17 files changed, 160 insertions(+), 168 deletions(-) diff --git a/client/src/App.jsx b/client/src/App.jsx index 6fc3e0a..4f4564f 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -39,15 +39,9 @@ const App = () => { <Route index={true} element={<PublicPage />} /> <Route element={<PrefetchEvents />}> - {/* browse events as a guest */} - <Route path="/events" element={ <EventHeader/>} > - <Route index={true} element={<PublicEvents />}/> - <Route path=":eventId" element={<EventPage />} /> - <Route path="search" element={<SearchedEventsList />} /> - <Route path="sort" element={<SortedEventsList />} /> - <Route path="filter" element={<FilteredEventList />} /> - </Route> + {/* browse events as a guest */} + <Route path="/events/*" element={<PublicEvents/>}/> <Route path="/login" element={<Login />} /> diff --git a/client/src/components/PublicEvents.jsx b/client/src/components/PublicEvents.jsx index 7f7f5ac..3ef2cac 100644 --- a/client/src/components/PublicEvents.jsx +++ b/client/src/components/PublicEvents.jsx @@ -1,21 +1,30 @@ -import React from 'react' -import EventHeader from '../features/event/EventHeader' -import EventList from '../features/event/EventList' -import useAuth from '../hooks/useAuth' +import React from "react"; +import EventHeader from "../features/event/EventHeader"; +import EventList from "../features/event/EventList"; +import useAuth from "../hooks/useAuth"; +import { Route, Routes } from "react-router-dom"; +import EventPage from "../features/event/EventPage"; +import SearchedEventsList from "../features/event/search/SearchedEventsList"; +import SortedEventsList from "../features/event/sort/SortedEventsList"; +import FilteredEventList from "../features/event/filter/FilteredEventList"; const PublicEvents = () => { + const authData = useAuth(); + const content = ( + <> + <EventHeader /> + <Routes> + <Route index={true} element={<EventList />} /> + <Route path=":eventId" element={<EventPage />} /> + <Route path="search" element={<SearchedEventsList />} /> + <Route path="sort" element={<SortedEventsList />} /> + <Route path="filter" element={<FilteredEventList />} /> + </Routes> + </> + ); - const authData = useAuth() - const content = ( + console.log("guest info: ", authData); + return content; +}; - <> - {/* <EventHeader/> */} - <EventList/> - </> - ) - - console.log("guest info: ", authData); - return content -} - -export default PublicEvents \ No newline at end of file +export default PublicEvents; diff --git a/client/src/components/PublicFooter.jsx b/client/src/components/PublicFooter.jsx index 3aae93c..b8d2c6e 100644 --- a/client/src/components/PublicFooter.jsx +++ b/client/src/components/PublicFooter.jsx @@ -15,10 +15,7 @@ const PublicFooter = () => { </Button> </Col> - {/* <Col> - <p>Hey! {username} | Status : {role}</p> - - </Col> */} + <Col> <p> Copyright © KZH 2023</p> diff --git a/client/src/components/PublicPage.jsx b/client/src/components/PublicPage.jsx index 05148cc..eaebc99 100644 --- a/client/src/components/PublicPage.jsx +++ b/client/src/components/PublicPage.jsx @@ -19,7 +19,7 @@ const PublicPage = () => { <Badge className="me-3" bg="info" pill> or </Badge> - <Button as={Link} variant="secondary" to="/register"> + <Button as={Link} variant="primary" to="/register"> Register </Button> </div> diff --git a/client/src/features/auth/PersistLogin.jsx b/client/src/features/auth/PersistLogin.jsx index efa4109..ac83fce 100644 --- a/client/src/features/auth/PersistLogin.jsx +++ b/client/src/features/auth/PersistLogin.jsx @@ -26,7 +26,7 @@ const PersistLogin = () => { try { const {accessToken } = await refresh().unwrap() - console.log('new AccessTOkne from PersistLogin: ', accessToken); + // console.log('new AccessTOkne from PersistLogin: ', accessToken); setTrueSuccess(true) diff --git a/client/src/features/event/DeleteEvent.jsx b/client/src/features/event/DeleteEvent.jsx index 1d80b92..952ba36 100644 --- a/client/src/features/event/DeleteEvent.jsx +++ b/client/src/features/event/DeleteEvent.jsx @@ -24,7 +24,6 @@ const DeleteEvent = ({ eventId }) => { const handleDeleteEvent = async () => { try { - // const deleteEventResult = await deleteEvent(eventId).unwrap(); const deleteEventResult = await deleteEvent({eventId}).unwrap(); console.log("deleteEventResult: ", deleteEventResult); diff --git a/client/src/features/event/EventExcerpt.jsx b/client/src/features/event/EventExcerpt.jsx index 66d7674..4e45004 100644 --- a/client/src/features/event/EventExcerpt.jsx +++ b/client/src/features/event/EventExcerpt.jsx @@ -1,69 +1,41 @@ - -import { useNavigate } from 'react-router-dom'; import React from "react"; import { useSelector } from "react-redux"; -import { selectEventById } from './eventsApiSlice'; -import { Button } from 'react-bootstrap'; -import {FaBook} from 'react-icons/fa' -import FilterTagsDisplay from './filter/FilterTagsDisplay'; -import useAuth from '../../hooks/useAuth'; - - +import { selectEventById } from "./eventsApiSlice"; +import { Button } from "react-bootstrap"; +import { FaBook } from "react-icons/fa"; +import FilterTagsDisplay from "./filter/FilterTagsDisplay"; +import usePublicOrPrivateNavigate from "../../hooks/usePublicOrPrivateNavigate"; //regex for filtered page - conditional col in table - const EventExcerpt = ({ eventId, filterTags }) => { - - - const event = useSelector(state => (selectEventById(state, eventId))) - - const {role, volunId} = useAuth() - const navigate = useNavigate() - - - - let content - - if(!event){ - - content = <p>Event NOT found</p> - } - - else{ - - - const handleViewEvent = () => { - - if(!role && !volunId){ - navigate(`/events/${event.id}`) - return - } - - - navigate(`/dash/events/${event.id}`)} - content = ( - - <> - <td>{event.eventName}</td> - <td>{event.eventVenue}</td> - <td>{event.eventDescription}</td> - <td> - <Button - type='button' - onClick={handleViewEvent} - variant='warning'> - - <FaBook/> - Details - </Button> - </td> - <FilterTagsDisplay filterTags={filterTags} /> - </> - ) - } - return content -} - - -export default EventExcerpt \ No newline at end of file + const event = useSelector((state) => selectEventById(state, eventId)); + + const navigateFn = usePublicOrPrivateNavigate(); + + let content; + + if (!event) { + content = <p>Event NOT found</p>; + } else { + const handleViewEvent = () => navigateFn(`/events/${event.id}`); + + content = ( + <> + <td>{event.eventName}</td> + <td>{event.eventVenue}</td> + <td>{event.eventDescription}</td> + <td> + <Button type="button" onClick={handleViewEvent} variant="warning"> + <FaBook /> + Details + </Button> + </td> + <FilterTagsDisplay filterTags={filterTags} /> + </> + ); + } + return content; +}; + +export default EventExcerpt; diff --git a/client/src/features/event/EventHeader.jsx b/client/src/features/event/EventHeader.jsx index 7768fc6..175e6e9 100644 --- a/client/src/features/event/EventHeader.jsx +++ b/client/src/features/event/EventHeader.jsx @@ -66,7 +66,7 @@ const EventHeader = () => { <Container className="my-2"> <LinkContainer to={"/dash/events"}> - <Button variant="primary">Back to Events List</Button> + <Button variant="warning">Back to Events List</Button> </LinkContainer> </Container> diff --git a/client/src/features/event/EventPage.jsx b/client/src/features/event/EventPage.jsx index 6e7d69e..89a4fa0 100644 --- a/client/src/features/event/EventPage.jsx +++ b/client/src/features/event/EventPage.jsx @@ -1,19 +1,22 @@ import React from "react"; import { useSelector } from "react-redux"; -import { useNavigate, useParams } from "react-router-dom"; +import { Link, useNavigate, useParams } from "react-router-dom"; import { selectEventById } from "./eventsApiSlice"; import useAuth from "../../hooks/useAuth"; -import { Container, Row, Col, Button, ListGroup } from "react-bootstrap"; +import { Container, Row, Col, Button, ListGroup, Modal } from "react-bootstrap"; import convertEventDisplayDate from "../../helpers/convertEventDisplayDate"; import EventShiftTable from "./EventShiftTable"; import DeleteEvent from "./DeleteEvent"; +import useModal from "../../hooks/useModal"; const EventPage = () => { const { eventId } = useParams(); const navigate = useNavigate(); - const { role, isAdmin } = useAuth(); + const { role, isAdmin, volunId } = useAuth(); + + const signUpModal = useModal(); //vs getEventByIdQuery const event = useSelector((state) => selectEventById(state, eventId)); @@ -21,7 +24,6 @@ const EventPage = () => { const handleEditEvent = () => { // fulll url needed navigate(`/dash/events/${eventId}/edit`); - // navigate(`/edit`) }; const handleViewSignedUpVolunteers = async () => { @@ -44,27 +46,58 @@ const EventPage = () => { }); const adminContent = - (isAdmin && role === "ADMIN") ? ( - <Row className="my-2"> - <Col> - <Button type="button" onClick={handleEditEvent}> - Edit Event - add shift, etc - </Button> - </Col> + isAdmin && role === "ADMIN" ? ( + <Container className="d-flex flex-column justify-content-center"> + <Row className="my-2 "> + <Col> + <Button type="button" onClick={handleEditEvent}> + Edit Event - add shift, etc + </Button> + </Col> + + <Col> + <DeleteEvent eventId={eventId} /> + </Col> + <Col> + <Button type="button" onClick={handleViewSignedUpVolunteers}> + See signed-up volunteers + </Button> + </Col> + </Row> + </Container> + ) : null; - <Col> - <DeleteEvent eventId={eventId} /> - </Col> - <Col> - <Button type="button" onClick={handleViewSignedUpVolunteers}> - See signedUPVolunteers/stats for admin - </Button> - </Col> - </Row> + const publicContent = + !role && !volunId ? ( + <Container className="text-center"> + <Button type="button" onClick={signUpModal.showModal}> + Sign up! + </Button> + + <Modal show={signUpModal.isDisplayed} onHide={signUpModal.hideModal}> + <Modal.Header closeButton> + <Modal.Title>Sign up?</Modal.Title> + </Modal.Header> + <Modal.Body>Register or login to start volunteering!</Modal.Body> + <Modal.Footer> + <Button variant="secondary" onClick={signUpModal.hideModal}> + Back + </Button> + + <Button as={Link} variant="primary" to="/login"> + Sign In + </Button> + + <Button as={Link} variant="primary" to="/register"> + Register + </Button> + </Modal.Footer> + </Modal> + </Container> ) : null; content = ( - <Container fluid className="py-2"> + <Container fluid className="py-2 "> <Row> <Col> <h1>{event.eventName}</h1> @@ -94,7 +127,7 @@ const EventPage = () => { </Col> </Row> - <Row> + <Row className=" d-flex flex-column justify-content-center"> <Col> <label>Event Shifts: </label> <EventShiftTable shifts={event.shifts} eventId={eventId} /> @@ -102,6 +135,7 @@ const EventPage = () => { </Row> {adminContent} + {publicContent} </Container> ); } diff --git a/client/src/features/event/EventShiftTable.jsx b/client/src/features/event/EventShiftTable.jsx index 4401dae..3be393c 100644 --- a/client/src/features/event/EventShiftTable.jsx +++ b/client/src/features/event/EventShiftTable.jsx @@ -25,7 +25,7 @@ const EventShiftTable = ({shifts, eventId}) => { )) return ( - <Container className='my-2 px-3'> + <Container className='my-2 '> <Table striped bordered hover > <thead > diff --git a/client/src/features/event/filter/EventFilter.jsx b/client/src/features/event/filter/EventFilter.jsx index 6a6b903..4124532 100644 --- a/client/src/features/event/filter/EventFilter.jsx +++ b/client/src/features/event/filter/EventFilter.jsx @@ -7,6 +7,7 @@ import format from 'date-fns/format'; import "react-datepicker/dist/react-datepicker.css"; import findingQueryTypes from '../../../config/findingQueryTypes'; import { toast } from 'react-toastify'; +import usePublicOrPrivateNavigate from '../../../hooks/usePublicOrPrivateNavigate'; const EventFilter = ({setFindingQuery}) => { @@ -17,9 +18,10 @@ const EventFilter = ({setFindingQuery}) => { const [selectedDate, setSelectedDate] = useState(new Date()); const [date, setDateFilter] = useState(""); + const navigateFn = usePublicOrPrivateNavigate() const [filterEvents, {isLoading}] = useLazyPostFilteredEventsQuery() - const navigate = useNavigate() + // const navigate = useNavigate() const onVenueFilterChange = (e) => setVenueFilter( e.target.value) const onIsOpenFilterChange = (e) => setIsOpenFilter(prev => !prev) @@ -141,7 +143,8 @@ const EventFilter = ({setFindingQuery}) => { const preferCacheValue = false await filterEvents(filterKeysObj, preferCacheValue).unwrap() - navigate('/dash/events/filter') + // navigate('/dash/events/filter') + navigateFn('/events/filter') setFindingQuery((prev) => ({ ...prev, diff --git a/client/src/features/event/filter/checkIsFilteredEventsPage.js b/client/src/features/event/filter/checkIsFilteredEventsPage.js index 92d2d5d..e34a11a 100644 --- a/client/src/features/event/filter/checkIsFilteredEventsPage.js +++ b/client/src/features/event/filter/checkIsFilteredEventsPage.js @@ -2,16 +2,9 @@ const checkIsFilteredEventsPage = (pathname) => { + const filteredEventsPageRegex = [new RegExp(`^/dash/events/filter$`) , new RegExp(`^/events/filter$`)] - // const devBaseUrl = import.meta.env.BASE_URL; - // const filteredEventsPageRegex = new RegExp( - // `^${devBaseUrl}dash/events/filter$` - // ); - const filteredEventsPageRegex = new RegExp( - `^/dash/events/filter$` - ); - - const isFilteredEventsPage = filteredEventsPageRegex.test(pathname); + const isFilteredEventsPage = filteredEventsPageRegex.some(regex => regex.test(pathname)) if (isFilteredEventsPage) { // console.log("at filtered events page"); diff --git a/client/src/features/event/search/EventSearchBar.jsx b/client/src/features/event/search/EventSearchBar.jsx index 8802a58..f0f9705 100644 --- a/client/src/features/event/search/EventSearchBar.jsx +++ b/client/src/features/event/search/EventSearchBar.jsx @@ -1,16 +1,14 @@ import React, { useState } from "react"; -import { useNavigate, useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router-dom"; import { - useGetEventsQuery, useLazyPostSearchedEventsQuery, } from "../eventsApiSlice"; import { Button, Form } from "react-bootstrap"; import findingQueryTypes from "../../../config/findingQueryTypes"; -import useAuth from "../../../hooks/useAuth"; +import usePublicOrPrivateNavigate from "../../../hooks/usePublicOrPrivateNavigate"; const EventSearchBar = ({ setFindingQuery }) => { - const {role, volunId} = useAuth() const [searchParams, setSearchParams] = useSearchParams(); @@ -19,7 +17,7 @@ const EventSearchBar = ({ setFindingQuery }) => { ); const [searchEvent] = useLazyPostSearchedEventsQuery(); - const navigate = useNavigate(); + const navigateFn = usePublicOrPrivateNavigate() const handleSearchSubmit = async (e) => { e.preventDefault(); @@ -33,14 +31,9 @@ const EventSearchBar = ({ setFindingQuery }) => { const preferCacheValue = false; const { data } = await searchEvent(searchQuery, preferCacheValue); - if(!role && !volunId){ - navigate("/events/search?q=" + encodedSearchQuery) - } - else{ - navigate("/dash/events/search?q=" + encodedSearchQuery); - - } + navigateFn(`/events/search?q=${encodedSearchQuery}`) + setFindingQuery((prev) => ({ ...prev, findingQueryType: findingQueryTypes.SEARCH, diff --git a/client/src/features/event/search/HighlightSearchResults.jsx b/client/src/features/event/search/HighlightSearchResults.jsx index 9ceee29..92c2090 100644 --- a/client/src/features/event/search/HighlightSearchResults.jsx +++ b/client/src/features/event/search/HighlightSearchResults.jsx @@ -11,7 +11,8 @@ const HighlightSearchResults = ({text, highlight}) => { const textParts = text.split(new RegExp(`(${highlight})`, "gi")); - console.log(textParts, ' : textParts'); + // console.log(textParts, ' : textParts'); + const withHighlightedContent = ( <span> {textParts.map((textPart, index) => { diff --git a/client/src/features/event/search/SearchedEventExcerpt.jsx b/client/src/features/event/search/SearchedEventExcerpt.jsx index 9ea198f..ed2f4a0 100644 --- a/client/src/features/event/search/SearchedEventExcerpt.jsx +++ b/client/src/features/event/search/SearchedEventExcerpt.jsx @@ -1,11 +1,11 @@ -import { useNavigate } from 'react-router-dom'; import React from "react"; import { useSelector } from "react-redux"; import { selectEventById } from '../eventsApiSlice'; import { Button } from 'react-bootstrap'; -import {FaShareAlt} from 'react-icons/fa' +import {FaBook} from 'react-icons/fa' import HighlightSearchResults from './HighlightSearchResults'; +import usePublicOrPrivateNavigate from '../../../hooks/usePublicOrPrivateNavigate'; @@ -15,8 +15,8 @@ import HighlightSearchResults from './HighlightSearchResults'; const SearchedEventExcerpt = ({ eventId, searchTerm }) => { const event = useSelector(state => (selectEventById(state, eventId))) - - const navigate = useNavigate() + + const navigateFn = usePublicOrPrivateNavigate() @@ -29,7 +29,7 @@ const SearchedEventExcerpt = ({ eventId, searchTerm }) => { else{ - const handleViewEvent = () => (navigate(`/dash/events/${event.id}`)) + const handleViewEvent = () => (navigateFn(`/events/${event.id}`)) if(!searchTerm){ console.warn("searchTerm is null"); @@ -48,7 +48,7 @@ const SearchedEventExcerpt = ({ eventId, searchTerm }) => { onClick={handleViewEvent} variant='warning'> - <FaShareAlt/> + <FaBook/> Details </Button> </td> diff --git a/client/src/hooks/useFindingQueryDisplayHook.jsx b/client/src/hooks/useFindingQueryDisplayHook.jsx index 4d1c5f3..1425679 100644 --- a/client/src/hooks/useFindingQueryDisplayHook.jsx +++ b/client/src/hooks/useFindingQueryDisplayHook.jsx @@ -21,7 +21,7 @@ const useFindingQueryDisplayHook = () => { ); setShowFindingQuery(displayQuery); - //jsut setShowFindingQuery(displayQuery); + }, [location]); //not dep on showFindingQuery return {findingQuery, showFindingQuery, setFindingQuery} diff --git a/client/src/hooks/usePublicOrPrivateNavigate.jsx b/client/src/hooks/usePublicOrPrivateNavigate.jsx index 1ef8c66..7f6c37e 100644 --- a/client/src/hooks/usePublicOrPrivateNavigate.jsx +++ b/client/src/hooks/usePublicOrPrivateNavigate.jsx @@ -1,24 +1,21 @@ -import React from 'react' -import useAuth from './useAuth' -import { useNavigate } from 'react-router-dom' +import useAuth from "./useAuth"; +import { useNavigate } from "react-router-dom"; -const usePublicOrPrivateNavigate = (pathname) => { - - const authObj = useAuth() +const usePublicOrPrivateNavigate = () => { - const navigate = useNavigate() + const authObj = useAuth(); - return () =>{ - if(!(Object.values(authObj).every(val => val))){ - navigate(pathname) - } - - else { - navigate(`/dash${pathname}`) - } + const navigate = useNavigate(); + const navigateFn = (publicPathname) => { + if (!Object.values(authObj).every((val) => val)) { + navigate(publicPathname); + } else { + navigate(`/dash${publicPathname}`); } - -} + }; -export default usePublicOrPrivateNavigate \ No newline at end of file + return navigateFn +}; + +export default usePublicOrPrivateNavigate; From dd2f97207d1d29c6276c2c56e4ea44255b8134eb Mon Sep 17 00:00:00 2001 From: Zen-cronic <83657429+Zen-cronic@users.noreply.github.com> Date: Wed, 22 Nov 2023 18:38:35 -0500 Subject: [PATCH 3/4] minor updates --- client/src/App.css | 12 + client/src/components/Layout.jsx | 43 +- client/src/components/PublicEvents.jsx | 5 +- client/src/components/PublicPage.jsx | 5 +- .../src/features/auth/AuthFormContainer.jsx | 2 +- .../event/form/AddEventDateAndShiftTime.jsx | 8 +- .../src/features/event/form/EditEventForm.jsx | 6 +- .../features/event/form/EditEventForm_One.jsx | 504 ------------------ .../features/event/form/EditEventForm_Two.jsx | 483 ----------------- client/src/features/volun/VolunteerHeader.jsx | 8 +- 10 files changed, 50 insertions(+), 1026 deletions(-) delete mode 100644 client/src/features/event/form/EditEventForm_One.jsx delete mode 100644 client/src/features/event/form/EditEventForm_Two.jsx diff --git a/client/src/App.css b/client/src/App.css index 48bc08d..cbdebfa 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -12,6 +12,18 @@ label.total_volunteers_label{ margin: 10px; } +body.public_layout { + + display: flex; + flex-direction: column; + min-height: 100vh; + +} + +main.public_layout { + flex-grow: 1; +} + /* 992px expand:lg */ @media screen and (max-width: 992px) { diff --git a/client/src/components/Layout.jsx b/client/src/components/Layout.jsx index 7fb5a9d..509168a 100644 --- a/client/src/components/Layout.jsx +++ b/client/src/components/Layout.jsx @@ -1,28 +1,25 @@ -import { Outlet } from 'react-router-dom' -import useAuth from '../hooks/useAuth' -import PublicHeader from './PublicHeader' -import { ToastContainer } from 'react-toastify' -import PublicFooter from './PublicFooter' +import { Outlet } from "react-router-dom"; +import useAuth from "../hooks/useAuth"; +import PublicHeader from "./PublicHeader"; +import { ToastContainer } from "react-toastify"; +import PublicFooter from "./PublicFooter"; const Layout = () => { + const { volunId, role } = useAuth(); - const {volunId, role}= useAuth() + const displayPublic = !volunId && !role; - const displayPublic = !volunId && !role + return ( + <body className="public_layout"> + {displayPublic && <PublicHeader />} + <main className="public_layout"> - return ( - - <body className='d-flex flex-column min-vh-100'> - {displayPublic && <PublicHeader/>} - <main className=' flex-grow-1'> - <ToastContainer/> - - <Outlet/> - </main> - - - {displayPublic && <PublicFooter/>} - </body> - ) -} -export default Layout \ No newline at end of file + <ToastContainer /> + <Outlet /> + </main> + + {displayPublic && <PublicFooter />} + </body> + ); +}; +export default Layout; diff --git a/client/src/components/PublicEvents.jsx b/client/src/components/PublicEvents.jsx index 3ef2cac..5a12d21 100644 --- a/client/src/components/PublicEvents.jsx +++ b/client/src/components/PublicEvents.jsx @@ -9,7 +9,8 @@ import SortedEventsList from "../features/event/sort/SortedEventsList"; import FilteredEventList from "../features/event/filter/FilteredEventList"; const PublicEvents = () => { - const authData = useAuth(); + +// const authData = useAuth(); const content = ( <> <EventHeader /> @@ -23,7 +24,7 @@ const PublicEvents = () => { </> ); - console.log("guest info: ", authData); +// console.log("guest info: ", authData); return content; }; diff --git a/client/src/components/PublicPage.jsx b/client/src/components/PublicPage.jsx index eaebc99..ca6b025 100644 --- a/client/src/components/PublicPage.jsx +++ b/client/src/components/PublicPage.jsx @@ -16,8 +16,9 @@ const PublicPage = () => { <Button as={Link} variant="primary" to="/login" className=" me-3"> Sign In </Button> - <Badge className="me-3" bg="info" pill> - or + <Badge className="me-3 d-flex align-items-center justify-content-center" bg="info" pill> + or + </Badge> <Button as={Link} variant="primary" to="/register"> Register diff --git a/client/src/features/auth/AuthFormContainer.jsx b/client/src/features/auth/AuthFormContainer.jsx index bb5857c..2f992e8 100644 --- a/client/src/features/auth/AuthFormContainer.jsx +++ b/client/src/features/auth/AuthFormContainer.jsx @@ -5,7 +5,7 @@ const AuthFormContainer = ({children}) => { return ( - <Container> + <Container className='my-3'> <Row className='justify-content-md-center mt-5'> <Col xs={12} md={6} className='card p-5' > {children} diff --git a/client/src/features/event/form/AddEventDateAndShiftTime.jsx b/client/src/features/event/form/AddEventDateAndShiftTime.jsx index 1b71462..3d5f9ec 100644 --- a/client/src/features/event/form/AddEventDateAndShiftTime.jsx +++ b/client/src/features/event/form/AddEventDateAndShiftTime.jsx @@ -23,14 +23,14 @@ const AddEventDateAndShiftTime = ({ // const handleShiftStartChange = (e) => return ( - <Row> - <Col > + <Row > + <Col xs={12} sm={6} md={4} lg={3}> {<DatePickerForm eventDate={eventDate} updateEvent={updateEvent} eventId={eventId}/>} </Col> - <Col xs={1}> + {/* <Col xs={1}> - </Col> + </Col> */} <Col> {/* {timePickerForm} */} diff --git a/client/src/features/event/form/EditEventForm.jsx b/client/src/features/event/form/EditEventForm.jsx index ada7cbe..2d6110e 100644 --- a/client/src/features/event/form/EditEventForm.jsx +++ b/client/src/features/event/form/EditEventForm.jsx @@ -404,12 +404,12 @@ const EditEventForm = ({ event, eventId }) => { <Row> <Col>Event Dates</Col> - <Col xs={1}></Col> + {/* <Col xs={1}></Col> */} <Col>ShiftStart Time</Col> <Col>ShiftEnd Time</Col> <Col>Shift Posituios</Col> - <Col xs={1}></Col> + {/* <Col xs={1}></Col> */} </Row> <Row> <br></br> @@ -434,7 +434,7 @@ const EditEventForm = ({ event, eventId }) => { <Row> <Col> - <Button type="button" variant="warning" onClick={handleShowModal}> + <Button type="button" variant="success" onClick={handleShowModal}> Edit Event </Button> diff --git a/client/src/features/event/form/EditEventForm_One.jsx b/client/src/features/event/form/EditEventForm_One.jsx deleted file mode 100644 index 59cd281..0000000 --- a/client/src/features/event/form/EditEventForm_One.jsx +++ /dev/null @@ -1,504 +0,0 @@ -import React, { useState, useEffect } from "react"; -import AddEventDateAndShiftTime from "./AddEventDateAndShiftTime"; - -import { toast } from "react-toastify"; -import { useNavigate } from "react-router-dom"; -import isValidNumberInput from "../../../helpers/isValidNumberInput"; -import { - Container, - Row, - Form, - Stack, - Col, - FloatingLabel, - Button, -} from "react-bootstrap"; -import { usePutUpdateEventInfoMutation } from "../eventsApiSlice"; - -//event obj from back - localEventDates+T00:00 -const EditEventForm_One = ({ event, eventId }) => { - const [listId, setListId] = useState(1); - const [shiftListId, setShiftListId] = useState(1 * 100); - - const navigate = useNavigate(); - - const [formData, setFormData] = useState({ - eventName: "", - eventVenue: "", - eventDates: [], - eventDescription: "", - shifts: [], - }); - - const [ - updateEventInfo, - { isSuccess: isUpdateSuccess, isLoading: isUpdateLoading }, - ] = usePutUpdateEventInfoMutation(); - - //reformat data for front rendering - //local dates format from back using date-fns-tz format(): yyyy-MM-dd HH:mm TZ - - useEffect(() => { - if (event) { - let initialListId = listId - 1; - let initialShiftListId = shiftListId - 1; - - //non dup arr - dup arr needed for rendering - //use event.shifts.map(shift => shift.localShiftStart.split(' ')[0]) to get arr of dates and concat T00:00 - - //try as fork - // const dupEventDatesWithListIdsAndDates = event.shifts.map((shift) => { - - // initialListId++; - // const date = shift.localShiftStart.split(" ")[0].concat("T00:00"); - - // return { - // listId: initialListId, - // date, - // }; - - // }) - const eventDatesWithListIdsAndDates = event.localEventDates.map( - (localEventDate) => { - // const listId = initialListId++ - initialListId++; - const date = localEventDate.split(" ")[0].concat("T00:00"); - - return { - listId: initialListId, - date, - //shiftCount: 0 - }; - } - ); - - let correspondingEventsArr = []; - - let shiftCountOnSameDate = 0; - - //shfitListIds && parentListIdForShift - const shiftsWithListIds = event.shifts.map((shift) => { - if (!Array.isArray(eventDatesWithListIdsAndDates)) { - throw new Error("Must be an arr for shiftsWithListIds"); - } - - const splitLocalShiftStart = shift.localShiftStart.split(" "); - const splitLocalShiftEnd = shift.localShiftEnd.split(" "); - - //for finding correspodingEvent only - const shiftStartDate = splitLocalShiftStart[0]; - - //HH:mm format sliced from 2021-08-01 09:00:00 ZZZ - const shiftStartTime = splitLocalShiftStart[1].slice(0, 5); - const shiftEndTime = splitLocalShiftEnd[1].slice(0, 5); - - initialShiftListId++; - - //eventDateObj.date = date + T00:00 - const correspondingEvent = eventDatesWithListIdsAndDates.find( - (eventDateObj) => eventDateObj.date.includes(shiftStartDate) - ); - - const foundCorrespondingEvent = correspondingEventsArr.find( - (eventDateObj) => - eventDateObj.date === correspondingEvent.date && - eventDateObj.listId === correspondingEvent.listId - ); - - //if corrsepoidngEvents alreday in arr, DO NOT add to arr - if (!foundCorrespondingEvent) { - correspondingEventsArr.push(correspondingEvent); - - //incremented to 1 - only when NOT foundCorrespondingEvent - shiftCountOnSameDate++; - // return - } - - //duplicate eventDateObj exists in correspondingEventsArr - //what if more than 2 shifts in same date? - else { - //incremented to 2 - only when foundCorrespondingEvent - shiftCountOnSameDate++; - - if (foundCorrespondingEvent && shiftCountOnSameDate > 1) { - initialListId++; - eventDatesWithListIdsAndDates.push({ - listId: initialListId, - date: foundCorrespondingEvent.date, - }); - - shiftCountOnSameDate = 0; - } - } - - console.log("foundCorrespondingEvent: ", foundCorrespondingEvent); - - //verified - BUT cannot handle alt shifts in same date - console.log( - "eventDatesWithLIstIDsandDates after adding each shift to shifsWIthListIDs", - eventDatesWithListIdsAndDates - ); - - return { - //to keep shiftId unchanged when sending submitting to back - shiftId: shift._id, - - shiftListId: initialShiftListId, - shiftStart: shiftStartTime, - shiftEnd: shiftEndTime, - shiftPositions: shift.shiftPositions.toString(), - parentListIdForShift: correspondingEvent.listId, - }; - }); - - setFormData({ - ...formData, - eventName: event.eventName, - eventVenue: event.eventVenue, - eventDescription: event.eventDescription, - eventDates: eventDatesWithListIdsAndDates, - shifts: shiftsWithListIds, - }); - - //only works with + 1 - setListId(initialListId + 1); - setShiftListId(initialShiftListId + 1); - } - }, []); - - useEffect(() => { - if (isUpdateSuccess) { - setFormData({ - eventName: "", - eventVenue: "", - eventDates: [], - eventDescription: "", - shifts: [], - }); - - toast.success("Event updated successfully"); - - navigate("/dash/events"); - } - }, [isUpdateSuccess, navigate]); - - //onClick addEventDateAndSHift, both eventDates and shifts changes - const addEventDateAndShift = () => { - setFormData({ - ...formData, - eventDates: [ - ...formData.eventDates, - { - listId: listId, - date: "", - }, - ], - shifts: [ - ...formData.shifts, - { - shiftListId: shiftListId, - shiftStart: "", - shiftEnd: "", - shiftPositions: "", - parentListIdForShift: listId, - }, - ], - }); - - console.log( - "addEventandShift & parentListIdForShift listID: ", - listId, - " | shiftListId: ", - shiftListId - ); - - setListId(listId + 1); - setShiftListId(shiftListId + 1); - }; - - const updateEvent = (eventDateObj) => { - const updatedEventDates = formData.eventDates.map((eventDate) => { - if (eventDate.listId === eventDateObj.listId) { - return eventDateObj; - } - - //unmodified obj - return eventDate; - }); - - setFormData({ ...formData, eventDates: updatedEventDates }); - }; - - const updateShift = (shiftObj) => { - const updatedShiftTimes = formData.shifts.map((shift) => { - if (shiftObj.shiftListId === shift.shiftListId) { - return shiftObj; - } - - return shift; - }); - - setFormData({ ...formData, shifts: updatedShiftTimes }); - }; - - const removeEventAndShift = (eventDateObj) => { - console.log("tbremoved evetnDateObj: ", eventDateObj); - if (formData.eventDates.length === 1) { - console.log("min 1 date/shift"); - return null; - } - - //Does NOT handle duplicate eventDates - const updatedEventDates = formData.eventDates.filter( - (eventDate) => eventDate.listId !== eventDateObj.listId - ); - - //[]remove shifts of that event too - for bcknd - console.log("updatedShiftTimes w/o removing: ", formData.shifts); - - const updatedShiftTimes = formData.shifts.filter( - (shift) => shift.parentListIdForShift !== eventDateObj.listId - ); - - setFormData({ - ...formData, - - eventDates: updatedEventDates, - shifts: updatedShiftTimes, - }); - }; - - //base this on shifts to display same date shifts - - // const renderEventDatesAndShifts = formData.eventDates.map((eventDate) => { - - // console.log('eventDate from renderEventDatesAndShifts: ', eventDate); - - // const correspondingShift = formData.shifts.find(shift => shift.parentListIdForShift === eventDate.listId); - - // //undefined correspondingShift - // console.log('correspondingShift: ', correspondingShift); - - // return ( - - // <AddEventDateAndShiftTime - // key={eventDate.listId} - - // listId={listId-1} - // eventDate={eventDate} - - // removeEventAndShift={removeEventAndShift} - // updateEvent={updateEvent} - // updateShift={updateShift} - // shift={correspondingShift} - // shiftListId={shiftListId - 1} - - // eventId={eventId} - // /> - // ) - - // }) - - const renderEventDatesAndShifts = formData.shifts.map((shift) => { - //shift.shiftStart and shiftEnd here are jsut HH:mm - const correspondingEvent = formData.eventDates.find( - (eventDateObj) => eventDateObj.listId === shift.parentListIdForShift - ); - - // console.log("shift from renderEventDatesAndShifts: ", shift); - // console.log("correspondingEvent for renderEventDatesAndShifts: ", correspondingEvent); - return ( - <AddEventDateAndShiftTime - //potential listId duplicated - // key={correspondingEvent.listId} - key={shift.shiftListId} - listId={listId - 1} - eventDate={correspondingEvent} - removeEventAndShift={removeEventAndShift} - updateEvent={updateEvent} - updateShift={updateShift} - shift={shift} - shiftListId={shiftListId - 1} - eventId={eventId} - /> - ); - }); - - const handleFormSubmit = async (e) => { - e.preventDefault(); - - //mini input validation - if ( - formData.shifts.some((shift) => !isValidNumberInput(shift.shiftPositions)) - ) { - toast.error( - "Please enter a valid number greater than 0 for shift positions" - ); - return; - } - - //data format for back - const modifiedEventDatesOnly = formData.eventDates.map( - (eventDate) => eventDate.date - ); - - const modifiedShifts = formData.shifts.map((shift) => { - const correspondingEvent = formData.eventDates.find( - (eventDate) => eventDate.listId === shift.parentListIdForShift - ); - - const shiftStart = correspondingEvent.date.replace( - "T00:00", - "T" + shift.shiftStart - ); - const shiftEnd = correspondingEvent.date.replace( - "T00:00", - "T" + shift.shiftEnd - ); - - return { - ...shift, - shiftStart, - shiftEnd, - shiftPositions: Number(shift.shiftPositions), - }; - }); - - // console.log('------------------'); - // console.log('modified shifts for back: ', modifiedShifts); - // console.log('modified eventDatesOnly for back: ', modifiedEventDatesOnly); - - const formDataForBack = { - ...formData, - eventDates: modifiedEventDatesOnly, - shifts: modifiedShifts, - eventId, - }; - console.log("formData from handleSubmitForm: ", formDataForBack); - - try { - const updatedEvent = await updateEventInfo(formDataForBack).unwrap(); - - console.log("updatedEvent from front ", updatedEvent); - } catch (err) { - toast.error("Error updating event"); - console.error("error from updatedEventInfo: ", err); - } - }; - - //make use - const createdAt = new Date(event.createdAt).toLocaleString("en-US", { - day: "numeric", - month: "long", - year: "numeric", - hour: "numeric", - minute: "numeric", - second: "numeric", - }); - const updatedAt = new Date(event.updatedAt).toLocaleString("en-US", { - day: "numeric", - month: "long", - year: "numeric", - hour: "numeric", - minute: "numeric", - second: "numeric", - }); - return ( - <Container> - <Stack gap={3}> - <Form> - <h2>Event Form</h2> - - <FloatingLabel - controlId="eventFormInput" - label="Event Name" - className="mb-3" - > - <Form.Control - type="text" - value={formData.eventName} - onChange={(e) => - setFormData({ ...formData, eventName: e.target.value }) - } - placeholder="Enter event name" - /> - </FloatingLabel> - - <FloatingLabel - controlId="eventFormInput" - label="Event Description" - className="mb-3" - > - <Form.Control - type="text" - value={formData.eventDescription} - onChange={(e) => - setFormData({ ...formData, eventDescription: e.target.value }) - } - placeholder="Enter event description" - /> - </FloatingLabel> - - <FloatingLabel - controlId="eventFormInput" - label="Event Venue" - className="mb-3" - > - <Form.Select - value={formData.eventVenue} - name="venueFilter" - onChange={(e) => - setFormData({ ...formData, eventVenue: e.target.value }) - } - > - <option value={""}> - Select Campus -</option> - <option value={"Casa Loma"}>Casa Loma</option> - <option value={"St James"}>St James</option> - <option value={"Waterfront"}>Waterfront</option> - <option value={"External"}>External</option> - </Form.Select> - </FloatingLabel> - - <br></br> - - <Row> - <Col>Event Dates</Col> - <Col xs={1}></Col> - - <Col>ShiftStart Time</Col> - <Col>ShiftEnd Time</Col> - <Col>Shift Posituios</Col> - <Col xs={1}></Col> - </Row> - <Row> - <br></br> - </Row> - - {renderEventDatesAndShifts} - <Row> - <Col> - <Button - type="button" - variant="warning" - onClick={addEventDateAndShift} - > - Add Event And Shift - </Button> - </Col> - </Row> - - <br></br> - <Button type="submit" variant="warning" onClick={handleFormSubmit}> - Edit Event - </Button> - - <Row> - <br></br> - </Row> - </Form> - </Stack> - </Container> - ); -}; - -export default EditEventForm_One; diff --git a/client/src/features/event/form/EditEventForm_Two.jsx b/client/src/features/event/form/EditEventForm_Two.jsx deleted file mode 100644 index 61ef5e3..0000000 --- a/client/src/features/event/form/EditEventForm_Two.jsx +++ /dev/null @@ -1,483 +0,0 @@ -import React, { useState, useEffect } from "react"; -import AddEventDateAndShiftTime from "./AddEventDateAndShiftTime"; - -import { toast } from "react-toastify"; -import { useNavigate } from "react-router-dom"; -import isValidNumberInput from "../../../helpers/isValidNumberInput"; -import { - Container, - Row, - Form, - Stack, - Col, - FloatingLabel, - Button, -} from "react-bootstrap"; -import { usePutUpdateEventInfoMutation } from "../eventsApiSlice"; - -//event obj from back - localEventDates+T00:00 -const EditEventForm_Two = ({ event, eventId }) => { - const [listId, setListId] = useState(1); - const [shiftListId, setShiftListId] = useState(1 * 100); - - const navigate = useNavigate(); - - const [formData, setFormData] = useState({ - eventName: "", - eventVenue: "", - eventDates: [], - eventDescription: "", - shifts: [], - }); - - const [ - updateEventInfo, - { isSuccess: isUpdateSuccess, isLoading: isUpdateLoading }, - ] = usePutUpdateEventInfoMutation(); - - //reformat data for front rendering - //local dates format from back using date-fns-tz format(): yyyy-MM-dd HH:mm TZ - - useEffect(() => { - if (event) { - let initialListId = listId - 1; - let initialShiftListId = shiftListId - 1; - - //non dup arr - dup arr needed for rendering - //use event.shifts.map(shift => shift.localShiftStart.split(' ')[0]) to get arr of dates and concat T00:00 - - //try as fork - const dupEventDatesWithListIdsAndDates = event.shifts.map((shift) => { - initialListId++; - const date = shift.localShiftStart.split(" ")[0].concat("T00:00"); - - return { - listId: initialListId, - date, - }; - }); - - //verified - // console.log( - // "dupEventDatesWithListIdsAndDates: ", - // dupEventDatesWithListIdsAndDates - // ); - - if (!Array.isArray(dupEventDatesWithListIdsAndDates)) { - throw new Error("Must be an arr for shiftsWithListIds"); - } - - const correspondingEventsArr = []; - - //shfitListIds && parentListIdForShift - const shiftsWithListIds = event.shifts.map((shift) => { - - - const splitLocalShiftStart = shift.localShiftStart.split(" "); - const splitLocalShiftEnd = shift.localShiftEnd.split(" "); - - //for finding correspodingEvent only - const shiftStartDate = splitLocalShiftStart[0]; - - //HH:mm format sliced from 2021-08-01 09:00:00 ZZZ - const shiftStartTime = splitLocalShiftStart[1].slice(0, 5); - const shiftEndTime = splitLocalShiftEnd[1].slice(0, 5); - - initialShiftListId++; - - //eventDateObj.date = date + T00:00 - - //src of err - correspondingEvent.listId would be the same for same date shifts if used find - let correspondingEvent = dupEventDatesWithListIdsAndDates.find( - (eventDateObj) => eventDateObj.date.includes(shiftStartDate) - ); - - const foundCorrespondingEvent = correspondingEventsArr.find( - (eventDateObj) => - eventDateObj.date === correspondingEvent.date - && - eventDateObj.listId === correspondingEvent.listId - ); - - if (foundCorrespondingEvent) { - - correspondingEvent = dupEventDatesWithListIdsAndDates - .filter( - (eventDateObj) => - eventDateObj.listId !== foundCorrespondingEvent.listId - ) - .find((eventDateObj) => eventDateObj.date.includes(shiftStartDate)); - } - - else{ - correspondingEventsArr.push(correspondingEvent); - - } - - - return { - //to keep shiftId unchanged when sending submitting to back - shiftId: shift._id, - - shiftListId: initialShiftListId, - shiftStart: shiftStartTime, - shiftEnd: shiftEndTime, - shiftPositions: shift.shiftPositions.toString(), - parentListIdForShift: correspondingEvent.listId, - }; - }); - - setFormData({ - ...formData, - eventName: event.eventName, - eventVenue: event.eventVenue, - eventDescription: event.eventDescription, - eventDates: dupEventDatesWithListIdsAndDates, - shifts: shiftsWithListIds, - }); - - //only works with + 1 - setListId(initialListId + 1); - setShiftListId(initialShiftListId + 1); - } - }, []); - - useEffect(() => { - if (isUpdateSuccess) { - setFormData({ - eventName: "", - eventVenue: "", - eventDates: [], - eventDescription: "", - shifts: [], - }); - - toast.success("Event updated successfully"); - - navigate("/dash/events"); - } - }, [isUpdateSuccess, navigate]); - - //onClick addEventDateAndSHift, both eventDates and shifts changes - const addEventDateAndShift = () => { - setFormData({ - ...formData, - eventDates: [ - ...formData.eventDates, - { - listId: listId, - date: "", - }, - ], - shifts: [ - ...formData.shifts, - { - shiftListId: shiftListId, - shiftStart: "", - shiftEnd: "", - shiftPositions: "", - parentListIdForShift: listId, - }, - ], - }); - - console.log( - "addEventandShift & parentListIdForShift listID: ", - listId, - " | shiftListId: ", - shiftListId - ); - - setListId(listId + 1); - setShiftListId(shiftListId + 1); - }; - - const updateEvent = (eventDateObj) => { - const updatedEventDates = formData.eventDates.map((eventDate) => { - if (eventDate.listId === eventDateObj.listId) { - return eventDateObj; - } - - //unmodified obj - return eventDate; - }); - - setFormData({ ...formData, eventDates: updatedEventDates }); - }; - - const updateShift = (shiftObj) => { - const updatedShiftTimes = formData.shifts.map((shift) => { - if (shiftObj.shiftListId === shift.shiftListId) { - return shiftObj; - } - - return shift; - }); - - setFormData({ ...formData, shifts: updatedShiftTimes }); - }; - - const removeEventAndShift = (eventDateObj) => { - console.log("tbremoved evetnDateObj: ", eventDateObj); - if (formData.eventDates.length === 1) { - console.log("min 1 date/shift"); - return null; - } - - //Does NOT handle duplicate eventDates - const updatedEventDates = formData.eventDates.filter( - (eventDate) => eventDate.listId !== eventDateObj.listId - ); - - //[]remove shifts of that event too - for bcknd - console.log("updatedShiftTimes w/o removing: ", formData.shifts); - - const updatedShiftTimes = formData.shifts.filter( - (shift) => shift.parentListIdForShift !== eventDateObj.listId - ); - - setFormData({ - ...formData, - - eventDates: updatedEventDates, - shifts: updatedShiftTimes, - }); - - console.log("updatedEventDates after removing: ", updatedEventDates); - console.log("updatedShiftTimes after removing: ", updatedShiftTimes); - }; - - //base this on shifts to display same date shifts - - // const renderEventDatesAndShifts = formData.eventDates.map((eventDate) => { - - // console.log('eventDate from renderEventDatesAndShifts: ', eventDate); - - // const correspondingShift = formData.shifts.find(shift => shift.parentListIdForShift === eventDate.listId); - - // //undefined correspondingShift - // console.log('correspondingShift: ', correspondingShift); - - // return ( - - // <AddEventDateAndShiftTime - // key={eventDate.listId} - - // listId={listId-1} - // eventDate={eventDate} - - // removeEventAndShift={removeEventAndShift} - // updateEvent={updateEvent} - // updateShift={updateShift} - // shift={correspondingShift} - // shiftListId={shiftListId - 1} - - // eventId={eventId} - // /> - // ) - - // }) - - const renderEventDatesAndShifts = formData.shifts.map((shift) => { - //shift.shiftStart and shiftEnd here are jsut HH:mm - const correspondingEvent = formData.eventDates.find( - (eventDateObj) => eventDateObj.listId === shift.parentListIdForShift - ); - - // console.log("shift from renderEventDatesAndShifts: ", shift); - // console.log("correspondingEvent for renderEventDatesAndShifts: ", correspondingEvent); - return ( - <AddEventDateAndShiftTime - //potential listId duplicated - // key={correspondingEvent.listId} - key={shift.shiftListId} - listId={listId - 1} - eventDate={correspondingEvent} - removeEventAndShift={removeEventAndShift} - updateEvent={updateEvent} - updateShift={updateShift} - shift={shift} - shiftListId={shiftListId - 1} - eventId={eventId} - /> - ); - }); - - const handleFormSubmit = async (e) => { - e.preventDefault(); - - //mini input validation - if ( - formData.shifts.some((shift) => !isValidNumberInput(shift.shiftPositions)) - ) { - toast.error( - "Please enter a valid number greater than 0 for shift positions" - ); - return; - } - - //data format for back - const modifiedEventDatesOnly = formData.eventDates.map( - (eventDate) => eventDate.date - ); - - const modifiedShifts = formData.shifts.map((shift) => { - const correspondingEvent = formData.eventDates.find( - (eventDate) => eventDate.listId === shift.parentListIdForShift - ); - - const shiftStart = correspondingEvent.date.replace( - "T00:00", - "T" + shift.shiftStart - ); - const shiftEnd = correspondingEvent.date.replace( - "T00:00", - "T" + shift.shiftEnd - ); - - return { - ...shift, - shiftStart, - shiftEnd, - shiftPositions: Number(shift.shiftPositions), - }; - }); - - // console.log('------------------'); - // console.log('modified shifts for back: ', modifiedShifts); - // console.log('modified eventDatesOnly for back: ', modifiedEventDatesOnly); - - const formDataForBack = { - ...formData, - eventDates: modifiedEventDatesOnly, - shifts: modifiedShifts, - eventId, - }; - console.log("formData from handleSubmitForm: ", formDataForBack); - - try { - const updatedEvent = await updateEventInfo(formDataForBack).unwrap(); - - console.log("updatedEvent from front ", updatedEvent); - } catch (err) { - toast.error("Error updating event"); - console.error("error from updatedEventInfo: ", err); - } - }; - - //make use - const createdAt = new Date(event.createdAt).toLocaleString("en-US", { - day: "numeric", - month: "long", - year: "numeric", - hour: "numeric", - minute: "numeric", - second: "numeric", - }); - const updatedAt = new Date(event.updatedAt).toLocaleString("en-US", { - day: "numeric", - month: "long", - year: "numeric", - hour: "numeric", - minute: "numeric", - second: "numeric", - }); - return ( - <Container> - <Stack gap={3}> - <Form> - <h2>Event Form</h2> - - <FloatingLabel - controlId="eventFormInput" - label="Event Name" - className="mb-3" - > - <Form.Control - type="text" - value={formData.eventName} - onChange={(e) => - setFormData({ ...formData, eventName: e.target.value }) - } - placeholder="Enter event name" - /> - </FloatingLabel> - - <FloatingLabel - controlId="eventFormInput" - label="Event Description" - className="mb-3" - > - <Form.Control - type="text" - value={formData.eventDescription} - onChange={(e) => - setFormData({ ...formData, eventDescription: e.target.value }) - } - placeholder="Enter event description" - /> - </FloatingLabel> - - <FloatingLabel - controlId="eventFormInput" - label="Event Venue" - className="mb-3" - > - <Form.Select - value={formData.eventVenue} - name="venueFilter" - onChange={(e) => - setFormData({ ...formData, eventVenue: e.target.value }) - } - > - <option value={""}> - Select Campus -</option> - <option value={"Casa Loma"}>Casa Loma</option> - <option value={"St James"}>St James</option> - <option value={"Waterfront"}>Waterfront</option> - <option value={"External"}>External</option> - </Form.Select> - </FloatingLabel> - - <br></br> - - <Row> - <Col>Event Dates</Col> - <Col xs={1}></Col> - - <Col>ShiftStart Time</Col> - <Col>ShiftEnd Time</Col> - <Col>Shift Posituios</Col> - <Col xs={1}></Col> - </Row> - <Row> - <br></br> - </Row> - - {renderEventDatesAndShifts} - <Row> - <Col> - <Button - type="button" - variant="warning" - onClick={addEventDateAndShift} - > - Add Event And Shift - </Button> - </Col> - </Row> - - <br></br> - <Button type="submit" variant="warning" onClick={handleFormSubmit}> - Edit Event - </Button> - - <Row> - <br></br> - </Row> - </Form> - </Stack> - </Container> - ); -}; - -export default EditEventForm_Two; diff --git a/client/src/features/volun/VolunteerHeader.jsx b/client/src/features/volun/VolunteerHeader.jsx index caadb83..bd2186e 100644 --- a/client/src/features/volun/VolunteerHeader.jsx +++ b/client/src/features/volun/VolunteerHeader.jsx @@ -1,5 +1,5 @@ import React from "react"; -import { Outlet} from "react-router-dom"; +import { Outlet } from "react-router-dom"; import VolunSearchBar from "./search/VolunSearchBar"; import VolunSort from "./sort/VolunSort"; import useAuth from "../../hooks/useAuth"; @@ -12,8 +12,8 @@ import useFindingQueryDisplayHook from "../../hooks/useFindingQueryDisplayHook"; const VolunteerHeader = () => { const { isAdmin, role } = useAuth(); - - const {findingQuery, showFindingQuery, setFindingQuery} = useFindingQueryDisplayHook() + const { findingQuery, showFindingQuery, setFindingQuery } = + useFindingQueryDisplayHook(); const findingQueryDisplay = showFindingQuery ? ( <Stack gap={2} direction="horizontal" className="d-flex flex-wrap"> @@ -51,7 +51,7 @@ const VolunteerHeader = () => { <Row className="my-2"> <Col> <LinkContainer to={"/dash/volunteers"}> - <Button variant="primary">Back to Volunteers List</Button> + <Button variant="warning">Back to Volunteers List</Button> </LinkContainer> </Col> </Row> From 6fb80c968ba81c95c60e7adb6dfb6963cf808403 Mon Sep 17 00:00:00 2001 From: Zen-cronic <83657429+Zen-cronic@users.noreply.github.com> Date: Wed, 22 Nov 2023 18:54:30 -0500 Subject: [PATCH 4/4] fix: public or private navigate hook --- .../src/hooks/usePublicOrPrivateNavigate.jsx | 3 ++- server/src/routes/eventsRoutes.js | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/client/src/hooks/usePublicOrPrivateNavigate.jsx b/client/src/hooks/usePublicOrPrivateNavigate.jsx index 7f6c37e..05b3a72 100644 --- a/client/src/hooks/usePublicOrPrivateNavigate.jsx +++ b/client/src/hooks/usePublicOrPrivateNavigate.jsx @@ -5,10 +5,11 @@ const usePublicOrPrivateNavigate = () => { const authObj = useAuth(); + console.log('authObj from customHook: ', authObj); const navigate = useNavigate(); const navigateFn = (publicPathname) => { - if (!Object.values(authObj).every((val) => val)) { + if (Object.values(authObj).every((val) => !Boolean(val))) { navigate(publicPathname); } else { navigate(`/dash${publicPathname}`); diff --git a/server/src/routes/eventsRoutes.js b/server/src/routes/eventsRoutes.js index 690180d..90dc6cd 100644 --- a/server/src/routes/eventsRoutes.js +++ b/server/src/routes/eventsRoutes.js @@ -2,26 +2,31 @@ const express = require("express"); const eventsControllers = require("../controllers/eventsControllers"); const { ROLES } = require("../config/roles"); const verifyRole = require("../middleware/verifyRole"); +const verifyJWT = require("../middleware/verifyJWT"); const router = express.Router(); - router .route("/") - - .get(eventsControllers.getAllEvents) - .post(verifyRole(ROLES.ADMIN), eventsControllers.createNewEvent) - .put(verifyRole(ROLES.ADMIN), eventsControllers.updateEventInfo) - .delete(verifyRole(ROLES.ADMIN), eventsControllers.deleteEvent); + .get(eventsControllers.getAllEvents) + .post(verifyJWT, verifyRole(ROLES.ADMIN), eventsControllers.createNewEvent) + .put(verifyJWT, verifyRole(ROLES.ADMIN), eventsControllers.updateEventInfo) + .delete(verifyJWT, verifyRole(ROLES.ADMIN), eventsControllers.deleteEvent); // event/refresh - taskscheduling router.route("/:eventId").get(eventsControllers.getEventById); //only admin -router.route("/volunteers").post(verifyRole(ROLES.ADMIN),eventsControllers.getSignedUpVolunteers); +router + .route("/volunteers") + .post( + verifyJWT, + verifyRole(ROLES.ADMIN), + eventsControllers.getSignedUpVolunteers + ); router.route("/search").post(eventsControllers.searchEvents); router.route("/sort").post(eventsControllers.sortEvents);