Skip to content

Commit

Permalink
Merge branch 'main' into feature/arnav/home-page
Browse files Browse the repository at this point in the history
  • Loading branch information
jennymar committed Mar 13, 2024
2 parents 86f9de1 + b3e4aa1 commit b5e95ae
Show file tree
Hide file tree
Showing 63 changed files with 2,547 additions and 308 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# MacOS autogenerated files
.DS_Store

# Logs
logs
*.log
Expand Down
7 changes: 7 additions & 0 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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.
Expand Down
12 changes: 12 additions & 0 deletions backend/src/controllers/BackgroundImage.ts
Original file line number Diff line number Diff line change
@@ -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);
}
};
84 changes: 84 additions & 0 deletions backend/src/controllers/eventDetails.ts
Original file line number Diff line number Diff line change
@@ -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);
}
};
64 changes: 64 additions & 0 deletions backend/src/controllers/volunteeerDetails.ts
Original file line number Diff line number Diff line change
@@ -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);
}
};
15 changes: 15 additions & 0 deletions backend/src/models/BackgroundImage.ts
Original file line number Diff line number Diff line change
@@ -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<typeof backgroundImageSchema>;

export default model<BackgroundImage>("BackgroundImage", backgroundImageSchema);
16 changes: 16 additions & 0 deletions backend/src/models/eventDetails.ts
Original file line number Diff line number Diff line change
@@ -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<typeof eventDetailsSchema>;

export default model<EventDetails>("EventDetails", eventDetailsSchema);
28 changes: 28 additions & 0 deletions backend/src/models/volunteerDetails.ts
Original file line number Diff line number Diff line change
@@ -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<typeof volunteerDetailsSchema>;

export default model<VolunteerDetails>("VolunteerDetails", volunteerDetailsSchema);
8 changes: 8 additions & 0 deletions backend/src/routes/background_images.ts
Original file line number Diff line number Diff line change
@@ -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;
20 changes: 20 additions & 0 deletions backend/src/routes/eventDetails.ts
Original file line number Diff line number Diff line change
@@ -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;
11 changes: 11 additions & 0 deletions backend/src/routes/volunteerDetails.ts
Original file line number Diff line number Diff line change
@@ -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;
65 changes: 65 additions & 0 deletions backend/src/validators/eventDetails.ts
Original file line number Diff line number Diff line change
@@ -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()];
9 changes: 8 additions & 1 deletion frontend/next.config.js
Original file line number Diff line number Diff line change
@@ -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",
],
},
};

Expand Down
Loading

0 comments on commit b5e95ae

Please sign in to comment.