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

Feature/us4-recipe-details #254

Merged
merged 27 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d55ea44
feat: clean reviews
ivanmansilla Dec 5, 2023
e74a19f
feat: Change variable env
ivanmansilla Dec 5, 2023
8d8baa5
feat: Create and implement new component ModifyReview
ivanmansilla Dec 5, 2023
41f78df
feat: Implement edit and delete review
ivanmansilla Dec 5, 2023
15db87f
Wip: Actually only edit comment
ivanmansilla Dec 5, 2023
dd64ef1
feat: Update rating algo
ivanmansilla Dec 7, 2023
a084972
WIP: update image in progess
ivanmansilla Dec 7, 2023
13e1807
Merge pull request #234 from UB-ES-2023-A2/feature/edit-delete-review
ivanmansilla Dec 7, 2023
8064922
feat: Button Review of RecipeDetail change text and design
ivanmansilla Dec 7, 2023
baa3c1e
feat: Control errors with no reviews, etc
ivanmansilla Dec 7, 2023
7b359e9
feat: Post review button change design
ivanmansilla Dec 7, 2023
b48c051
feat: Control different things, when is recipe owner
ivanmansilla Dec 7, 2023
fb99c4e
feat: Comment doesn't overlap username
ivanmansilla Dec 7, 2023
a197809
Merge pull request #235 from UB-ES-2023-A2/feature/offcanvas-and-designs
ivanmansilla Dec 7, 2023
b0ffbd0
fix: Post review enabled no recipes no owner
ivanmansilla Dec 7, 2023
dbc0188
fix: Fix bug enabled button
ivanmansilla Dec 7, 2023
2beea1f
feat: New style for reviews without images
ivanmansilla Dec 12, 2023
9c4866d
feat: same design post review as page
ivanmansilla Dec 12, 2023
7bc2f2c
feat: control no pass x characters
ivanmansilla Dec 12, 2023
7a4f4d1
feat: num of characters in real time
ivanmansilla Dec 12, 2023
2382579
feat: New design to Modify Review and Delete Review
ivanmansilla Dec 13, 2023
a9f4e85
Merge branch 'feature/details-reviews' of https://github.com/UB-ES-20…
ivanmansilla Dec 13, 2023
aec4448
feat: change style and design of different places
ivanmansilla Dec 13, 2023
ff9f90c
feat: Implement all about similar recipes DONE
ivanmansilla Dec 13, 2023
b26504c
Merge pull request #256 from UB-ES-2023-A2/feature/images-recipe-details
ivanmansilla Dec 14, 2023
fcf08dc
feat: Change design about similar recipes section
ivanmansilla Dec 14, 2023
9bff1c9
fix: Change size of Similars title
ivanmansilla Dec 14, 2023
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
162 changes: 162 additions & 0 deletions src/components/ModifyReview.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// ModifyReview.js
import React, { useState } from 'react';
import { Button, Modal, Form } from 'react-bootstrap';
import { useAuth } from "./AuthContext";
import {
StarFill,
Star
} from "react-bootstrap-icons";

const ModifyReview = ({ show, reviewId, recipeId, onHide, reloadReviews, funct, reviewInfo }) => {
const [newComment, setNewComment] = useState(reviewInfo.comment);
const [newRating, setNewRating] = useState(reviewInfo.rating);
const [newImage, setNewImage] = useState(reviewInfo.file);
const [characterCount, setCharacterCount] = useState(0);
const { token } = useAuth();

const handleUpdateReview = async () => {
const bodyData = {
comment: newComment,
rating: newRating,
};
if (newImage) {
bodyData.file = newImage;
}
console.log(">>>>NewImage: ", newImage)

try {
const response = await fetch(
`${process.env.REACT_APP_API_URL}/review/${recipeId}/${reviewId}`,
{
method: 'PUT',
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ comment: newComment, rating: newRating }),
file: newImage
}
);

const data = await response.json();
if (response.ok) {
console.log('Review actualizada:', data);
reloadReviews();
} else {
console.error('Error al actualizar la revisión:', data);
}
} catch (error) {
console.error('Error en la solicitud de actualización de la revisión:', error);
}

onHide();
};

const handleDeleteReview = async () => {
try {
const response = await fetch(
`${process.env.REACT_APP_API_URL}/review/${recipeId}/${reviewId}`,
{
method: 'DELETE',
headers: {
Authorization: `Bearer ${token}`,
},
}
);

const data = await response.json();
if (response.ok) {
console.log('Review eliminada:', data);
reloadReviews();
} else {
console.error('Error al eliminar la revisión:', data);
}
} catch (error) {
console.error('Error en la solicitud de eliminación de la revisión:', error);
}

onHide();
};

