diff --git a/.gitignore b/.gitignore index 59e66552..2984cbcf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# MacOS autogenerated files +.DS_Store + # Logs logs *.log diff --git a/backend/src/app.ts b/backend/src/app.ts index 09fae52b..a2f604de 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -8,6 +8,9 @@ import express, { NextFunction, Request, Response } from "express"; import { isHttpError } from "http-errors"; import subscriberRoutes from "src/routes/subscriber"; import memberRoutes from "src/routes/members"; +import backgroundImageRoutes from "src/routes/background_images"; +import eventDetailsRoutes from "./routes/eventDetails"; +import volunteerDetailsRoutes from "./routes/volunteerDetails"; import testimonialRoutes from "src/routes/testimonial"; const app = express(); @@ -29,6 +32,10 @@ app.use( // Routes ( e.g. app.use("/api/task", taskRoutes); ) app.use("/api/subscribers", subscriberRoutes); app.use("/api/member", memberRoutes); +app.use("/api/BackgroundImage", backgroundImageRoutes); + +app.use("/api/eventDetails", eventDetailsRoutes); +app.use("/api/volunteerDetails", volunteerDetailsRoutes); app.use("/api/testimonial", testimonialRoutes); /** * Error handler; all errors thrown by server are handled here. diff --git a/backend/src/controllers/BackgroundImage.ts b/backend/src/controllers/BackgroundImage.ts new file mode 100644 index 00000000..e5bfb8e1 --- /dev/null +++ b/backend/src/controllers/BackgroundImage.ts @@ -0,0 +1,12 @@ +import { RequestHandler } from "express"; +import BackgroundImageModel from "src/models/BackgroundImage"; + +export const getBackgroundImages: RequestHandler = async (req, res, next) => { + const { page } = req.query; + try { + const images = await BackgroundImageModel.find({ page }); + res.status(200).json(images); + } catch (error) { + next(error); + } +}; diff --git a/backend/src/controllers/eventDetails.ts b/backend/src/controllers/eventDetails.ts new file mode 100644 index 00000000..b0af355d --- /dev/null +++ b/backend/src/controllers/eventDetails.ts @@ -0,0 +1,84 @@ +import { RequestHandler } from "express"; +import { validationResult } from "express-validator"; +import createHttpError from "http-errors"; +import EventDetails from "src/models/eventDetails"; +import validationErrorParser from "src/util/validationErrorParser"; + +export const getAllEventDetails: RequestHandler = async (req, res, next) => { + try { + const events = await EventDetails.find({}); + + if (!events) { + res.status(200).json({ message: "No events found." }); + } + res.status(200).json(events); + } catch (error) { + next(error); + } +}; + +export const getEventDetails: RequestHandler = async (req, res, next) => { + const { id } = req.params; + + try { + const eventDetails = await EventDetails.findById(id); + + if (!eventDetails) { + throw createHttpError(404, "Event not found."); + } + + res.status(200).json(eventDetails); + } catch (error) { + next(error); + } +}; + +export const createEventDetails: RequestHandler = async (req, res, next) => { + const errors = validationResult(req); + const { name, description, guidelines, date, location, imageURI } = req.body; + + try { + validationErrorParser(errors); + + const eventDetails = await EventDetails.create({ + name, + description, + guidelines, + date, + location, + imageURI, + }); + + res.status(201).json(eventDetails); + } catch (error) { + next(error); + } +}; + +export const updateEventDetails: RequestHandler = async (req, res, next) => { + const errors = validationResult(req); + const { id } = req.params; + + if (id !== req.body._id) { + // If the _id in the URL does not match the _id in the body, bad request + res.status(400); + } + + try { + validationErrorParser(errors); + + const eventDetails = await EventDetails.findByIdAndUpdate(id, req.body); + if (eventDetails === null) { + // No event found + res.status(404); + } + const updatedEventDetails = await EventDetails.findById(id); + if (updatedEventDetails === null) { + // No event found, something went wrong + res.status(404); + } + res.status(200).json(updatedEventDetails); + } catch (error) { + next(error); + } +}; diff --git a/backend/src/controllers/volunteeerDetails.ts b/backend/src/controllers/volunteeerDetails.ts new file mode 100644 index 00000000..8f497865 --- /dev/null +++ b/backend/src/controllers/volunteeerDetails.ts @@ -0,0 +1,64 @@ +import { RequestHandler } from "express"; +import VolunteerDetails from "../models/volunteerDetails"; + +export const getVolunteer: RequestHandler = async (req, res, next) => { + const { id } = req.params; + + try { + const volunteer = await VolunteerDetails.findById(id); + + if (!volunteer) { + return res.status(404).json({ error: "Volunteer not found." }); + } + + res.status(200).json(volunteer); + } catch (error) { + next(error); + } +}; + +export const getAllVolunteers: RequestHandler = async (req, res, next) => { + try { + const volunteers = await VolunteerDetails.find({}); + + if (!volunteers || volunteers.length === 0) { + return res.status(200).json({ message: "No volunteers yet!" }); + } + + res.status(200).json(volunteers); + } catch (error) { + next(error); + } +}; + +export const addVolunteer: RequestHandler = async (req, res, next) => { + // Extract volunteer data from the request body + const volunteerData = req.body; + + try { + // Check if any of the required fields are missing + const requiredProperties = [ + "first_name", + "last_name", + "email", + "phone", + "signed_up_for_updates", + ]; + + for (const prop of requiredProperties) { + if (!Object.prototype.hasOwnProperty.call(volunteerData, prop)) { + return res + .status(400) + .json({ error: `Missing property: ${prop}. Please provide all required fields.` }); + } + } + + // Create a new volunteer using the extracted data + const newVolunteer = await VolunteerDetails.create(volunteerData); + + // Respond with the newly created volunteer + return res.status(201).json(newVolunteer); + } catch (error) { + next(error); + } +}; diff --git a/backend/src/models/BackgroundImage.ts b/backend/src/models/BackgroundImage.ts new file mode 100644 index 00000000..40375667 --- /dev/null +++ b/backend/src/models/BackgroundImage.ts @@ -0,0 +1,15 @@ +import { InferSchemaType, Schema, model } from "mongoose"; + +export enum BackgroundImagePages { + TEAM = "TEAM", + HOME = "HOME", +} + +const backgroundImageSchema = new Schema({ + imageURI: { type: String, required: true }, + page: { type: String, required: true }, +}); + +type BackgroundImage = InferSchemaType; + +export default model("BackgroundImage", backgroundImageSchema); diff --git a/backend/src/models/eventDetails.ts b/backend/src/models/eventDetails.ts new file mode 100644 index 00000000..1c18a60c --- /dev/null +++ b/backend/src/models/eventDetails.ts @@ -0,0 +1,16 @@ +import { InferSchemaType, Schema, model } from "mongoose"; + +const eventDetailsSchema = new Schema({ + name: { type: String, required: true }, + description: { type: String, required: true }, + guidelines: { type: String, required: true }, + date: { type: String, required: true }, + location: { type: String, required: true }, + imageURI: { type: String, required: true }, // TODO: Change this if necessary + // empty by default, stores _id of volunteers + volunteers: { type: [String], required: false, default: [] }, +}); + +type EventDetails = InferSchemaType; + +export default model("EventDetails", eventDetailsSchema); diff --git a/backend/src/models/volunteerDetails.ts b/backend/src/models/volunteerDetails.ts new file mode 100644 index 00000000..a029b65e --- /dev/null +++ b/backend/src/models/volunteerDetails.ts @@ -0,0 +1,28 @@ +import { InferSchemaType, Schema, model } from "mongoose"; + +const volunteerDetailsSchema = new Schema({ + first_name: { + type: String, + required: true, + }, + last_name: { + type: String, + required: true, + }, + email: { + type: String, + required: true, + }, + phone: { + type: String, + required: true, + }, + signed_up_for_updates: { + type: Boolean, + default: false, + }, +}); + +type VolunteerDetails = InferSchemaType; + +export default model("VolunteerDetails", volunteerDetailsSchema); diff --git a/backend/src/routes/background_images.ts b/backend/src/routes/background_images.ts new file mode 100644 index 00000000..9eba180c --- /dev/null +++ b/backend/src/routes/background_images.ts @@ -0,0 +1,8 @@ +import express from "express"; +import * as BackgroundImageController from "src/controllers/BackgroundImage"; + +const router = express.Router(); + +router.get("/get", BackgroundImageController.getBackgroundImages); + +export default router; diff --git a/backend/src/routes/eventDetails.ts b/backend/src/routes/eventDetails.ts new file mode 100644 index 00000000..bd733e83 --- /dev/null +++ b/backend/src/routes/eventDetails.ts @@ -0,0 +1,20 @@ +import express from "express"; +import * as EventDetailsController from "src/controllers/eventDetails"; +import * as EventDetailsValidator from "src/validators/eventDetails"; + +const router = express.Router(); + +router.get("/", EventDetailsController.getAllEventDetails); +router.get("/:id", EventDetailsValidator.getEventDetails, EventDetailsController.getEventDetails); +router.put( + "/:id", // getEventDetails validator works to just check ID + EventDetailsValidator.getEventDetails, + EventDetailsController.updateEventDetails, +); +router.post( + "/", + EventDetailsValidator.createEventDetails, + EventDetailsController.createEventDetails, +); + +export default router; diff --git a/backend/src/routes/volunteerDetails.ts b/backend/src/routes/volunteerDetails.ts new file mode 100644 index 00000000..79eae2dc --- /dev/null +++ b/backend/src/routes/volunteerDetails.ts @@ -0,0 +1,11 @@ +import express from "express"; +import { getVolunteer, getAllVolunteers, addVolunteer } from "../controllers/volunteeerDetails"; + +const volunteerDetailsRoutes = express.Router(); + +// Define routes +volunteerDetailsRoutes.get("/:id", getVolunteer); +volunteerDetailsRoutes.get("/", getAllVolunteers); +volunteerDetailsRoutes.post("/", addVolunteer); + +export default volunteerDetailsRoutes; diff --git a/backend/src/validators/eventDetails.ts b/backend/src/validators/eventDetails.ts new file mode 100644 index 00000000..91ec8450 --- /dev/null +++ b/backend/src/validators/eventDetails.ts @@ -0,0 +1,65 @@ +import { body } from "express-validator"; + +const makeIDValidator = () => + body("_id") + .exists() + .withMessage("_id is required") + .bail() + .isMongoId() + .withMessage("_id must be a MongoDB object ID"); +const makeNameValidator = () => + body("name") + .exists() + .withMessage("name is required") + .bail() + .isString() + .withMessage("name must be a string"); +const makeDescriptionValidator = () => + body("description") + .exists() + .withMessage("description is required") + .bail() + .isString() + .withMessage("description must be a string"); +const makeGuidlinesValidator = () => + body("guidelines") + .exists() + .withMessage("guidelines is required") + .bail() + .isString() + .withMessage("guidelines must be a string"); +const makeDateValidator = () => + body("date") + .exists() + .withMessage("date is required") + .bail() + .isString() + .withMessage("date must be a string"); +const makeLocationValidator = () => + body("location") + .exists() + .withMessage("location is required") + .bail() + .isString() + .withMessage("location must be a string"); +const makeImageURIValidator = () => + body("imageURI") + .exists() + .withMessage("imageURI is required") + .bail() + .isString() + .withMessage("imageURI must be a string") + .bail() + .isURL() + .withMessage("imageURI must be a URL"); + +export const createEventDetails = [ + makeNameValidator(), + makeDescriptionValidator(), + makeGuidlinesValidator(), + makeDateValidator(), + makeLocationValidator(), + makeImageURIValidator(), +]; + +export const getEventDetails = [makeIDValidator()]; diff --git a/frontend/next.config.js b/frontend/next.config.js index 2bc45431..151cdec6 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -1,7 +1,14 @@ /** @type {import('next').NextConfig} */ const nextConfig = { images: { - domains: ["images.fineartamerica.com"], + domains: [ + "images.fineartamerica.com", + "kagi.com", + "localhost", + "i.imgur.com", + "images.unsplash.com", + "plus.unsplash.com", + ], }, }; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c077296c..62ea5bf2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,9 +8,15 @@ "name": "frontend", "version": "0.1.0", "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.0", + "@mui/icons-material": "^5.15.12", + "@mui/material": "^5.15.12", + "material-symbols": "^0.16.0", "next": "14.0.4", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "react-material-symbols": "^4.3.1" }, "devDependencies": { "@types/node": "^20", @@ -51,11 +57,190 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/runtime": { "version": "7.23.9", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -63,6 +248,152 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/types": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/react": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", + "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/styled": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", + "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -141,6 +472,40 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dependencies": { + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "dependencies": { + "@floating-ui/dom": "^1.6.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -288,6 +653,261 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.38", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.38.tgz", + "integrity": "sha512-AsjD6Y1X5A1qndxz8xCcR8LDqv31aiwlgWMPxFAX/kCKiIGKlK65yMeVZ62iQr/6LBz+9hSKLiD1i4TZdAHKcQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.13", + "@mui/utils": "^5.15.12", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.15.12", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.12.tgz", + "integrity": "sha512-brRO+tMFLpGyjEYHrX97bzqeF6jZmKpqqe1rY0LyIHAwP6xRVzh++zSecOQorDOCaZJg4XkGT9xfD+RWOWxZBA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "5.15.12", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.12.tgz", + "integrity": "sha512-3BXiDlOd3AexZoEXa/VqpIpVIvosCzjLHsdMWzKMXbZdnBiJjmb9ECdqfjn5SpTClO49qvkKLhkTqdBH3fSFGw==", + "dependencies": { + "@babel/runtime": "^7.23.9" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "5.15.12", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.12.tgz", + "integrity": "sha512-vXJGg6KNKucsvbW6l7w9zafnpOp0CWc0Wx4mDykuABTpQ5QQBnZxP7+oB4yAS1hDZQ1WobbeIl0CjxK4EEahkA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.38", + "@mui/core-downloads-tracker": "^5.15.12", + "@mui/system": "^5.15.12", + "@mui/types": "^7.2.13", + "@mui/utils": "^5.15.12", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/@mui/private-theming": { + "version": "5.15.12", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.12.tgz", + "integrity": "sha512-cqoSo9sgA5HE+8vZClbLrq9EkyOnYysooepi5eKaKvJ41lReT2c5wOZAeDDM1+xknrMDos+0mT2zr3sZmUiRRA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.12", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.15.11", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.11.tgz", + "integrity": "sha512-So21AhAngqo07ces4S/JpX5UaMU2RHXpEA6hNzI6IQjd/1usMPxpgK8wkGgTe3JKmC2KDmH8cvoycq5H3Ii7/w==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.15.12", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.12.tgz", + "integrity": "sha512-/pq+GO6yN3X7r3hAwFTrzkAh7K1bTF5r8IzS79B9eyKJg7v6B/t4/zZYMR6OT9qEPtwf6rYN2Utg1e6Z7F1OgQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.15.12", + "@mui/styled-engine": "^5.15.11", + "@mui/types": "^7.2.13", + "@mui/utils": "^5.15.12", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.13", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz", + "integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.15.12", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.12.tgz", + "integrity": "sha512-8SDGCnO2DY9Yy+5bGzu00NZowSDtuyHP4H8gunhHGQoIlhlY2Z3w64wBzAOLpYw/ZhJNzksDTnS/i8qdJvxuow==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.11", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/@next/env": { "version": "14.0.4", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", @@ -482,6 +1102,15 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.7.2.tgz", @@ -517,17 +1146,20 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "dev": true + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" }, "node_modules/@types/react": { "version": "18.2.48", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -543,11 +1175,18 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "dev": true + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" }, "node_modules/@types/semver": { "version": "7.5.6", @@ -1064,6 +1703,20 @@ "dequal": "^2.0.3" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1161,7 +1814,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -1254,6 +1906,14 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", + "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1287,6 +1947,34 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1316,8 +2004,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -1424,6 +2111,15 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1455,6 +2151,14 @@ "node": ">=10.13.0" } }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -1583,7 +2287,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -2180,6 +2883,11 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2278,7 +2986,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2538,7 +3245,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -2546,6 +3252,14 @@ "node": ">= 0.4" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/husky": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", @@ -2574,7 +3288,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2639,6 +3352,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, "node_modules/is-async-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", @@ -2710,7 +3428,6 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -3035,6 +3752,11 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3126,8 +3848,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/locate-path": { "version": "6.0.0", @@ -3170,6 +3891,11 @@ "node": "14 || >=16.14" } }, + "node_modules/material-symbols": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/material-symbols/-/material-symbols-0.16.0.tgz", + "integrity": "sha512-ZgX0TEQQnaAPvy9jSg6WEoBfkkL95URJ5dGHsqU9cIdXVB3K6/T+IH8l7E1ZAl4HFmaZ0hMPBBQz8DoBkoaygg==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3366,7 +4092,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3549,7 +4274,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -3557,6 +4281,23 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3587,8 +4328,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.10.1", @@ -3610,7 +4350,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -3824,7 +4563,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -3886,8 +4624,34 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-material-symbols": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-material-symbols/-/react-material-symbols-4.3.1.tgz", + "integrity": "sha512-2/CC8GptDz5GJdp10j1Piab/Dh105B/mDw/aSNi6mjydhjJnjEOC4UsYYX4HII00mmEuGjtRhZH0kkL5DsLWGQ==", + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } }, "node_modules/read-cache": { "version": "1.0.0", @@ -3933,8 +4697,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", @@ -3957,7 +4720,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -3974,7 +4736,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -4234,6 +4995,14 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -4448,6 +5217,11 @@ } } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -4486,7 +5260,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -4567,6 +5340,14 @@ "node": ">=0.8" } }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 3764245c..057ecabc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,9 +14,15 @@ "prepare": "cd .. && husky install .husky" }, "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.0", + "@mui/icons-material": "^5.15.12", + "@mui/material": "^5.15.12", + "material-symbols": "^0.16.0", "next": "14.0.4", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "react-material-symbols": "^4.3.1" }, "devDependencies": { "@types/node": "^20", diff --git a/frontend/public/carousel-images/home.png b/frontend/public/carousel-images/home.png new file mode 100644 index 00000000..f1db9f69 Binary files /dev/null and b/frontend/public/carousel-images/home.png differ diff --git a/frontend/public/carousel-images/home2.jpeg b/frontend/public/carousel-images/home2.jpeg new file mode 100644 index 00000000..9a54baf6 Binary files /dev/null and b/frontend/public/carousel-images/home2.jpeg differ diff --git a/frontend/public/carousel-images/impact_bg.png b/frontend/public/carousel-images/impact_bg.png new file mode 100644 index 00000000..584eba74 Binary files /dev/null and b/frontend/public/carousel-images/impact_bg.png differ diff --git a/frontend/public/event-card.png b/frontend/public/event-card.png new file mode 100644 index 00000000..fe7fa4bf Binary files /dev/null and b/frontend/public/event-card.png differ diff --git a/frontend/public/eventSignupGraphic.svg b/frontend/public/eventSignupGraphic.svg new file mode 100644 index 00000000..e30ad248 --- /dev/null +++ b/frontend/public/eventSignupGraphic.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/public/placeholderHeaderImage.png b/frontend/public/placeholderHeaderImage.png new file mode 100644 index 00000000..639f5de5 Binary files /dev/null and b/frontend/public/placeholderHeaderImage.png differ diff --git a/frontend/public/volunteerSuccess.svg b/frontend/public/volunteerSuccess.svg new file mode 100644 index 00000000..fe5c82ee --- /dev/null +++ b/frontend/public/volunteerSuccess.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/api/eventDetails.ts b/frontend/src/api/eventDetails.ts new file mode 100644 index 00000000..131e416a --- /dev/null +++ b/frontend/src/api/eventDetails.ts @@ -0,0 +1,112 @@ +import { get, handleAPIError, post, put } from "./requests"; + +import type { APIResult } from "./requests"; + +export type EventDetails = { + _id: string; + name: string; + description: string; + guidelines: string; + date: string; + location: string; + imageURI: string; + volunteers: string[]; +}; + +export async function getEventDetails(id: string): Promise> { + try { + const response = await get(`/api/eventDetails/${id}`); + const json = (await response.json()) as EventDetails; + return { success: true, data: json }; + } catch (error) { + return handleAPIError(error); + } +} + +export async function getAllEventDetails(): Promise> { + try { + const response = await get("/api/eventDetails"); + const json = (await response.json()) as EventDetails[]; + const events = []; + for (const ev of json) { + // put each event in the event array + events.push(ev); + } + return { success: true, data: events }; + } catch (error) { + return handleAPIError(error); + } +} + +type CreateEventDetailsRequest = { + name: string; + description: string; + guidelines: string; + date: string; + location: string; + imageURI: string; + // volunteers: string[]; when creating an event, no volunteers are added +}; + +export async function createEventDetails( + eventDetails: CreateEventDetailsRequest, +): Promise> { + try { + const response = await post("/api/eventDetails", eventDetails); + const json = (await response.json()) as EventDetails; + return { success: true, data: json }; + } catch (error) { + return handleAPIError(error); + } +} + +type UpdateEventDetailsRequest = { + _id: string; + name: string; + description: string; + guidelines: string; + date: string; + location: string; + imageURI: string; + volunteers: string[]; +}; + +export async function updateEventDetails( + eventDetails: UpdateEventDetailsRequest, +): Promise> { + try { + const id = eventDetails._id; + const response = await put(`/api/eventDetails/${id}`, eventDetails, { + "Content-Type": "application/json", + }); + const json = (await response.json()) as EventDetails; + return { success: true, data: json }; + } catch (error) { + return handleAPIError(error); + } +} + +// wrap updateEventDetails to push a volunteer ID to an event +export async function addVolunteerToEvent( + eventId: string, + volunteerId: string, +): Promise> { + try { + const result = await getEventDetails(eventId); + if (!result.success) { + // if result is not successful, return the result which is APIError + return result; + } + const oldEvent = result.data; + oldEvent.volunteers.push(volunteerId); + const response = await updateEventDetails(oldEvent); + + if (!response.success) { + // if response is not successful, return the response which is APIError + return response; + } + return { success: true, data: response.data }; + } catch (error) { + return handleAPIError(error); + } +} diff --git a/frontend/src/api/images.ts b/frontend/src/api/images.ts new file mode 100644 index 00000000..ba0e5715 --- /dev/null +++ b/frontend/src/api/images.ts @@ -0,0 +1,29 @@ +import { get, handleAPIError } from "./requests"; + +import type { APIResult } from "./requests"; + +export enum BackgroundImagePages { + TEAM = "TEAM", + HOME = "HOME", + MISSION = "MISSION", + TESTIMONIALS = "TESTIMONIALS", +} + +export type BackgroundImage = { + page: string; + imageURI: string; +}; + +export async function getBackgroundImages( + page: BackgroundImagePages, +): Promise> { + try { + const urlWithPage = `/api/BackgroundImage/get?page=${encodeURIComponent(page)}`; + const response = await get(urlWithPage); + const json = (await response.json()) as BackgroundImage[]; + console.log(json); + return { success: true, data: json }; + } catch (error) { + return handleAPIError(error); + } +} diff --git a/frontend/src/api/requests.ts b/frontend/src/api/requests.ts index 9248c658..ec10c54b 100644 --- a/frontend/src/api/requests.ts +++ b/frontend/src/api/requests.ts @@ -26,7 +26,6 @@ async function fetchRequest( headers: Record, ): Promise { const hasBody = body !== undefined; - const newHeaders = { ...headers }; if (hasBody) { newHeaders["Content-Type"] = "application/json"; diff --git a/frontend/src/api/volunteerDetails.ts b/frontend/src/api/volunteerDetails.ts new file mode 100644 index 00000000..b611721d --- /dev/null +++ b/frontend/src/api/volunteerDetails.ts @@ -0,0 +1,8 @@ +export type VolunteerDetails = { + _id: string; + first_name: string; + last_name: string; + email: string; + phone: string; + signed_up_for_updates: boolean; +}; diff --git a/frontend/src/app/events/[eventId]/page.module.css b/frontend/src/app/events/[eventId]/page.module.css new file mode 100644 index 00000000..1878eff3 --- /dev/null +++ b/frontend/src/app/events/[eventId]/page.module.css @@ -0,0 +1,27 @@ +@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wdth,wght@0,75..100,300..800;1,75..100,300..800&family=Roboto+Slab:wght@100..900&display=swap"); + +/* Button */ +.button { + width: max-content; + height: 48px; + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + + background: var(--color-primary-purple); + border-radius: 4px; + padding: 12px 24px 8px; + + font: var(--font-body); + font-style: normal; + font-weight: 700; + font-size: 20px; + letter-spacing: 1px; + /* Center align text */ + text-align: center; + + /* Neutral/White */ + color: #ffffff; +} diff --git a/frontend/src/app/events/[eventId]/page.tsx b/frontend/src/app/events/[eventId]/page.tsx new file mode 100644 index 00000000..221224d2 --- /dev/null +++ b/frontend/src/app/events/[eventId]/page.tsx @@ -0,0 +1,104 @@ +"use client"; + +import Image from "next/image"; +import React, { useEffect, useState } from "react"; + +import { EventDetails, getEventDetails } from "../../../api/eventDetails"; +import SectionCard from "../../../components/SectionCard"; +import VolunteerPopup from "../../../components/VolunteerPopup"; + +import styles from "./page.module.css"; + +type Props = { + params: { eventId: string }; +}; + +export default function EventSignup({ params }: Props) { + const [event, setEvent] = useState(null); + const [popupOpen, setPopup] = useState(false); + + useEffect(() => { + // Fetch event details + getEventDetails(params.eventId) + .then((response) => { + if (response.success) { + setEvent(response.data); + } else { + alert(response.error); + } + }) + .catch((error) => { + alert(error); + }); + }, []); + + return ( +
+ +
+ Placeholder Header Image +
+ +
+
+
+ People with Boxes +
+
+
+

