diff --git a/backend/controllers/reactionsController.js b/backend/controllers/reactionsController.js
new file mode 100644
index 00000000..3a964ceb
--- /dev/null
+++ b/backend/controllers/reactionsController.js
@@ -0,0 +1,57 @@
+import Reactions from '../models/reactions.js';
+import asyncHandler from 'express-async-handler';
+
+export const getReaction = asyncHandler(async (req, res) => {
+ const { userEmail, eventId } = req.params;
+
+ if (!userEmail || !eventId) {
+ res.status(400);
+ }
+
+ const reaction = await Reactions.findOne({ userEmail, eventId });
+
+ if (reaction) {
+ res.status(200).json(reaction); // Return the found reaction
+ } else {
+ res.status(404).json({ message: 'Reaction not found' }); // No reaction found
+ }
+
+})
+
+export const createReaction = asyncHandler(async (req, res) => {
+ const { userEmail, eventId, reactionType } = req.body;
+
+ // Validate input
+ if (!userEmail || !eventId || ![1, -1].includes(reactionType)) {
+ res.status(400);
+ }
+
+ // Check if a reaction already exists
+ const existingReaction = await Reactions.findOne({ userEmail, eventId });
+
+ if (existingReaction) {
+ // Update the existing reaction
+ existingReaction.reactionType = reactionType;
+ await existingReaction.save();
+ res.status(200).json({
+ message: 'Reaction updated successfully',
+ reaction: existingReaction
+ });
+ } else {
+ // Create a new reaction
+ const newReaction = await Reactions.create({
+ userEmail,
+ eventId,
+ reactionType
+ });
+ res.status(201).json({
+ message: 'Reaction created successfully',
+ reaction: newReaction
+ });
+ }
+});
+
+export default {
+ getReaction,
+ createReaction
+};
diff --git a/backend/index.js b/backend/index.js
index 8ca7619c..b861118e 100644
--- a/backend/index.js
+++ b/backend/index.js
@@ -11,6 +11,7 @@ import connectMailchimp from './mailchimp/connect-mailchimp.js';
import eventRoutes from './routes/event.js';
import subscriptionRoutes from './routes/emailSubscription.js';
import userRoutes from './routes/user.js';
+import reactionsRoutes from './routes/reactions.js'
// initialize the application
const app = express();
@@ -41,6 +42,7 @@ app.get('/', function (_, res) {
app.use(`${baseApi}`, eventRoutes);
app.use(`${baseApi}/subscribers`, subscriptionRoutes);
app.use(`${baseApi}/users`, userRoutes);
+app.use(`${baseApi}/reactions`, reactionsRoutes);
var server = app.listen(PORT, function () {
var port = server.address().port;
diff --git a/backend/models/reactions.js b/backend/models/reactions.js
new file mode 100644
index 00000000..429ea3e2
--- /dev/null
+++ b/backend/models/reactions.js
@@ -0,0 +1,22 @@
+import mongoose from 'mongoose';
+import { Schema, model } from 'mongoose';
+
+const reactionsSchema = new Schema({
+ userEmail: {
+ type: String,
+ required: true
+ },
+ eventId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'Event',
+ required: true
+ },
+ reactionType: {
+ type: Number,
+ enum: [1, -1], // 1 = like, -1 = dislike
+ required: true
+ }
+});
+
+// Export model
+export default model('Reactions', reactionsSchema);
\ No newline at end of file
diff --git a/backend/routes/reactions.js b/backend/routes/reactions.js
new file mode 100644
index 00000000..0ca72338
--- /dev/null
+++ b/backend/routes/reactions.js
@@ -0,0 +1,14 @@
+import express from 'express';
+import reactionsController from '../controllers/reactionsController.js';
+
+const router = express.Router();
+
+//POST request for new reaction
+router.post('/create', reactionsController.createReaction);
+
+//GET request for user reaction
+router.get('/:userEmail/:eventId', reactionsController.getReaction);
+
+// Export router.
+export default router;
+
diff --git a/frontend/src/components/Button/ThumbButtons.tsx b/frontend/src/components/Button/ThumbButtons.tsx
new file mode 100644
index 00000000..e5d5b176
--- /dev/null
+++ b/frontend/src/components/Button/ThumbButtons.tsx
@@ -0,0 +1,84 @@
+import React, { useState, useEffect} from "react";
+import ThumbUpIcon from "@mui/icons-material/ThumbUp";
+import ThumbDownIcon from "@mui/icons-material/ThumbDown";
+import Button from "@mui/material/Button";
+import Stack from "@mui/material/Stack";
+import axios from "axios";
+
+interface ThumbButtonProps {
+ userEmail: string;
+ eventId: string;
+}
+
+const ThumbButtons = ({ userEmail, eventId } : ThumbButtonProps) => {
+ const [selected, setSelected] = useState<1 | -1 | null>(null);
+ const API = axios.create({ baseURL: `${process.env.REACT_APP_BACKEND_URL}/api/v1` });
+
+ useEffect(() => {
+ const fetchReaction = async () => {
+ try {
+ const response = await API.get(`/reactions/${userEmail}/${eventId}`);
+ if (response.data && response.data.reactionType) {
+ setSelected(response.data.reactionType);
+ }
+ } catch (error) {
+ console.error("Error fetching reaction:", error);
+ }
+ };
+
+ fetchReaction();
+ }, [userEmail, eventId]);
+
+ const updateReaction = async (reaction: 1 | -1 | null) => {
+ try {
+ await API.post("/reactions/create", {
+ userEmail,
+ eventId,
+ reactionType: reaction,
+ });
+ } catch (error) {
+ console.error("Error updating reaction:", error);
+ }
+ };
+
+ const handleThumbUp = () => {
+ const newReaction = selected === 1 ? null : 1;
+ setSelected(newReaction);
+ updateReaction(newReaction);
+ };
+
+ const handleThumbDown = () => {
+ const newReaction = selected === -1 ? null : -1;
+ setSelected(newReaction);
+ updateReaction(newReaction);
+ };
+
+ return (
+
+ {title}
+
+ days
+
+
+
+ hours
+
+
+
+ minutes
+
+
+
+ seconds
+
+ {cleanDate()}
+
+ {cleanTime()}
+
+ {location}
+ (e.currentTarget.style.filter = 'grayscale(0%)')}
+ onMouseLeave={(e) => (e.currentTarget.style.filter = 'grayscale(100%)')}
+ />
+
+ )}
+
+ {calendar_link !== '' && (
+
+
(e.currentTarget.style.filter = 'grayscale(0%)')}
+ onMouseLeave={(e) => (e.currentTarget.style.filter = 'grayscale(100%)')}
+ />
+
+ )}
+