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 ( + + {/* Thumbs Up Button */} +