+ Details +

+

+ Date/Time: +

+

+ {event ? event.date : "Loading..."} +

+

+ Location: +

+

+ {event ? event.location : "Loading..."} +

+

+ Guidelines: +

+
+

+ {event ? event.guidelines : "Loading..."} +

+
+
+
+
+
+ +
+
+
+ ); +} diff --git a/frontend/src/app/events/page.module.css b/frontend/src/app/events/page.module.css new file mode 100644 index 00000000..62332f3a --- /dev/null +++ b/frontend/src/app/events/page.module.css @@ -0,0 +1,19 @@ +@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wdth,wght@0,75..100,300..800;1,75..100,300..800&family=Roboto+Slab:wght@100..900&display=swap"); + +.body { + max-width: 1440px; + margin: 0 auto; + height: auto; + padding: 0px 98px 136px; + background-color: #f9f9f9; + color: black; +} +.bodyTitle { + margin: 0; + padding: 64px 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: left; + gap: 24px; +} diff --git a/frontend/src/app/events/page.tsx b/frontend/src/app/events/page.tsx new file mode 100644 index 00000000..822b5667 --- /dev/null +++ b/frontend/src/app/events/page.tsx @@ -0,0 +1,47 @@ +"use client"; +import React, { useEffect, useState } from "react"; + +import EventsList from "../../components/EventsList"; + +import styles from "./page.module.css"; + +import { BackgroundImage, BackgroundImagePages, getBackgroundImages } from "@/api/images"; +import BackgroundHeader from "@/components/BackgroundHeader"; + +export default function UpcomingEvents() { + const [images, setImages] = useState([]); + + useEffect(() => { + getBackgroundImages(BackgroundImagePages.TEAM) + .then((result) => { + if (result.success) { + setImages(result.data); + } + }) + .catch((error) => { + alert(error); + }); + }, []); + + return ( +
+ image.imageURI)} + header="GET INVOLVED" + title="Upcoming Events" + description="Lorem ipsum dolor sit amet consectetur. Et vestibulum enim nunc ultrices. Donec blandit sollicitudin vitae integer mauris sed. Mattis duis id viverra suscipit morbi." + /> +
+
+