const renderStars = (amount) => {
let stars = [];
for (let i = 1; i <= 5; i++) {
stars.push(
<span
key={i}
className={"difficulty-star "
.concat(i <= amount ? "active" : "inactive")
.concat(" ms-2 fs-4")}
role="button"
onClick={() => setNewRating(i)}
>
{i <= amount ? <StarFill /> : <Star />}
</span>
);
}
return stars;
};

const handleReviewChange = (e) => {
const inputReview = e.target.value;
setNewComment(inputReview)
setCharacterCount(inputReview.length);
};

const handleConfirmDelete = () => {
handleDeleteReview();
};

return (
<Modal show={show} onHide={onHide}>
<Modal.Header closeButton className="fw-bold bg-normal">
<Modal.Title>{funct === 'Edit' ? 'Modify' : 'Delete'} review</Modal.Title>
</Modal.Header>
<Modal.Body className="bg-lightest">
{funct === 'Edit' ? (
<Form>
<Form.Group className="mb-3 fw-bold">
<Form.Label>New Review</Form.Label>
<Form.Control
as="textarea"
rows={3}
placeholder="Write your new review here"
value={newComment}
onChange={handleReviewChange}
style={{ borderColor: characterCount > 80 ? 'red' : null }}
/>
{characterCount > 80 && (
<div className="text-danger">You exceeded 80 characters.</div>
)}
<div className="mt-2 text-muted">Num characters: {characterCount}</div>
</Form.Group>
<Form.Group className="mb-3 fw-bold">
<Form.Label>New Rating</Form.Label>
<div>{renderStars(newRating)}</div>
</Form.Group>
</Form>
) : (
<p className='fw-bold fs-4'>Are you sure delete review?</p>
)}
</Modal.Body>
<Modal.Footer style={{ backgroundColor: "#ffe7dfe0"}}>
{funct === 'Edit' ? (
<Button className='bg-danger fw-bold border-secondary text-white' variant="primary" onClick={handleUpdateReview} disabled={characterCount > 80}>
Update
</Button>
) : (
<>
<Button variant="danger" onClick={handleConfirmDelete}>
Accept
</Button>
<Button variant="secondary" onClick={onHide}>
Cancel
</Button>
</>
)}
</Modal.Footer>
</Modal>
);
};

