Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rating feature implementation #538

Open
wants to merge 45 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
d606e5c
Initialized routing for the review system
T7alabdullah Dec 7, 2024
0a9e9d1
Addition of Review system button in the navigation bar with the prope…
T7alabdullah Dec 7, 2024
f8e15ae
Added a new file for the review system frotend, addition of dependenc…
T7alabdullah Dec 7, 2024
082ac7c
Added food experience describtion input and the restaurant selection …
T7alabdullah Dec 7, 2024
bebb0fd
Addition of service and cleanliness experiences input for the form
T7alabdullah Dec 7, 2024
32dfa8b
Add a checkbox inquiring people on whether they would recommend this …
fdounis Dec 7, 2024
3d968eb
Added a textfield for people to add any comments or suggestions
fdounis Dec 7, 2024
a997075
Added a button for people to submit their feedback and send the email
fdounis Dec 7, 2024
b4b8cc3
Enhanced TextField UX by updating focus colors and adding hover funct…
mthani2 Dec 8, 2024
60a113f
Adjust navbar pill sizes and styles for enhanced UI consistency
mthani2 Dec 8, 2024
709bc9f
added rating form css page
mthani2 Dec 8, 2024
4663c8a
fixed rating form UI and fixed select restaurant toggle
mthani2 Dec 8, 2024
00fe0cf
added description to review form
mthani2 Dec 8, 2024
67876b5
Edited the previously named RatingPage to ReviewPage since we'll be a…
Nalseaf Dec 8, 2024
aa43d3b
Changed the route to better fit the naming changes from Rate to Review
Nalseaf Dec 8, 2024
e0c50be
Added the Rate A Restaurant button and icon on the NAV bar
Nalseaf Dec 8, 2024
e6c9e72
Fixed the routing to the Rating page in the App.tsx file
Nalseaf Dec 8, 2024
36a7118
Created a Rating form Page with the rating questions from 1-5
Nalseaf Dec 8, 2024
775eae5
Changed the colors of the text fields for better visibility
Nalseaf Dec 8, 2024
0eabafc
modifying React Frontend to receive ratings on backend
mthani2 Dec 8, 2024
479063c
adding a Ratings.js model file
mthani2 Dec 8, 2024
398f278
adding a routes file for the ratings
mthani2 Dec 8, 2024
b41623c
adding the rating model to the services folder
mthani2 Dec 8, 2024
2af48a9
adding rating average calculations to ratingModel
mthani2 Dec 8, 2024
0ebd05d
Add initial implementation for average restaurant ratings feature
mthani2 Dec 8, 2024
b6b1f27
Test: Added unit tests for RatingModel to validate create, fetch, and…
Dec 9, 2024
57c63f1
Add: Created two test files - ratingModel.test.ts and ratingRoutes.te…
Dec 9, 2024
d8bf23a
Test: Added integration tests for Rating Routes to validate POST, GET…
Dec 9, 2024
e802b9d
Test: Added server error handling test for Rating Routes
Dec 9, 2024
1610b00
Test: Implemented mocking for RatingModel methods to simulate databas…
Dec 9, 2024
a488096
Created the backend directory which was populated with a controllers …
fdounis Dec 9, 2024
a9de32d
Created in the directory to define the schema and model for feedbac…
fdounis Dec 9, 2024
1110bdf
Created server.js to set up and configure the Express server, includi…
fdounis Dec 9, 2024
d09bb3f
Created emailTransporter.js in the backend/util directory to configur…
fdounis Dec 9, 2024
ff59bdd
Add restaurant email mappings to ReviewFormPage.tsx for improved main…
T7alabdullah Dec 9, 2024
4c404a5
Changed the application so ReviewFormPage.tsx is correctly imported a…
T7alabdullah Dec 9, 2024
98a3b86
fixed navigation bar to adapt to number of pages
mthani2 Dec 10, 2024
0b67c25
Merge branch 'rating-system-frontend' of https://github.com/mthani2/c…
mthani2 Dec 10, 2024
31e2d2b
fixed all routing and backend errors associated with rating averages
mthani2 Dec 10, 2024
776bd41
Enhanced rating routes with POST and GET logic for average ratings an…
mthani2 Dec 10, 2024
058834a
Added ratingController.js with endpoints for creating, fetching, and …
Dec 11, 2024
27ee003
Added rating routes and integrated them into the server
Dec 11, 2024
7279ddd
Revert " Added rating routes and integrated them into the server"
mthani2 Dec 11, 2024
26b0ce7
Revert "Added ratingController.js with endpoints for creating, fetchi…
mthani2 Dec 11, 2024
8edb43a
Merge branch 'Rating-system-testing' into rating-feature-backend
mthani2 Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 90 additions & 82 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,100 +6,108 @@ import Navbar from './components/Navbar';
import ListPage from './pages/ListPage';
import MapPage from './pages/MapPage';
import NotFoundPage from './pages/NotFoundPage';
import ReviewFormPage from './pages/ReviewFormPage'; // Import the ReviewFormPage
import RatingPage from './pages/RatingFormPage'; // Import the new RatingPage
import { queryLocations, getLocationStatus } from './util/queryLocations';
import './App.css';
import {
IReadOnlyExtendedLocation,
IReadOnlyLocation,
IReadOnlyExtendedLocation,
IReadOnlyLocation,
} from './types/locationTypes';

