1
unknown
plain_text
a year ago
11 kB
6
Indexable
import React, { useEffect, useState } from "react";
import { useLocation, Link } from "react-router-dom";
import Navbar from "../components/Navbar";
import logo from "../assets/images/logoweb.png";
import Footer from "../components/Footer";
import Loader from "../components/Loader";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowLeft, faArrowRight, faTimes } from "@fortawesome/free-solid-svg-icons";
import "./ProductList.css";
import { BASE_URL } from "../server/Endpoint";
const ProductList = () => {
const [categories, setCategories] = useState([]);
const [authors, setAuthors] = useState([]);
const [books, setBooks] = useState([]);
const [filteredBooks, setFilteredBooks] = useState([]);
const [selectedRatings, setSelectedRatings] = useState([]);
const [selectedGenres, setSelectedGenres] = useState([]);
const [selectedAuthors, setSelectedAuthors] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
const [loading, setLoading] = useState(true);
const booksPerPage = 10;
const location = useLocation();
useEffect(() => {
const fetchInitialData = async () => {
try {
await fetchData();
await fetchAuthors();
fetchBooks();
} catch (error) {
console.error("Error fetching initial data:", error);
}
};
fetchInitialData();
}, []);
useEffect(() => {
const searchParams = new URLSearchParams(location.search);
const genreParam = searchParams.get("genre");
if (genreParam) {
setSelectedGenres([genreParam]);
}
const sortBy = searchParams.get("sortBy") || "title";
const sortDir = searchParams.get("sortDir") || "ASC";
fetchBooks(currentPage - 1, sortBy, sortDir);
}, [location.search, currentPage]);
useEffect(() => {
filterBooks();
}, [selectedGenres, selectedAuthors, selectedRatings, books]);
const fetchData = async () => {
try {
const response = await fetch(`${BASE_URL}api/public/categories/getall`, {
headers: { "ngrok-skip-browser-warning": "69420" }
});
if (!response.ok) throw new Error("Error fetching categories");
const data = await response.json();
setCategories(data);
} catch (error) {
console.error("Error fetching categories:", error);
}
};
const fetchBooks = async (page = 0, sortBy = "title", sortDir = "ASC") => {
setLoading(true);
try {
const response = await fetch(`${BASE_URL}api/public/books/pagedAndSorted?page=${page}&size=${booksPerPage}&sortBy=${sortBy}&sortDir=${sortDir}`, {
headers: { "ngrok-skip-browser-warning": "69420" }
});
if (!response.ok) throw new Error("Error fetching books");
const data = await response.json();
const booksWithRatings = await Promise.all(
data.content.map(async (book) => {
const rating = await fetchRating(book.id);
return { ...book, averageRating: rating.averageRating };
})
);
setBooks(booksWithRatings);
setFilteredBooks(booksWithRatings);
setTotalPages(data.totalPages);
} catch (error) {
console.error("Error fetching books:", error);
} finally {
setLoading(false);
}
};
const fetchRating = async (bookId) => {
try {
const response = await fetch(`${BASE_URL}api/public/review/ave-rat/${bookId}/average-rating`, {
headers: { "ngrok-skip-browser-warning": "69420" }
});
if (!response.ok) throw new Error("Error fetching rating");
return await response.json();
} catch (error) {
console.error("Error fetching rating:", error);
return { averageRating: 0 };
}
};
const fetchAuthors = async () => {
try {
const response = await fetch(`${BASE_URL}api/public/author/getAll`, {
headers: { "ngrok-skip-browser-warning": "69420" }
});
if (!response.ok) throw new Error("Error fetching authors");
const data = await response.json();
setAuthors(data);
} catch (error) {
console.error("Error fetching authors:", error);
}
};
const filterBooks = () => {
let filtered = [...books];
if (selectedGenres.length > 0) {
filtered = filtered.filter((book) => selectedGenres.includes(book.category.name));
}
if (selectedAuthors.length > 0) {
filtered = filtered.filter((book) => selectedAuthors.includes(book.author.fullName));
}
if (selectedRatings.length > 0) {
filtered = filtered.filter((book) => book.averageRating >= selectedRatings[0]);
}
setFilteredBooks(filtered);
};
const handleCategoryClick = (categoryId) => {
const category = categories.find((cat) => cat.id === categoryId);
if (category) {
setSelectedGenres((prevGenres) =>
prevGenres.includes(category.name)
? prevGenres.filter((g) => g !== category.name)
: [...prevGenres, category.name]
);
}
};
const handleAuthorClick = (authorId) => {
const author = authors.find((auth) => auth.id === authorId);
if (author) {
setSelectedAuthors((prevAuthors) =>
prevAuthors.includes(author.fullName)
? prevAuthors.filter((a) => a !== author.fullName)
: [...prevAuthors, author.fullName]
);
}
};
const handleRatingClick = (rating) => {
setSelectedRatings((prevRatings) =>
prevRatings.length > 0 && prevRatings[0] === rating ? [] : [rating]
);
};
const handlePageChange = (page) => {
setCurrentPage(page);
fetchBooks(page - 1);
};
const handleRemoveFilter = (type, value) => {
if (type === "genre") {
setSelectedGenres((prevGenres) => prevGenres.filter((g) => g !== value));
} else if (type === "author") {
setSelectedAuthors((prevAuthors) => prevAuthors.filter((a) => a !== value));
} else if (type === "rating") {
setSelectedRatings([]);
}
};
const renderRating = (rating) => {
const fullStars = Math.floor(rating);
const emptyStars = 5 - fullStars;
return (
<>
{"★".repeat(fullStars)}
{"☆".repeat(emptyStars)}
</>
);
};
const handleClearAll = () => {
setSelectedRatings([]);
setSelectedGenres([]);
setSelectedAuthors([]);
};
if (loading) {
return <Loader />;
}
return (
<div className="app-container">
<Navbar />
<div className="content-wrapper">
<div className="sidebar-list">
<img
className="logo"
src={logo}
alt="Logo"
style={{ width: "130px", height: "70px" }}
/>
<div className="rating-filter">
<h2>Ratings</h2>
{[4, 3, 2, 1].map((rating) => (
<p key={rating} onClick={() => handleRatingClick(rating)}>
{renderRating(rating)} & Up
</p>
))}
</div>
<div className="genre-list">
<h2>Genre</h2>
{categories.map((category) => (
<button
key={category.id}
onClick={() => handleCategoryClick(category.id)}
className={
selectedGenres.includes(category.name) ? "active" : ""
}
>
{category.name}
</button>
))}
</div>
<div className="author-list">
<h2>Authors</h2>
{authors.map((author) => (
<button
key={author.id}
onClick={() => handleAuthorClick(author.id)}
className={
selectedAuthors.includes(author.fullName) ? "active" : ""
}
>
{author.fullName}
</button>
))}
</div>
</div>
<div className="main-list">
<h2 className="list-title">Product List</h2>
<div className="filtered-by">
<p>Filtered By:</p>
<div className="filtered-options">
{selectedGenres.map((genre) => (
<button
key={genre}
onClick={() => handleRemoveFilter("genre", genre)}
>
{genre}{" "}
<FontAwesomeIcon icon={faTimes} className="remove-icon" />
</button>
))}
{selectedAuthors.map((author) => (
<button
key={author}
onClick={() => handleRemoveFilter("author", author)}
>
{author}{" "}
<FontAwesomeIcon icon={faTimes} className="remove-icon" />
</button>
))}
{selectedRatings.map((rating) => (
<button
key={rating}
onClick={() => handleRemoveFilter("rating")}
>
{renderRating(rating)} & Up{" "}
<FontAwesomeIcon icon={faTimes} className="remove-icon" />
</button>
))}
</div>
<p className="clear-all" onClick={handleClearAll}>
Clear All
</p>
</div>
<div className="books-list">
{filteredBooks.length > 0 ? (
filteredBooks.map((book) => (
<div key={book.id} className="booklist">
<Link to={`/book/${book.id}`} className="book-link">
<img
src={book.cover}
alt={book.title}
className="product-image"
/>
<h3 className="book-title">{book.title}</h3>
<p className="book-rating">
{renderRating(book.averageRating)}
</p>
</Link>
</div>
))
) : (
<p>No books found.</p>
)}
</div>
<div className="pagination">
<button
className="prev"
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
>
<FontAwesomeIcon icon={faArrowLeft} />
</button>
{Array.from({ length: totalPages }, (_, i) => (
<button
key={i + 1}
onClick={() => handlePageChange(i + 1)}
className={currentPage === i + 1 ? "active" : ""}
>
{i + 1}
</button>
))}
<button
className="next"
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
>
<FontAwesomeIcon icon={faArrowRight} />
</button>
</div>
</div>
</div>
<Footer />
</div>
);
};
export default ProductList;
Editor is loading...
Leave a Comment