Skip to content

Commit

Permalink
Merge pull request #254 from UB-ES-2023-A2/feature/us5-recipe-details
Browse files Browse the repository at this point in the history
Feature/us4-recipe-details
  • Loading branch information
ivanmansilla authored Dec 14, 2023
2 parents c9009ba + 9bff1c9 commit 02f2cc0
Show file tree
Hide file tree
Showing 4 changed files with 360 additions and 31 deletions.
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 @@ -41,8 +42,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 @@ -81,6 +81,7 @@ function RecipeDetail() {

useEffect(() => {
getRecipe();
getSimilarRecipes();
if (isLogged()) {
getIsLiked(userName);
}
Expand All @@ -105,6 +106,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 @@ -370,8 +382,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 @@ -515,10 +527,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 @@ -528,7 +561,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

0 comments on commit 02f2cc0

Please sign in to comment.