const CMU_EATS_API_URL = 'https://dining.apis.scottylabs.org/locations';
// const CMU_EATS_API_URL = 'http://localhost:5173/example-response.json'; // for debugging purposes (note that you need an example-response.json file in the /public folder)
// const CMU_EATS_API_URL = 'http://localhost:5010/locations'; // for debugging purposes (note that you need an example-response.json file in the /public folder)

function App() {
// Load locations
const [locations, setLocations] = useState<IReadOnlyLocation[]>();
const [extendedLocationData, setExtendedLocationData] =
useState<IReadOnlyExtendedLocation[]>();
useEffect(() => {
queryLocations(CMU_EATS_API_URL).then((parsedLocations) => {
setLocations(parsedLocations);
});
}, []);
// Load locations
const [locations, setLocations] = useState<IReadOnlyLocation[]>();
const [extendedLocationData, setExtendedLocationData] =
useState<IReadOnlyExtendedLocation[]>();

useEffect(() => {
queryLocations(CMU_EATS_API_URL).then((parsedLocations) => {
setLocations(parsedLocations);
});
}, []);

useEffect(() => {
const intervalId = setInterval(
(function updateExtendedLocationData() {
if (locations !== undefined) {
// Remove .setZone('America/New_York') and change time in computer settings when testing
// Alternatively, simply set now = DateTime.local(2023, 12, 22, 18, 33); where the parameters are Y,M,D,H,M
const now = DateTime.now().setZone('America/New_York');
setExtendedLocationData(
locations.map((location) => ({
...location,
...getLocationStatus(location.times, now), // populate location with more detailed info relevant to current time
})),
);
}
return updateExtendedLocationData; // returns itself here
})(), // self-invoking function
1 * 1000, // updates every second
);
return () => clearInterval(intervalId);
}, [locations]);
useEffect(() => {
const intervalId = setInterval(
(function updateExtendedLocationData() {
if (locations !== undefined) {
const now = DateTime.now().setZone('America/New_York');
setExtendedLocationData(
locations.map((location) => ({
...location,
...getLocationStatus(location.times, now), // populate location with more detailed info relevant to current time
}))
);
}
return updateExtendedLocationData;
})(),
1 * 1000, // updates every second
);
return () => clearInterval(intervalId);
}, [locations]);

// Auto-refresh the page when the user goes online after previously being offline
useEffect(() => {
function handleOnline() {
if (navigator.onLine) {
// Refresh the page
window.location.reload();
}
}
// Auto-refresh the page when the user goes online after previously being offline
useEffect(() => {
function handleOnline() {
if (navigator.onLine) {
// Refresh the page
window.location.reload();
}
}

window.addEventListener('online', handleOnline);
window.addEventListener('online', handleOnline);

return () => window.removeEventListener('online', handleOnline);
}, []);
return () => window.removeEventListener('online', handleOnline);
}, []);

return (
<React.StrictMode>
<BrowserRouter>
<div className="App">
<div className="AdBanner">
Pre-register for{' '}
<a
href="https://go.scottylabs.org/tartanhacks-cmueats"
style={{ color: 'white' }}
>
<strong>TartanHacks</strong>
</a>
, Pittsburgh&apos;s LARGEST hackathon! 🖥️
</div>
<div className="MainContent">
<Routes>
<Route
path="/"
element={
<ListPage
locations={extendedLocationData}
/>
}
/>
<Route
path="/map"
element={
<MapPage locations={extendedLocationData} />
}
/>
<Route path="*" element={<NotFoundPage />} />
</Routes>
</div>
<Navbar />
</div>
</BrowserRouter>
</React.StrictMode>
);
return (
<React.StrictMode>
<BrowserRouter>
<div className="App">
<div className="AdBanner">
Pre-register for{' '}
<a
href="https://go.scottylabs.org/tartanhacks-cmueats"
style={{ color: 'white' }}
>
<strong>TartanHacks</strong>
</a>
, Pittsburgh&apos;s LARGEST hackathon! 🖥️
</div>
<div className="MainContent">
<Routes>
<Route
path="/"
element={
<ListPage
locations={extendedLocationData}
/>
}
/>
<Route
path="/map"
element={
<MapPage locations={extendedLocationData} />
}
/>
<Route
path="/review-restaurant"
element={<ReviewFormPage />} // Add the new ReviewFormPage route
/>
<Route
path="/rate-restaurant"
element={<RatingPage />} // New route for RatingPage
/>
<Route path="*" element={<NotFoundPage />} />
</Routes>
</div>
<Navbar />
</div>
</BrowserRouter>
</React.StrictMode>
);
}

