diff --git a/backend/database.sqlite b/backend/database.sqlite index 6c2b539..95f3b3f 100644 Binary files a/backend/database.sqlite and b/backend/database.sqlite differ diff --git a/backend/server.js b/backend/server.js index 388bb05..b3a142b 100644 --- a/backend/server.js +++ b/backend/server.js @@ -5,7 +5,7 @@ const passport = require("./authentication/auth/passport"); const routes = require("./authentication/routes"); const path = require("path"); -const { Event, sequelize, User } = require("./database.js"); +const { Event, sequelize, User, Review } = require("./database.js"); const express = require("express"); const cors = require("cors"); @@ -209,17 +209,29 @@ app.delete("/events/:id", async (req, res) => { }); app.post("/api/reviews", async (req, res) => { + console.log("in /api/reviews"); // Handle review submission const { review_text, event_name } = req.body; - console.log("Server has received a review: ", review_text); - // Save the review or do other logic here res.status(200).json({ review_text }); - console.log("THIS THE TEXTTT: ", req.body.review_text); - console.log(req.body.event_name); + console.log("Server has received a review: ", review_text); + const newReview = await Review.create({ review_text: req.body.review_text, event_name: req.body.event_name, }); + +}); + +app.get("/api/reviews", async (req, res) => { + const eventName = req.query.event_name; + + try { + const reviews = await Review.findAll({ where: { event_name: eventName } }); + res.json(reviews); + } catch (error) { + console.error("Error fetching reviews:", error); + res.status(500).json({ error: "Failed to fetch reviews" }); + } }); //start server diff --git a/frontend/source/components/eventPage/compare.js b/frontend/source/components/eventPage/compare.js new file mode 100644 index 0000000..de8e0b4 --- /dev/null +++ b/frontend/source/components/eventPage/compare.js @@ -0,0 +1,208 @@ +import { openEventForm } from "../eventCreationForm/eventCreationForm.js"; + +export function showEventDetails(event) { + const eventDetailView = + document.getElementById("event-detail-view") || + document.createElement("div"); + eventDetailView.id = "event-detail-view"; + eventDetailView.classList.add("event-detail-view"); + document.body.appendChild(eventDetailView); + + eventDetailView.innerHTML = ` +
+
+ + ${
+    event.title
+  } +

${event.title}

+

Genre: ${event.genre || "N/A"}

+

Location: ${ + event.location || "N/A" + }

+ + + + ${ + event.host === getCurrentUserName() + ? `` + : "" + } +
+
+
+ Map Placeholder +
+ +
+
+

Reviews

+
+
+
+
+ + +
+
+ +
+ +
+ `; + + document + .getElementById("add-to-interested") + .addEventListener("click", () => updateInterested(event)); + document.getElementById("back-to-main").addEventListener("click", backToMain); + document + .getElementById("share-button") + .addEventListener("click", () => shareEvent(event)); + document.getElementById("rsvp-button").addEventListener("click", rsvpEvent); + document + .getElementById("submit-review") + .addEventListener("click", (event) => submitReview(event)); + + if (event.host === getCurrentUserName()) { + document + .getElementById("edit-event-button") + .addEventListener("click", () => { + openEventForm(event.id); + }); + } +} + +//fot testing + +localStorage.setItem("currentUser", "Rock Society"); + +// this will be replaced with logic to get the current user +function getCurrentUserName() { + const currentUser = localStorage.getItem("currentUser") || "Guest"; + console.log(`Current User: ${currentUser}`); + return currentUser; +} + +function updateInterested(event) { + event.interested = !event.interested; + document.getElementById("add-to-interested").textContent = event.interested + ? "Remove" + : "Add"; + alert( + `Event ${ + event.interested ? "added to" : "removed from" + } your Interested list!` + ); +} + +function backToMain() { + const eventDetailView = document.getElementById("event-detail-view"); + if (eventDetailView) eventDetailView.remove(); + + document.getElementById("popular-events").style.display = "block"; + document.getElementById("events-for-you").style.display = "block"; + document.getElementById("search-results").style.display = "none"; + document.getElementById("map-container").style.display = "none"; +} + +function shareEvent(event) { + const eventUrl = `${window.location.origin}${ + window.location.pathname + }?event=${encodeURIComponent(event.title)}`; + + navigator.clipboard + .writeText(eventUrl) + .then(() => alert(`Event link copied to clipboard: ${eventUrl}`)) + .catch((error) => console.error("Error", error)); +} + +async function submitReview(event) { + // get the typed text + event.preventDefault(); + const reviewText = document.getElementById("review-text").value; + const eventTitle = document.querySelector(".event-detail-title"); + const eventName = eventTitle + ? eventTitle.innerText + : "No event name available"; + // if there is text, then we need to post to db + if (reviewText) { + const response = await fetch("http://127.0.0.1:4000/api/reviews", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + // in db, records contain the text and the event its for + body: JSON.stringify({ + review_text: reviewText, + event_name: eventName, + }), + }); + + // the post request should return res with just the text + const newReview = await response.json(); + if (response.ok) { + const reviewBox = document.getElementById("review-box"); + reviewBox.innerHTML += `

${newReview.review_text}

`; // Append review + document.getElementById("review-text").value = ""; // Clear text area + alert("Review Submitted!"); + } else { + alert("Failed to submit review"); + } + } else { + alert("Please write a review"); + } +} + +function rsvpEvent(event) { + const modal = document.getElementById("rsvp-modal"); + if (!modal) { + const modalElement = document.createElement("div"); + modalElement.id = "rsvp-modal"; + modalElement.classList.add("modal"); + modalElement.innerHTML = ` + + `; + + document.body.appendChild(modalElement); + + document + .querySelector(".close") + .addEventListener("click", () => (modalElement.style.display = "none")); + + document.getElementById("rsvp-form").addEventListener("submit", (e) => { + e.preventDefault(); + const attendance = document.getElementById("attendance").value; + const attendeesCount = document.getElementById("attendees").value; + const comments = document.getElementById("comments").value; + + console.log(`RSVP for event: ${event.title}`); + console.log(`Attendance: ${attendance}`); + console.log(`Number of people: ${attendeesCount}`); + console.log(`Comments: ${comments}`); + + modalElement.style.display = "none"; + }); + } + modal.style.display = "block"; +} diff --git a/frontend/source/components/eventPage/eventPage.css b/frontend/source/components/eventPage/eventPage.css index 0983f9c..42d4ddf 100644 --- a/frontend/source/components/eventPage/eventPage.css +++ b/frontend/source/components/eventPage/eventPage.css @@ -125,6 +125,44 @@ footer { font-size: 32px; } +.review-box { + width: 100%; + min-height: 100px; + max-height: 200px; + overflow-y: auto; + background-color: #2a2a3f; + padding: 10px; + border-radius: 2px; + color: #e0c084; + display: flex; + flex-direction: column; + gap: 10px; + box-sizing: border-box; +} + +.review { + background-color: #1a002b; + border-radius: 5px; + padding: 10px; + color: #fafafa;; + font-size: 0.9em; + text-align: left; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + flex-shrink: 0; +} + +/* #review-text { + width: 100%; + height: 60px; + padding: 10px; + border-radius: 8px; + border: none; + margin-bottom: 10px; + resize: none; + background-color: #2a2a3f; + color: #e0c084; +} */ + /* Prevent Modal Style Inheritance */ .modal { display: none; diff --git a/frontend/source/components/eventPage/eventPage.js b/frontend/source/components/eventPage/eventPage.js index a6c7df3..0e174e8 100644 --- a/frontend/source/components/eventPage/eventPage.js +++ b/frontend/source/components/eventPage/eventPage.js @@ -1,18 +1,26 @@ export function showEventDetails(event) { - const eventDetailView = document.getElementById('event-detail-view') || document.createElement('div'); - eventDetailView.id = 'event-detail-view'; - eventDetailView.classList.add('event-detail-view'); - document.body.appendChild(eventDetailView); - - eventDetailView.innerHTML = ` + const eventDetailView = + document.getElementById("event-detail-view") || + document.createElement("div"); + eventDetailView.id = "event-detail-view"; + eventDetailView.classList.add("event-detail-view"); + document.body.appendChild(eventDetailView); + + eventDetailView.innerHTML = `
- ${event.title} + ${
+    event.title
+  }

${event.title}

-

Genre: ${event.genre || 'N/A'}

-

Location: ${event.location || 'N/A'}

- +

Genre: ${event.genre || "N/A"}

+

Location: ${ + event.location || "N/A" + }

+
@@ -37,89 +45,87 @@ export function showEventDetails(event) {
`; - - document.getElementById('add-to-interested').addEventListener('click', () => updateInterested(event)); - document.getElementById('back-to-main').addEventListener('click', backToMain); - document.getElementById('share-button').addEventListener('click', () => shareEvent(event)); - document.getElementById('rsvp-button').addEventListener('click', rsvpEvent); - document - .getElementById("submit-review") - .addEventListener("click", (event) => submitReview(event)); + const eventName = document.querySelector(".event-detail-title").innerText; + fetchAndDisplayReviews(eventName); + + document + .getElementById("add-to-interested") + .addEventListener("click", () => updateInterested(event)); + document.getElementById("back-to-main").addEventListener("click", backToMain); + document + .getElementById("share-button") + .addEventListener("click", () => shareEvent(event)); + document.getElementById("rsvp-button").addEventListener("click", rsvpEvent); + document + .getElementById("submit-review") + .addEventListener("click", submitReview); } function updateInterested(event) { - event.interested = !event.interested; - document.getElementById('add-to-interested').textContent = event.interested ? "Remove" : "Add"; - alert(`Event ${event.interested ? 'added to' : 'removed from'} your Interested list!`); + event.interested = !event.interested; + document.getElementById("add-to-interested").textContent = event.interested + ? "Remove" + : "Add"; + alert( + `Event ${ + event.interested ? "added to" : "removed from" + } your Interested list!` + ); } - - function backToMain() { - const eventDetailView = document.getElementById('event-detail-view'); - if (eventDetailView) eventDetailView.remove(); + const eventDetailView = document.getElementById("event-detail-view"); + if (eventDetailView) eventDetailView.remove(); - document.getElementById('popular-events').style.display = 'block'; - document.getElementById('events-for-you').style.display = 'block'; - document.getElementById('search-results').style.display = 'none'; - document.getElementById('map-container').style.display = 'none'; + document.getElementById("popular-events").style.display = "block"; + document.getElementById("events-for-you").style.display = "block"; + document.getElementById("search-results").style.display = "none"; + document.getElementById("map-container").style.display = "none"; } function shareEvent(event) { - const eventUrl = `${window.location.origin}${window.location.pathname}?event=${encodeURIComponent(event.title)}`; - - navigator.clipboard.writeText(eventUrl) + const eventUrl = `${window.location.origin}${ + window.location.pathname + }?event=${encodeURIComponent(event.title)}`; + + navigator.clipboard + .writeText(eventUrl) .then(() => alert(`Event link copied to clipboard: ${eventUrl}`)) - .catch((error) => console.error('Error', error)); + .catch((error) => console.error("Error", error)); } -// function submitReview() { -// const reviewText = document.getElementById("review-text").value.trim(); -// const reviewBox = document.getElementById("review-box"); +async function submitReview() { + const reviewText = document.getElementById("review-text").value; + const eventName = document.querySelector(".event-detail-title").innerText; + + if (reviewText) { + // create a reviewElement div for css purposes + const reviewElement = document.createElement("div"); + reviewElement.className = "review"; + reviewElement.textContent = reviewText; -// if (reviewText) { -// const reviewElement = document.createElement("div"); -// reviewElement.className = "review"; -// reviewElement.textContent = reviewText; + // append the review to other displayed reviews + const reviewBox = document.getElementById("review-box"); + reviewBox.appendChild(reviewElement); -// reviewBox.appendChild(reviewElement); + document.getElementById("review-text").value = ""; -// document.getElementById("review-text").value = ""; -// } -// // else { -// // alert("Please write a review before submitting."); -// // } -// } + // logic to post to db + const review = JSON.stringify({ + review_text: reviewText, + event_name: eventName, + }); -async function submitReview(event) { - // get the typed text - event.preventDefault(); - const reviewText = document.getElementById("review-text").value; - const eventTitle = document.querySelector(".event-detail-title"); - const eventName = eventTitle - ? eventTitle.innerText - : "No event name available"; - // if there is text, then we need to post to db - if (reviewText) { const response = await fetch("http://127.0.0.1:4000/api/reviews", { method: "POST", headers: { "Content-Type": "application/json", }, - // in db, records contain the text and the event its for - body: JSON.stringify({ - review_text: reviewText, - event_name: eventName, - }), + body: review, }); - // the post request should return res with just the text - const newReview = await response.json(); if (response.ok) { - const reviewBox = document.getElementById("review-box"); - reviewBox.innerHTML += `

${newReview.review_text}

`; // Append review - document.getElementById("review-text").value = ""; // Clear text area - alert("Review Submitted!"); + alert("Review has been submitted!"); } else { alert("Failed to submit review"); } @@ -128,13 +134,57 @@ async function submitReview(event) { } } +async function fetchAndDisplayReviews(eventName) { + // Check if eventName is provided + console.log(eventName); + if (!eventName) { + console.error("Event name is undefined or empty"); + alert("Event name is missing. Unable to fetch reviews."); + return; + } + + try { + // Fetch reviews from the server + const response = await fetch( + `http://127.0.0.1:4000/api/reviews?event_name=${eventName}` + ); + console.log(response); + // Check if the response is successful + if (!response.ok) { + console.error("Error fetching reviews: " + response.statusText); + alert("Failed to fetch reviews. Please try again later."); + return; + } + + // Parse the response JSON + const reviews = await response.json(); + + // Get the review box element + const reviewBox = document.getElementById("review-box"); + reviewBox.innerHTML = ""; // Clear existing reviews + + // Display the reviews + reviews.forEach((review) => { + const reviewElement = document.createElement("div"); + reviewElement.className = "review"; + reviewElement.textContent = review.review_text; + reviewBox.appendChild(reviewElement); + }); + } catch (error) { + // Handle errors (network issues, JSON parsing, etc.) + console.error("Error fetching reviews:", error); + alert("Failed to fetch reviews. Please try again later."); + } +} + + function rsvpEvent(event) { - const modal = document.getElementById("rsvp-modal"); - if (!modal) { - const modalElement = document.createElement("div"); - modalElement.id = "rsvp-modal"; - modalElement.classList.add("modal"); - modalElement.innerHTML = ` + const modal = document.getElementById("rsvp-modal"); + if (!modal) { + const modalElement = document.createElement("div"); + modalElement.id = "rsvp-modal"; + modalElement.classList.add("modal"); + modalElement.innerHTML = ` `; - document.body.appendChild(modalElement); + document.body.appendChild(modalElement); - document.querySelector(".close").addEventListener("click", () => modalElement.style.display = "none"); + document + .querySelector(".close") + .addEventListener("click", () => (modalElement.style.display = "none")); - document.getElementById("rsvp-form").addEventListener("submit", (e) => { - e.preventDefault(); - const attendance = document.getElementById("attendance").value; - const attendeesCount = document.getElementById("attendees").value; - const comments = document.getElementById("comments").value; + document.getElementById("rsvp-form").addEventListener("submit", (e) => { + e.preventDefault(); + const attendance = document.getElementById("attendance").value; + const attendeesCount = document.getElementById("attendees").value; + const comments = document.getElementById("comments").value; - console.log(`RSVP for event: ${event.title}`); - console.log(`Attendance: ${attendance}`); - console.log(`Number of people: ${attendeesCount}`); - console.log(`Comments: ${comments}`); + console.log(`RSVP for event: ${event.title}`); + console.log(`Attendance: ${attendance}`); + console.log(`Number of people: ${attendeesCount}`); + console.log(`Comments: ${comments}`); - modalElement.style.display = "none"; - }); - } - modal.style.display = "block"; + modalElement.style.display = "none"; + }); + } + modal.style.display = "block"; } diff --git a/node_modules/sqlite3/build/Makefile b/node_modules/sqlite3/build/Makefile index c887ca0..8731d03 100644 --- a/node_modules/sqlite3/build/Makefile +++ b/node_modules/sqlite3/build/Makefile @@ -347,7 +347,7 @@ endif quiet_cmd_regen_makefile = ACTION Regenerating $@ cmd_regen_makefile = cd $(srcdir); /usr/local/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/ipsita/Library/Caches/node-gyp/23.1.0" "-Dnode_gyp_dir=/usr/local/lib/node_modules/npm/node_modules/node-gyp" "-Dnode_lib_file=/Users/ipsita/Library/Caches/node-gyp/23.1.0/<(target_arch)/node.lib" "-Dmodule_root_dir=/Users/ipsita/Downloads/UMass_Junior_Year/CS326/LocalVibes/node_modules/sqlite3" "-Dnode_engine=v8" "--depth=." "-Goutput_dir=." "--generator-output=build" -I/Users/ipsita/Downloads/UMass_Junior_Year/CS326/LocalVibes/node_modules/sqlite3/build/config.gypi -I/usr/local/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/Users/ipsita/Library/Caches/node-gyp/23.1.0/include/node/common.gypi "--toplevel-dir=." binding.gyp -Makefile: $(srcdir)/build/config.gypi $(srcdir)/../../../../../../../../usr/local/lib/node_modules/npm/node_modules/node-gyp/addon.gypi $(srcdir)/../node-addon-api/node_api.gyp $(srcdir)/binding.gyp $(srcdir)/../../../../../../Library/Caches/node-gyp/23.1.0/include/node/common.gypi $(srcdir)/deps/common-sqlite.gypi $(srcdir)/deps/sqlite3.gyp +Makefile: $(srcdir)/../node-addon-api/node_api.gyp $(srcdir)/build/config.gypi $(srcdir)/deps/sqlite3.gyp $(srcdir)/binding.gyp $(srcdir)/../../../../../../Library/Caches/node-gyp/23.1.0/include/node/common.gypi $(srcdir)/../../../../../../../../usr/local/lib/node_modules/npm/node_modules/node-gyp/addon.gypi $(srcdir)/deps/common-sqlite.gypi $(call do_cmd,regen_makefile) # "all" is a concatenation of the "all" targets from all the included