export default ModifyReview;
37 changes: 27 additions & 10 deletions src/components/PostReview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const PostReview = ({ id, show, onHide, reloadReviews }) => {
const { token } = useAuth();
const [error, setError] = useState(null);
const [showErrorModal, setShowErrorModal] = useState(false);
const [characterCount, setCharacterCount] = useState(0);



useEffect(() => {
Expand All @@ -39,6 +41,12 @@ const PostReview = ({ id, show, onHide, reloadReviews }) => {
setImage(selectedImage);
};

const handleReviewChange = (e) => {
const inputReview = e.target.value;
setReview(inputReview);
setCharacterCount(inputReview.length);
};

const handlePostReview = async () => {
const reviewData = {
username: username,
Expand Down Expand Up @@ -103,38 +111,47 @@ const PostReview = ({ id, show, onHide, reloadReviews }) => {

return (<>
<Modal show={show} onHide={onHide}>
<Modal.Header closeButton>
<Modal.Header closeButton className="fw-bold bg-normal">
<Modal.Title>Post a review</Modal.Title>
</Modal.Header>
<Modal.Body>
<Modal.Body className="bg-lightest">
<Form>
<Form.Group className="mb-3">
<Form.Group className="mb-3 fw-bold">
<Form.Label>Review</Form.Label>
<Form.Control
as="textarea"
rows={3}
placeholder="Write your review here"
value={review}
onChange={(e) => setReview(e.target.value)}
onChange={handleReviewChange}
style={{ borderColor: characterCount > 80 ? 'red' : null }}
/>
{characterCount > 80 && (
<div className="text-danger">You exceeded 80 characters.</div>
)}
<div className="mt-2 text-muted">Num characters: {characterCount}</div>
</Form.Group>

<Form.Group className="mb-3">
<Form.Group className="mb-3 fw-bold">
<Form.Label>Rating</Form.Label>
<div>{renderStars(difficulty)}</div>
</Form.Group>

<Form.Group className="mb-3">
<Form.Group className="mb-3 fw-bold">
<Form.Label>Select Image</Form.Label>
<Form.Control type="file" onChange={handleFileChange} />
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Modal.Footer style={{ backgroundColor: "#ffe7dfe0"}}>
<Button variant="secondary" onClick={onHide}>
Close
</Button>
<Button variant="primary" onClick={handlePostReview}>
<Button
className='bg-danger fw-bold border-secondary text-white'
variant="primary"
onClick={handlePostReview}
disabled={characterCount > 80}
>
Post Review
</Button>
</Modal.Footer>
Expand All @@ -149,7 +166,7 @@ const PostReview = ({ id, show, onHide, reloadReviews }) => {
</Alert>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={() => setShowErrorModal(false)}>
<Button variant="secondary" onClick={() => setShowErrorModal(false)}>
Close
</Button>
</Modal.Footer>
Expand Down
47 changes: 40 additions & 7 deletions src/components/RecipeDetail.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import React, { useState, useEffect } from "react";
import { useAuth } from "./AuthContext";
import "../css/common.css";
import "../css/Transitions.css";
import RecipeCard from "./RecipeCard";
import chefIcon from "../assets/icons/chef.png"
import { useParams, useNavigate } from "react-router-dom";
import { useParams, useNavigate, Link } from "react-router-dom";
import defaultProfile from "../assets/defaultProfile.png";
import { CSSTransition } from "react-transition-group";
import gyozas from '../assets/gyozas.jpg';
Expand Down Expand Up @@ -40,8 +41,7 @@ function RecipeDetail() {
const [userId, setUserId] = useState('');
const [userName, setUserName] = useState('');
const [userImage, setUserImage] = useState('');
const [showModal, setShowModal] = useState(false);
const [user, setUser] = useState({});
const [similarRecipes, setSimilarRecipes] = useState('');
const { id } = useParams();
const [recipe, setRecipe] = useState({ images: [] });
const [showReviews, setShowReviews] = useState(false);
Expand Down Expand Up @@ -73,6 +73,7 @@ function RecipeDetail() {

useEffect(() => {
getRecipe();
getSimilarRecipes();
if (isLogged()) {
getIsLiked(username);
}
Expand All @@ -89,6 +90,17 @@ function RecipeDetail() {
.catch((error) => console.error("Error al obtener receta:", error));
};

const getSimilarRecipes = () => {
fetch(process.env.REACT_APP_API_URL + `/recipe/similar/${id}`)
.then((response) => response.json())
.then((data) => {
setSimilarRecipes(data)
console.log(">><data: ", data)
})
.catch((error) => console.error("Error al obtener recetas similares:", error));
};


const fetchUserData = async (userId) => {
try {
const response = await fetch(process.env.REACT_APP_API_URL + '/user/' + userId, {
Expand Down Expand Up @@ -307,8 +319,8 @@ function RecipeDetail() {
{recipe.energy ?? "No info of"} kcal
</span>
</div>
<Button className="mt-3" onClick={handleToggleReviews}>
Toggle Reviews
<Button className="mt-3 bg-danger fw-bold border-secondary text-white" onClick={handleToggleReviews}>
Reviews
</Button>
</div>
</Col>
Expand Down Expand Up @@ -418,10 +430,31 @@ function RecipeDetail() {
</Row>
</Container>
</CSSTransition>
<CSSTransition in={true} timeout={500} classNames="slideUp" appear>
<Container className="mt-5 pb-3 text-center box-rounded shadow bg-lightest">
<Row>
<Col sm={12}>
<h1 className="py-4 mt-1">Similar Recipes</h1>
</Col>
{similarRecipes.length > 0 ? similarRecipes?.map((recipe) => (
<Col key={recipe._id} sm={4} className="mb-4">
<Link
to={`/RecipeDetail/${recipe._id}`}
className="text-decoration-none"
>
<RecipeCard recipe={recipe} />
</Link>
</Col>
)): null}
</Row>
</Container>
</CSSTransition>
</Col>
</Row>
</Container>
{/* Offcanvas para mostrar los comentarios */}



<Offcanvas
show={showReviews}
onHide={() => setShowReviews(false)}
Expand All @@ -431,7 +464,7 @@ function RecipeDetail() {
<Offcanvas.Title className="fs-2 mt-3">Reviews</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body style={{ backgroundColor: "#ffb79fe0" }}>
<Reviews id={id} reloadReviews={reloadReviewsFunction} />
<Reviews id={id} reloadReviews={reloadReviewsFunction} owner={recipe.username}/>
</Offcanvas.Body>
</Offcanvas>
<Modal
Expand Down
Loading
Loading