export default App;
33 changes: 31 additions & 2 deletions src/components/EateryCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';

import {
Card,
CardHeader,
Expand Down Expand Up @@ -164,6 +165,7 @@ const SpecialsContent = styled(Accordion)({
backgroundColor: '#23272A',
});


function EateryCard({ location }: { location: IReadOnlyExtendedLocation }) {
const {
name,
Expand All @@ -177,9 +179,33 @@ function EateryCard({ location }: { location: IReadOnlyExtendedLocation }) {
} = location;
const changesSoon = !location.closedLongTerm && location.changesSoon;
const isOpen = !location.closedLongTerm && location.isOpen;

const [modalOpen, setModalOpen] = useState(false);


const [averageRatings, setAverageRatings] = useState<{
overall: number;
}>({ overall: 0 }); // Default values

useEffect(() => {
const fetchAverageRating = async () => {
try {
const response = await fetch(`/api/ratings/average/${name}`);
if (response.ok) {
const data = await response.json();
setAverageRatings(data.averageRating || { overall: 0 });
} else {
console.warn(`No ratings available for ${name}`);
}
} catch (error) {
console.error('Error fetching average ratings:', error);
}
};

fetchAverageRating();
}, [name]);



return (
<>
<Grid item xs={12} md={4} lg={3} xl={3}>
Expand Down Expand Up @@ -227,6 +253,9 @@ function EateryCard({ location }: { location: IReadOnlyExtendedLocation }) {
{locationText}
</LocationText>
<DescriptionText>{shortDescription}</DescriptionText>
<Typography variant="body1" style={{ color: '#FFD700' }}>
Average Rating: {averageRatings.overall ? averageRatings.overall.toFixed(1) : 'Not Rated'}
</Typography>
</CardContent>
<CardActions sx={{ marginTop: 'auto' }}>
{menu && (
Expand Down
60 changes: 28 additions & 32 deletions src/components/Navbar.css
Original file line number Diff line number Diff line change
@@ -1,52 +1,48 @@
.Navbar {
height: var(--navbar-height);
background-color: #1e1e1e;
padding: 14px;
box-sizing: border-box;
border-top: 2px solid #31373e;
background-color: #1e1e1e;
padding: 14px;
box-sizing: border-box;
border-top: 2px solid #31373e;
}

.Navbar-links {
position: relative;
display: grid;
grid-auto-columns: 1fr;
grid-auto-flow: column;
height: 40px;
margin: 0 auto;
max-width: 500px;
display: grid;
grid-template-columns: repeat(4, 1fr); /* Adjusted for 4 links now */
grid-auto-flow: column;
height: 40px;
margin: 0 auto;
max-width: 600px; /* Adjusted width to accommodate new link */
}

.Navbar-links a {
position: relative;
z-index: 5;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Zilla Slab', sans-serif;
font-weight: 500;
color: white;
font-size: 16px;
text-decoration: none;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Zilla Slab', sans-serif;
font-weight: 500;
color: white;
font-size: 16px;
text-decoration: none;
}

.Navbar-links svg {
width: 24px;
height: 24px;
margin-right: 0.4em;
height: 24px;
margin-right: 0.4em;
}

.Navbar-active {
position: absolute;
z-index: 2;
left: 0;
top: 0;
width: 50%;
height: 100%;
background-color: #2b2f33;
border-radius: 999px;
transition:
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.3s ease-in-out;
position: absolute;
background-color: #2b2f33;
border-radius: 999px;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 0 5px #ee6f52, 0 0 10px #ee6f52, 0 0 15px #ee6f52; /* Glowing effect */
width: calc(350% / 4); /* Adapt width to the number of links */
height: 100%;
}

.Navbar-active_map {
Expand Down
Loading
Loading