Volunteer With Us

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloremque necessitatibus + asperiores, optio quasi sit tempora in amet aut natus, similique enim explicabo id + expedita minima doloribus repellendus est? Quos, officia? +

+
+ +
+
+ ); +} diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 058fd3dc..83d647c2 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -6,10 +6,14 @@ --foreground-rgb: 0, 0, 0; --background-start-rgb: 255, 255, 255; --background-end-rgb: 255, 255, 255; + --font-small-subtitle: normal 700 28px/150% "Roboto Slab", sans-serif; + --font-body: normal 400 20px/120% "Open Sans", sans-serif; + --font-body-reg: normal 400 16px/150% "Open Sans", sans-serif; + --font-body-bold: normal 700 16px/150% "Open Sans", sans-serif; + --font-title-xl: normal 700 48px/150% "Roboto Slab", sans-serif; + --font-title-l: normal 700 32px/150% "Roboto Slab", sans-serif; + --font-title-m: normal 700 28px/150% "Roboto Slab", sans-serif; --color-primary-purple: #694c97; - - --font-small-subtitle: normal 700 28px/150% "Roboto Slab"; - --font-body: normal 400 20px/120% "Open Sans"; } @media (prefers-color-scheme: dark) { diff --git a/frontend/src/app/impact/page.module.css b/frontend/src/app/impact/page.module.css index af18f8f3..c826bd23 100644 --- a/frontend/src/app/impact/page.module.css +++ b/frontend/src/app/impact/page.module.css @@ -1,3 +1,5 @@ +@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wdth,wght@0,75..100,300..800;1,75..100,300..800&family=Roboto+Slab:wght@100..900&display=swap"); + .cards { display: flex; flex-direction: column; @@ -10,15 +12,12 @@ .backgroundImageContainer { position: relative; z-index: 0; - background-color: blue; } .whiteCardsContainer { position: absolute; - top: 557px; - /* left: 0; */ + top: 688px; z-index: 1; - /* background-color: pink; */ } .cardsBackground { diff --git a/frontend/src/app/impact/page.tsx b/frontend/src/app/impact/page.tsx index df077bb3..975f0827 100644 --- a/frontend/src/app/impact/page.tsx +++ b/frontend/src/app/impact/page.tsx @@ -1,19 +1,35 @@ -import React from "react"; +"use client"; +import React, { useEffect, useState } from "react"; import styles from "./page.module.css"; +import { BackgroundImage, BackgroundImagePages, getBackgroundImages } from "@/api/images"; import BackgroundHeader from "@/components/BackgroundHeader"; import WhiteCard from "@/components/WhiteCard"; export default function Impact() { + const [images, setImages] = useState([]); + + useEffect(() => { + getBackgroundImages(BackgroundImagePages.TEAM) + .then((result) => { + if (result.success) { + setImages(result.data); + } + }) + .catch((error) => { + alert(error); + }); + }, []); + return (
image.imageURI)} header="" title="Our Impact" - description="4FLOT is committed in preventing and ending homelessness, hunger and disparity in underprivileged communities." + description="4FLOT is committed in preventing and ending homelessness, hunger and disparity in underprivileged communities. " />
diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 3a728b37..d066bcd0 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -4,16 +4,32 @@ import type { Metadata } from "next"; import "./globals.css"; -import { HeaderBar } from "@/components/HeaderBar"; -import Footer from "@/components/footer"; - -// const inter = Inter({ subsets: ["latin"] }); +import Footer from "@/components/Footer"; +import HeaderBar from "@/components/HeaderBar"; +import HeaderBarSpace from "@/components/HeaderBarSpace"; +import "react-material-symbols/rounded"; export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", }; +// const openSans = Open_Sans({ +// subsets: ["latin"], +// display: "swap", +// variable: "--font-body", +// }); +// const inter = Inter({ +// subsets: ["latin"], +// display: "swap", +// variable: "--font-heading", +// }); +// const robotoSlab = Roboto_Slab({ +// subsets: ["latin"], +// display: "swap", +// variable: "--font-small-subtitle", +// }); + const openSans = Open_Sans({ subsets: ["latin"], display: "swap", @@ -22,7 +38,7 @@ const openSans = Open_Sans({ const inter = Inter({ subsets: ["latin"], display: "swap", - variable: "--font-heading", + variable: "--font-inter", }); const robotoSlab = Roboto_Slab({ subsets: ["latin"], @@ -32,17 +48,10 @@ const robotoSlab = Roboto_Slab({ export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - + + {children}