sdd
unknown
plain_text
a year ago
22 kB
5
Indexable
import React, { useState, useEffect } from "react"; import { useParams, useNavigate } from "react-router-dom"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faUser, faBookmark, faEye, faTag, faPrint, faBox } from "@fortawesome/free-solid-svg-icons"; import { faStar as fasStar } from "@fortawesome/free-solid-svg-icons"; import { faStar as farStar } from "@fortawesome/free-regular-svg-icons"; import { useViewHistory } from "../context/ViewHistoryContext"; import { useFavoriteBooks } from "../context/FavoriteBooksContext"; import Navbar from "../components/Navbar"; import Sidebar from "../components/Sidebar"; import Loader from "../components/Loader"; import "./BookDetail.css"; import { BASE_URL } from '../pages/link'; import { jwtDecode } from 'jwt-decode'; const BookDetail = () => { const { id } = useParams(); const navigate = useNavigate(); const { addToViewHistory } = useViewHistory(); const { favoriteBooks, addToFavoriteBooks, removeFromFavoriteBooks } = useFavoriteBooks(); const [review, setReview] = useState(""); const [rating, setRating] = useState(0); const [hoverRating, setHoverRating] = useState(0); const [book, setBook] = useState(null); const [recommendedBooks, setRecommendedBooks] = useState([]); const [submittedReview, setSubmittedReview] = useState(null); const [reviews, setReviews] = useState([]); const [error, setError] = useState(null); const [averageRating, setAverageRating] = useState(0); const [userId, setUserId] = useState(null); const [isAuthenticated, setIsAuthenticated] = useState(false); const [isSubmittingReview, setIsSubmittingReview] = useState(false); const booksPerPage = 30; const page = 0; useEffect(() => { sessionStorage.setItem("authToken", "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbjEyMyIsImlhdCI6MTcyMTk2OTIzMCwiZXhwIjoxNzIxOTg3MjMwfQ.7lyjpaHGOyO5kjNrTU0nBGnuwmLDKd9jDH4-HZ0y1b-SHTB1FUYS0G-mlyl0V50MQ-YotmH_vK8NTvom-OdBNw"); console.log("ID from useParams:", id); if (!id) { setError("Book ID is missing"); return; } const fetchData = async () => { try { const token = sessionStorage.getItem('authToken'); if (token) { const decodedToken = jwtDecode(token); if (decodedToken && decodedToken.sub) { setUserId(decodedToken.sub); setIsAuthenticated(true); } } const bookResponse = await fetch(`${BASE_URL}/api/public/books/getById/${id}`, { method: 'GET', headers: { 'ngrok-skip-browser-warning': '69420' } }); if (!bookResponse.ok) { throw new Error(`Failed to fetch book data: ${bookResponse.statusText}`); } const bookData = await bookResponse.json(); setBook(bookData); const reviewResponse = await fetch(`${BASE_URL}/api/public/review/allReview/${id}/reviews`, { method: 'GET', headers: { 'ngrok-skip-browser-warning': '69420' } }); if (!reviewResponse.ok) { throw new Error(`Failed to fetch reviews data: ${reviewResponse.statusText}`); } let reviewsData = []; try { reviewsData = await reviewResponse.json(); if (!Array.isArray(reviewsData)) { throw new Error('reviewsData is not a valid array'); } } catch (jsonError) { console.error("Error parsing reviews data:", jsonError); } setReviews(reviewsData); const averageRatingResponse = await fetch(`${BASE_URL}/api/public/review/ave-rat/${id}/average-rating`, { method: 'GET', headers: { 'ngrok-skip-browser-warning': '69420' } }); if (!averageRatingResponse.ok) { throw new Error(`Failed to fetch average rating data: ${averageRatingResponse.statusText}`); } const averageRatingData = await averageRatingResponse.json(); setAverageRating(averageRatingData.averageRating || 0); const categoriesResponse = await fetch(`${BASE_URL}/api/public/categories/getall`, { method: 'GET', headers: { 'ngrok-skip-browser-warning': '69420' } }); if (!categoriesResponse.ok) { throw new Error(`Failed to fetch categories data: ${categoriesResponse.statusText}`); } const categoriesData = await categoriesResponse.json(); const category = categoriesData.find(cat => cat.name.toLowerCase() === bookData.category.name.toLowerCase()); if (category) { const categoryId = category.id; const categoryBooksResponse = await fetch(`${BASE_URL}/api/public/categories/get/${categoryId}`, { method: 'GET', headers: { 'ngrok-skip-browser-warning': '69420' } }); if (!categoryBooksResponse.ok) { const errorText = await categoryBooksResponse.text(); throw new Error(`Cannot fetch books by category: ${categoryBooksResponse.statusText} - ${errorText}`); } const categoryBooksData = await categoryBooksResponse.json(); setRecommendedBooks(categoryBooksData.books); } else { console.error("Cannot find a matching category for the book title"); } } catch (error) { console.error("Error fetching data:", error); setError(`Error fetching data: ${error.message}`); } }; fetchData(); }, [id, navigate]); const handleReviewChange = (event) => { setReview(event.target.value); }; const handleStarClick = (star) => { setRating(star); }; const handleReadBookClick = () => { if (book.pdfLink) { addToViewHistory(book); window.open(book.pdfLink, '_blank'); } else { alert("This book's PDF link is not available yet."); } }; const toggleFavorite = async () => { if (!isAuthenticated) { alert("Please log in to add this book to your favorites."); navigate('/login'); return; } if (isBookFavorite()) { await removeFavoriteBook(); } else { await addFavoriteBook(); } }; const isBookFavorite = () => { return favoriteBooks.some((favBook) => favBook.id === book.id); }; const addFavoriteBook = async () => { try { const token = sessionStorage.getItem('authToken'); // Log để kiểm tra giá trị của token, id sách và id người dùng console.log("Token:", token); console.log("Book ID:", book.id); console.log("User ID:", userId); const response = await fetch(`${BASE_URL}/api/member/favorites/add`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', 'ngrok-skip-browser-warning': '69420' }, body: JSON.stringify({ id: book.id, // ID của sách userId: userId // ID của người dùng }) }); if (!response.ok) { throw new Error(`Failed to add book to favorites: ${response.statusText}`); } addToFavoriteBooks(book); // Cập nhật trạng thái yêu thích cục bộ } catch (error) { setError(`Error adding book to favorites: ${error.message}`); } }; const removeFavoriteBook = async () => { try { const token = sessionStorage.getItem('authToken'); const response = await fetch(`${BASE_URL}/api/member/favorites/remove/${book.id}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', 'ngrok-skip-browser-warning': '69420' } }); if (!response.ok) { throw new Error(`Failed to remove book from favorites: ${response.statusText}`); } removeFromFavoriteBooks(book.id); // Cập nhật trạng thái yêu thích cục bộ } catch (error) { setError(`Error removing book from favorites: ${error.message}`); } }; const handleRecommendedBookClick = (bookId) => { navigate(`/book/${bookId}`); window.scrollTo({ top: 0, behavior: 'smooth' }); }; const handleSubmitReview = async () => { if (!isAuthenticated) { alert("Please log in to submit a review."); navigate('/login'); return; } setIsSubmittingReview(true); try { const token = sessionStorage.getItem('authToken'); if (!token) { navigate('/login'); return; } const decodedToken = jwtDecode(token); const userId = decodedToken.sub; const response = await fetch(`${BASE_URL}/api/member/review/addReview/{bookId}/reviews?bookId=${id}&createdBy=${userId}`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', 'ngrok-skip-browser-warning': '69420' }, body: JSON.stringify({ userId, bookId: id, rating, review }) }); if (!response.ok) { throw new Error(`Failed to submit review: ${response.statusText}`); } const submittedReview = await response.json(); setSubmittedReview(submittedReview); setReviews([...reviews, submittedReview]); setReview(""); setRating(0); } catch (error) { setError(`Error submitting review: ${error.message}`); } finally { setIsSubmittingReview(false); } }; const handleMouseEnter = (star) => { setHoverRating(star); }; const handleMouseLeave = () => { setHoverRating(0); }; if (error) { return <div className="error-message">{error}</div>; } if (!book) { return <Loader />; } return ( <div className="book-detail"> <Navbar /> <div className="content"> <Sidebar /> <div className="main"> <div className="main-content"> <div className="book-detail"> <div className="book-left-column"> <div className="book-image"> <img src={book.cover} alt={book.title} /> </div> <div className="book-actions"> <button className="read-button" onClick={handleReadBookClick}> Read Book </button> <button className={`favorite-button${isBookFavorite() ? " favorited" : ""}`} onClick={toggleFavorite} > <FontAwesomeIcon icon={faBookmark} /> <span className="button-text"> {isBookFavorite() ? "Remove from Favorites" : "Add to Favorites"} </span> </button> </div> </div> <div className="book-infos"> <h1>{book.title}</h1> <div className="supplier"> <p><strong className="label"><FontAwesomeIcon icon={faUser} />:</strong> {book.author.fullName || "Loading..."}</p> <p><strong className="label"><FontAwesomeIcon icon={faTag} />:</strong> {book.category.name || "Loading..."}</p> </div> <div className="supplier"> <p><strong className="label"><FontAwesomeIcon icon={faBox} />:</strong> {book.supplier.name || "Loading..."}</p> <p><strong className="label"><FontAwesomeIcon icon={faPrint} />:</strong> {book.publisher.name || "Loading..."}</p> </div> <div className="supplier"> <p className="label"><FontAwesomeIcon icon={faEye} className="label" /> <strong className="label">:</strong></p> <p className="rating"> <strong> {[...Array(5)].map((_, index) => ( <FontAwesomeIcon key={index} icon={index < averageRating ? fasStar : farStar} /> ))} </strong> </p> </div> <div className="book-about"> <h2>Description</h2> <div className="book-description-container"> <p>{book.description}</p> </div> </div> </div> </div> <div className="book-additional-info"> <div className="recommended-books"> <h2>Recommended Books</h2> <div className="recommended-books-list"> {recommendedBooks.length > 0 ? ( recommendedBooks.map(recommendedBook => ( <div key={recommendedBook.id} className="recommended-book-item" onClick={() => handleRecommendedBookClick(recommendedBook.id)}> <img src={recommendedBook.cover} alt={recommendedBook.title} /> <div className="recommended-book-info"> <h3>{recommendedBook.title}</h3> </div> </div> )) ) : ( <p>No recommended books available.</p> )} </div> </div> <div className="review-section"> <h2>Review / Rating</h2> <div className="rating-stars"> {[...Array(5)].map((_, index) => { const starRating = index + 1; return ( <FontAwesomeIcon key={starRating} icon={starRating <= (hoverRating || rating) ? fasStar : farStar} onClick={() => handleStarClick(starRating)} onMouseEnter={() => handleMouseEnter(starRating)} onMouseLeave={handleMouseLeave} /> ); })} </div> <textarea placeholder="Write your review..." value={review} onChange={handleReviewChange} disabled={!isAuthenticated} /> <button className="submit-review-button" onClick={handleSubmitReview} disabled={!isAuthenticated || isSubmittingReview} > Submit Review </button> {!isAuthenticated && ( <p className="login-message">Please log in to submit a review.</p> )} {submittedReview && ( <div className="submitted-review-box"> <h3>Your Review:</h3> <div className="submitted-review"> <div className="review-stars"> {[...Array(5)].map((_, index) => ( <FontAwesomeIcon key={index} icon={index < submittedReview.rating ? fasStar : farStar} /> ))} </div> <p>{submittedReview.review}</p> </div> </div> )} {reviews.length > 0 && ( <div className="existing-reviews"> <h2>Existing Reviews</h2> {reviews.map((review) => ( <div key={review.id} className="review"> <p className="review-id">{review.username}</p> <div className="review-stars"> {[...Array(5)].map((_, index) => ( <FontAwesomeIcon key={index} icon={index < review.rating ? fasStar : farStar} /> ))} </div> <p className="review-text">{review.comment}</p> </div> ))} </div> )} </div> </div> </div> </div> <button className="back-button" onClick={() => { navigate("/books"); window.scrollTo({ top: 0, behavior: 'smooth' }); }}> Back </button> </div> </div> ); }; export default BookDetail;
Editor is loading...
Leave a Comment