From 1e3ed90237d3d16e99e13c5209a51cd3774c5a8d Mon Sep 17 00:00:00 2001 From: PedroMonte Date: Tue, 12 Mar 2024 22:10:27 -0300 Subject: [PATCH 1/6] fix(reviews): set initial ratings to zero --- frontend/src/routes/reviews/ReviewCreate.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/routes/reviews/ReviewCreate.js b/frontend/src/routes/reviews/ReviewCreate.js index c7e3e0a42b..06d13be1d4 100644 --- a/frontend/src/routes/reviews/ReviewCreate.js +++ b/frontend/src/routes/reviews/ReviewCreate.js @@ -18,23 +18,23 @@ const ReviewCreate = () => { const [title, setTitle] = useState(''); const [text, setText] = useState(''); - const [rating, setRating] = useState(''); + const [rating, setRating] = useState(0); const [hover, setHover] = useState(null); const [totalStars, setTotalStars] = useState(5); - const [sabor, setSabor] = useState(''); + const [sabor, setSabor] = useState(0); const [hoverS, setHoverS] = useState(null); const [totalStarsS, setTotalStarsS] = useState(5); - const [atendimento, setAtendimento] = useState(''); + const [atendimento, setAtendimento] = useState(0); const [hoverA, setHoverA] = useState(null); const [totalStarsA, setTotalStarsA] = useState(5); - const [tempoDeEspera, setTempoDeEspera] = useState(''); + const [tempoDeEspera, setTempoDeEspera] = useState(0); const [hoverT, setHoverT] = useState(null); const [totalStarsT, setTotalStarsT] = useState(5); - const [preco, setPreco] = useState(''); + const [preco, setPreco] = useState(0); const [hoverP, setHoverP] = useState(null); const [totalStarsP, setTotalStarsP] = useState(5); @@ -111,7 +111,7 @@ const ReviewCreate = () => { name="rating" value={currentRating} onChange={() => setRating(currentRating)} - required + required /> Date: Wed, 13 Mar 2024 07:43:39 -0300 Subject: [PATCH 2/6] style(reviews): add styling to ReviewsRestaurant --- .../routes/restaurants/RestaurantProfile.js | 27 +- frontend/src/routes/reviews/ReviewCreate.js | 9 +- frontend/src/routes/reviews/ReviewPage.js | 501 ++++++++---------- .../src/routes/reviews/ReviewsRestaurant.js | 78 ++- frontend/src/style/ReviewPage.css | 121 +++++ 5 files changed, 409 insertions(+), 327 deletions(-) create mode 100644 frontend/src/style/ReviewPage.css diff --git a/frontend/src/routes/restaurants/RestaurantProfile.js b/frontend/src/routes/restaurants/RestaurantProfile.js index 94f8ccc8fb..11c7d76ccf 100644 --- a/frontend/src/routes/restaurants/RestaurantProfile.js +++ b/frontend/src/routes/restaurants/RestaurantProfile.js @@ -179,17 +179,19 @@ const RestaurantProfile = () => {

{ restaurant.name }

Tipo de comida: {restaurant.typeOfFood}

{ restaurant.site && Site oficial } - {numRatings !== 1 ? ( -
- {numRatings} Avaliações -
- ) : ( -
- {numRatings} Avaliação -
- - )} -
+
+ {numRatings !== 1 ? ( +
+ {numRatings} Avaliações +
+ ) : ( +
+ {numRatings} Avaliação +
+ + )} + + {[...Array(5)].map((star, index) => { const starValue = index + 1; @@ -202,7 +204,10 @@ const RestaurantProfile = () => { ); })} +
+ +
diff --git a/frontend/src/routes/reviews/ReviewCreate.js b/frontend/src/routes/reviews/ReviewCreate.js index 06d13be1d4..591f985e4e 100644 --- a/frontend/src/routes/reviews/ReviewCreate.js +++ b/frontend/src/routes/reviews/ReviewCreate.js @@ -94,7 +94,8 @@ const ReviewCreate = () => {

Título

- setTitle(ev.target.value)} required @@ -109,6 +110,7 @@ const ReviewCreate = () => { setRating(currentRating)} required @@ -135,6 +137,7 @@ const ReviewCreate = () => { placeholder="Amei a coxinha..." wrap = "soft" name = "text" + id = "text" required value={text} onChange={ev => setText(ev.target.value)} /> @@ -148,6 +151,7 @@ const ReviewCreate = () => { setSabor(currentSabor)} /> @@ -175,6 +179,7 @@ const ReviewCreate = () => { setAtendimento(currentAtendimento)} /> @@ -202,6 +207,7 @@ const ReviewCreate = () => { setTempoDeEspera(currentTempoDeEspera)} /> @@ -229,6 +235,7 @@ const ReviewCreate = () => { setPreco(currentPreco)} /> diff --git a/frontend/src/routes/reviews/ReviewPage.js b/frontend/src/routes/reviews/ReviewPage.js index 2e91daff75..d83a32626a 100644 --- a/frontend/src/routes/reviews/ReviewPage.js +++ b/frontend/src/routes/reviews/ReviewPage.js @@ -1,289 +1,248 @@ +import React, { useState, useEffect } from "react"; import { useParams, useNavigate, Link } from "react-router-dom"; -import { useState, useEffect } from "react"; -import axios from 'axios'; +import axios from "axios"; import { AiOutlineLike, AiOutlineDislike } from "react-icons/ai"; - import { jwtDecode } from "jwt-decode"; +import "../../style/ReviewPage.css"; const API_BASE = "http://localhost:3001"; const ReviewPage = () => { - const params = useParams(); - const iduser = params.iduser; - const idrest = params.idrest; - - const navigate = useNavigate() - - const [review, setReview] = useState(null); - const [restaurant, setRestaurant] = useState(null); - const [user, setUser] = useState(null); - const [isOwner, setIsOwner] = useState(true) - - const [redirect, setRedirect] = useState(false); - - const [likes, setLikes] = useState(0); - const [dislikes, setDislikes] = useState(0); - - const [idUserLogin, setIdUserLogin] = useState(null); - - useEffect(() => { - - const getUserInfoFromToken = async () => { - const token = localStorage.getItem('token'); - - if (token) { - try { - const decoded = jwtDecode(token); - const userID = decoded.userId; - - setIdUserLogin(userID) - - } catch (error) { - console.error("Failed to decode token", error); - } - } - return null; - }; - getUserInfoFromToken() - }, []); - - useEffect(() => { - if (iduser === idUserLogin) { - setIsOwner(true) - } - async function fetchData() { - try { - const response = await axios.get(`${API_BASE}/reviews/${idrest}/${iduser}`); - - if (response.status === 200) { - setReview(response.data) - setLikes(response.data.likes) - setDislikes(response.data.dislikes) - } else { - console.error('Falha ao obter dados do review', response.statusText); - } - } catch (error) { - console.error('Erro ao fazer a solicitação de review para a API', error); - } - - try { - const response = await axios.get(`${API_BASE}/restaurants/${idrest}`); - - if (response.status === 200) { - setRestaurant(response.data) - } else { - console.error('Falha ao obter dados do restaurante', response.statusText); - } - } catch (error) { - console.error('Erro ao fazer a solicitação do restaurante para a API', error); - } - - try { - const response = await axios.get(`${API_BASE}/users/${iduser}`); - - if (response.status === 200) { - setUser(response.data) - } else { - console.error('Falha ao obter dados do usuário', response.statusText); - } - } catch (error) { - console.error('Erro ao fazer a solicitação do usuário para a API', error); - } - } - - fetchData(); - }, [idrest, iduser]); - - async function deleteReview(ev) { - + const { iduser, idrest } = useParams(); + const navigate = useNavigate(); + + const [review, setReview] = useState(null); + const [restaurant, setRestaurant] = useState(null); + const [user, setUser] = useState(null); + const [isOwner, setIsOwner] = useState(true); + + const [redirect, setRedirect] = useState(false); + const [likes, setLikes] = useState(0); + const [dislikes, setDislikes] = useState(0); + const [idUserLogin, setIdUserLogin] = useState(null); + + useEffect(() => { + const getUserInfoFromToken = async () => { + const token = localStorage.getItem("token"); + if (token) { try { - const response = await axios.delete(`${API_BASE}/reviews/${idrest}/${iduser}/delete`); - - if (response.status === 200) { - setRedirect(true); - } else { - console.error('Falha ao deletar review', response.statusText); - } + const decoded = jwtDecode(token); + const userID = decoded.userId; + setIdUserLogin(userID); } catch (error) { - console.error('Erro ao fazer a solicitação para a API', error); + console.error("Failed to decode token", error); } - } - - async function editReview(ev) { - - console.log('entrei') - try { - - } catch (error) { - console.error('Falha ao dar like ou dislike'); + } + }; + getUserInfoFromToken(); + }, []); + + useEffect(() => { + const fetchData = async () => { + try { + const response = await axios.get(`${API_BASE}/reviews/${idrest}/${iduser}`); + if (response.status === 200) { + setReview(response.data); + setLikes(response.data.likes); + setDislikes(response.data.dislikes); + } else { + console.error("Falha ao obter dados do review", response.statusText); } - } - - async function handleLike () { - setLikes((prevCount) => (prevCount === review.likes ? review.likes + 1 : review.likes)) - setDislikes(review.dislikes) - - console.log(likes) - - const response = await axios.put(`${API_BASE}/reviews/${idrest}/${iduser}/edit`, { - title: review.title, - user: iduser, - restaurant: idrest, - rating: review.rating, - text: review.text, - sabor: review.sabor, - atendimento: review.atendimento, - tempoDeEspera: review.tempoDeEspera, - preco: review.preco, - likes: likes, - dislikes: dislikes - }); - }; - - async function handleDislike () { - setDislikes((prevCount) => (prevCount === review.dislikes ? review.dislikes + 1 : review.dislikes)) - setLikes(review.likes) - - console.log(dislikes) - - const response = await axios.put(`${API_BASE}/reviews/${idrest}/${iduser}/edit`, { - title: review.title, - user: iduser, - restaurant: idrest, - rating: review.rating, - text: review.text, - sabor: review.sabor, - atendimento: review.atendimento, - tempoDeEspera: review.tempoDeEspera, - preco: review.preco, - likes: likes, - dislikes: dislikes - }); + } catch (error) { + console.error("Erro ao fazer a solicitação de review para a API", error); + } + + try { + const response = await axios.get(`${API_BASE}/restaurants/${idrest}`); + if (response.status === 200) { + setRestaurant(response.data); + } else { + console.error("Falha ao obter dados do restaurante", response.statusText); + } + } catch (error) { + console.error("Erro ao fazer a solicitação do restaurante para a API", error); + } + + try { + const response = await axios.get(`${API_BASE}/users/${iduser}`); + if (response.status === 200) { + setUser(response.data); + } else { + console.error("Falha ao obter dados do usuário", response.statusText); + } + } catch (error) { + console.error("Erro ao fazer a solicitação do usuário para a API", error); + } }; - - if (redirect) { - navigate('/reviews/' + idrest) + fetchData(); + }, [idrest, iduser]); + + const deleteReview = async () => { + try { + const response = await axios.delete(`${API_BASE}/reviews/${idrest}/${iduser}/delete`); + if (response.status === 200) { + setRedirect(true); + } else { + console.error("Falha ao deletar review", response.statusText); + } + } catch (error) { + console.error("Erro ao fazer a solicitação para a API", error); } - - return ( -
- {review && user && restaurant &&( -
-
-

{ review.title }

-
- {[...Array(5)].map((star, index) => { - const starValue = index + 1; - - return ( - - ★ - - ); - })} -
-

{ user.name}

-

{ restaurant.name}

-

{ review.text}

-

Sabor:

-
- {[...Array(5)].map((star, index) => { - const starValue = index + 1; - - return ( - - ★ - - ); - })} -
-

Atendimento:

-
- {[...Array(5)].map((star, index) => { - const starValue = index + 1; - - return ( - - ★ - - ); - })} -
-

Tempo de Espera

-
- {[...Array(5)].map((star, index) => { - const starValue = index + 1; - - return ( - - ★ - - ); - })} -
-

Preço:

-
- {[...Array(5)].map((star, index) => { - const starValue = index + 1; - - return ( - - ★ - - ); - })} -
-
- {isOwner ? ( -
- - Editar Review - - - - -
- ): ("")} -
-

Avalie este review:

-
- - - -
- -
- -
- - Voltar - -
-
+ }; + + const handleLike = async () => { + setLikes((prevCount) => (prevCount === review.likes ? review.likes + 1 : review.likes)); + setDislikes(review.dislikes); + try { + await axios.put(`${API_BASE}/reviews/${idrest}/${iduser}/edit`, { + title: review.title, + user: iduser, + restaurant: idrest, + rating: review.rating, + text: review.text, + sabor: review.sabor, + atendimento: review.atendimento, + tempoDeEspera: review.tempoDeEspera, + preco: review.preco, + likes: likes, + dislikes: dislikes, + }); + } catch (error) { + console.error("Erro ao editar review", error); + } + }; + + const handleDislike = async () => { + setDislikes((prevCount) => (prevCount === review.dislikes ? review.dislikes + 1 : review.dislikes)); + setLikes(review.likes); + try { + await axios.put(`${API_BASE}/reviews/${idrest}/${iduser}/edit`, { + title: review.title, + user: iduser, + restaurant: idrest, + rating: review.rating, + text: review.text, + sabor: review.sabor, + atendimento: review.atendimento, + tempoDeEspera: review.tempoDeEspera, + preco: review.preco, + likes: likes, + dislikes: dislikes, + }); + } catch (error) { + console.error("Erro ao editar review", error); + } + }; + + if (redirect) { + navigate(`/reviews/${idrest}`); + } + + return ( +
+ {review && user && restaurant && ( +
+
+ {restaurant.profileImage ? ( + Restaurant + ) : ( + No Restaurant Image )} +

{restaurant.name}

+
+
+
+

{review.title}

+
+ {[...Array(5)].map((_, index) => ( + + ★ + + ))} +
+
+
+

Por: {user.name} {isOwner && "(você)"}

+

{review.text}

+
+
+

Sabor:

+
+ {[...Array(5)].map((_, index) => ( + + ★ + + ))} +
+

Atendimento:

+
+ {[...Array(5)].map((_, index) => ( + + ★ + + ))} +
+

Tempo de Espera:

+
+ {[...Array(5)].map((_, index) => ( + + ★ + + ))} +
+

Preço:

+
+ {[...Array(5)].map((_, index) => ( + + ★ + + ))} +
+
+
+ {isOwner && ( +
+ + Editar Review + + +
+ )} +
+

Avalie este review:

+ + +
+
+ + Voltar + +
- ); + )} +
+ ); }; export default ReviewPage; diff --git a/frontend/src/routes/reviews/ReviewsRestaurant.js b/frontend/src/routes/reviews/ReviewsRestaurant.js index 420b6482c0..2e29697e43 100644 --- a/frontend/src/routes/reviews/ReviewsRestaurant.js +++ b/frontend/src/routes/reviews/ReviewsRestaurant.js @@ -1,9 +1,8 @@ import { useState, useEffect } from "react"; import { useParams, Link } from "react-router-dom"; import axios from 'axios'; - import '../../style/Restaurants.css'; - +import NoImg from "../../assets/almocin_logo_red.png" import { jwtDecode } from "jwt-decode"; const API_BASE = "http://localhost:3001"; @@ -40,11 +39,6 @@ const ReviewsRestaurant = () => { getUserInfoFromToken() }, []); - let user = { - name: "pedro", - id: "65d51e36c3b06ec45cdd2ac8" - } - useEffect(() => { const getRestaurant = async () => { try { @@ -119,12 +113,11 @@ const ReviewsRestaurant = () => { return (
- {restaurant && ( -
- + {restaurant && ( +
-

{restaurant.profileImage}

-

{restaurant.name}

+ +

{restaurant.name}

Nota média:

{[...Array(5)].map((star, index) => { @@ -133,7 +126,7 @@ const ReviewsRestaurant = () => { return ( @@ -142,8 +135,8 @@ const ReviewsRestaurant = () => {
- - {reviews.length !== 1 ? ( +
+

{reviews.length !== 1 ? (
{reviews.length} Reviews
@@ -152,48 +145,45 @@ const ReviewsRestaurant = () => { {reviews.length} Review

- )} - -
- {error &&

{error}

} - + )} {reviews.map(review => ( -
+
-
+ +

{review.title}

-
- {[...Array(5)].map((star, index) => { - const starValue = index + 1; - - return ( - - ★ - - ); - })} -
-

{usernames[review.user]}

- {review.user == idUserLogin ? ( -

(você)

- ):( -

- )} +
+ {[...Array(5)].map((star, index) => { + const starValue = index + 1; + + return ( + + ★ + + ); + })} +
+

Por: {usernames[review.user]}

+ {review.user == idUserLogin ? ( +

(você)

+ ):( +

+ )}
- +

Ver Mais...

))} -
+ )}
diff --git a/frontend/src/style/ReviewPage.css b/frontend/src/style/ReviewPage.css new file mode 100644 index 0000000000..8e62f715a1 --- /dev/null +++ b/frontend/src/style/ReviewPage.css @@ -0,0 +1,121 @@ +.review-page-container { + display: flex; + justify-content: center; + } + + .review-page { + max-width: 800px; + margin: 20px; + padding: 20px; + border: 1px solid #ccc; + border-radius: 10px; + background-color: #fff; + } + + .restaurant-details { + text-align: center; + margin-bottom: 20px; + } + + .restaurant-img { + width: 200px; + height: 200px; + border-radius: 50%; + object-fit: cover; + } + + .restaurant-details p { + margin-top: 10px; + font-size: 18px; + } + + .review-data { + background-color: #f9f9f9; + border-radius: 10px; + padding: 20px; + margin-bottom: 20px; + } + + .review-main-details h2 { + font-size: 24px; + margin-bottom: 10px; + } + + .star-rating { + margin-bottom: 10px; + } + + .star-rating span { + font-size: 20px; + margin-right: 5px; + } + + .review-details p { + margin-bottom: 10px; + } + + .review-ratings p { + font-weight: bold; + } + + .review-ratings div { + margin-bottom: 10px; + } + + .review-buttons { + display: flex; + align-items: center; + justify-content: space-between; + } + + .simple-button, + .delete-button { + padding: 8px 16px; + border: none; + border-radius: 5px; + background-color: #007bff; + color: white; + cursor: pointer; + text-decoration: none; + } + + .delete-button { + background-color: #dc3545; + } + + .simple-button:hover, + .delete-button:hover { + opacity: 0.7; + } + + .review-voting { + margin-top: 20px; + } + + .review-voting p { + font-weight: bold; + } + + .review-voting button { + display: flex; + align-items: center; + padding: 5px 10px; + margin-right: 10px; + border: none; + background-color: transparent; + cursor: pointer; + } + + .review-return { + display: block; + margin-top: 20px; + text-align: center; + color: blue; + text-decoration: none; + font-weight: bold; + } + + .review-return:hover { + text-decoration: underline; + } + \ No newline at end of file From 5a6b1cd9bb3549a2b87f59e7c185c0bcfd61ccf4 Mon Sep 17 00:00:00 2001 From: Pedro Vitor de Oliveira Monte Date: Wed, 13 Mar 2024 11:35:25 -0300 Subject: [PATCH 3/6] fix(reviews): users no longer can edit other users reviews --- frontend/src/routes/reviews/ReviewPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/routes/reviews/ReviewPage.js b/frontend/src/routes/reviews/ReviewPage.js index d83a32626a..dc6c8666a3 100644 --- a/frontend/src/routes/reviews/ReviewPage.js +++ b/frontend/src/routes/reviews/ReviewPage.js @@ -28,7 +28,7 @@ const ReviewPage = () => { try { const decoded = jwtDecode(token); const userID = decoded.userId; - setIdUserLogin(userID); + setIsOwner(iduser === userID) } catch (error) { console.error("Failed to decode token", error); } From d0d693ead07c8f8bcac19bc17080d10a2809b01a Mon Sep 17 00:00:00 2001 From: Pedro Vitor de Oliveira Monte Date: Wed, 13 Mar 2024 11:56:02 -0300 Subject: [PATCH 4/6] refactor(reviews): simplify conditional --- frontend/src/routes/reviews/ReviewsRestaurant.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/routes/reviews/ReviewsRestaurant.js b/frontend/src/routes/reviews/ReviewsRestaurant.js index 2e29697e43..e20c7d1b4f 100644 --- a/frontend/src/routes/reviews/ReviewsRestaurant.js +++ b/frontend/src/routes/reviews/ReviewsRestaurant.js @@ -167,10 +167,8 @@ const ReviewsRestaurant = () => { })}

Por: {usernames[review.user]}

- {review.user == idUserLogin ? ( + {(review.user == idUserLogin) && (

(você)

- ):( -

)}
From c9c7a9808f8851bd83a7e6aa10624887eaf3360c Mon Sep 17 00:00:00 2001 From: PedroMonte Date: Wed, 13 Mar 2024 21:36:34 -0300 Subject: [PATCH 5/6] refacter(reviews): remove dead code --- frontend/src/routes/reviews/ReviewPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/routes/reviews/ReviewPage.js b/frontend/src/routes/reviews/ReviewPage.js index dc6c8666a3..fb642faede 100644 --- a/frontend/src/routes/reviews/ReviewPage.js +++ b/frontend/src/routes/reviews/ReviewPage.js @@ -19,7 +19,6 @@ const ReviewPage = () => { const [redirect, setRedirect] = useState(false); const [likes, setLikes] = useState(0); const [dislikes, setDislikes] = useState(0); - const [idUserLogin, setIdUserLogin] = useState(null); useEffect(() => { const getUserInfoFromToken = async () => { From 1144219902fb5d0f67a808b0d39b98c4e269a0bf Mon Sep 17 00:00:00 2001 From: PedroMonte Date: Wed, 13 Mar 2024 22:32:11 -0300 Subject: [PATCH 6/6] fix(reviews): img of restaurant not showing in ReviewsRestaurant page --- frontend/src/routes/reviews/ReviewsRestaurant.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/routes/reviews/ReviewsRestaurant.js b/frontend/src/routes/reviews/ReviewsRestaurant.js index e20c7d1b4f..f2e1da576d 100644 --- a/frontend/src/routes/reviews/ReviewsRestaurant.js +++ b/frontend/src/routes/reviews/ReviewsRestaurant.js @@ -116,7 +116,9 @@ const ReviewsRestaurant = () => { {restaurant && (
- + {restaurant.profileImage !== "Noneundefined" && } + + {restaurant.profileImage == "Noneundefined" && }

{restaurant.name}

Nota média: