diff --git a/book-review-platform/src/Routes/Router.js b/book-review-platform/src/Routes/Router.js index eb569b46..946369bd 100644 --- a/book-review-platform/src/Routes/Router.js +++ b/book-review-platform/src/Routes/Router.js @@ -40,6 +40,7 @@ const Router = () => { } /> + } /> } /> } /> } /> diff --git a/book-review-platform/src/components/MainContent.js b/book-review-platform/src/components/MainContent.js index 6723de7d..ba15a7ce 100644 --- a/book-review-platform/src/components/MainContent.js +++ b/book-review-platform/src/components/MainContent.js @@ -1,9 +1,11 @@ import React, { useState, useEffect } from "react"; +import { useParams, useLocation } from "react-router-dom"; import "../styles/MainContent.css"; import { SearchBar } from "./SearchBar"; import { BookCard } from "./BookCard"; import { getFeaturedBooks, + getBooksBySubject, searchBooks, transformGoogleBooksResponse, addMockReviewData, @@ -62,6 +64,8 @@ const sampleBooks = [ ]; const MainContent = () => { + const { genre } = useParams(); + const location = useLocation(); const [books, setBooks] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -90,19 +94,28 @@ const MainContent = () => { // Fetch featured books on component mount // we'll expose a fetch helper so nav can trigger refresh directly + // Fetch books logic const fetchBooksHelper = async () => { try { setLoading(true); setError(null); - const response = await getFeaturedBooks(12); + setSearchQuery(""); // Reset search when switching genres/home + + let response; + if (genre) { + response = await getBooksBySubject(genre, 12); + } else { + response = await getFeaturedBooks(12); + } + const transformedBooks = transformGoogleBooksResponse(response); const booksWithReviews = transformedBooks.map(addMockReviewData); setBooks(booksWithReviews); } catch (err) { setError("Failed to fetch books. Please try again later."); console.error("Error fetching books:", err); - // Fallback to sample data if API fails - setBooks(sampleBooks); + // Fallback to sample data if API fails and we are on home + if (!genre) setBooks(sampleBooks); } finally { setLoading(false); } @@ -110,7 +123,7 @@ const MainContent = () => { useEffect(() => { fetchBooksHelper(); - }, []); + }, [genre, location.pathname]); // Re-run when genre or path changes // Handle search functionality const handleSearch = async (query) => { @@ -187,6 +200,8 @@ const MainContent = () => {

{searchQuery ? `Search Results for "${searchQuery}"` + : genre + ? `Exploring ${genre.charAt(0).toUpperCase() + genre.slice(1)}` : "Discover Your Next Favorite Book"}

diff --git a/book-review-platform/src/components/Sidebar.js b/book-review-platform/src/components/Sidebar.js index f9c1aaa0..400e9cc2 100644 --- a/book-review-platform/src/components/Sidebar.js +++ b/book-review-platform/src/components/Sidebar.js @@ -10,6 +10,16 @@ const navItems = [ { icon: User, label: "My Reviews", path: "/myreview" }, ]; +const genreItems = [ + "Fiction", + "Science", + "Fantasy", + "Mystery", + "History", + "Business", + "Cooking", +]; + const Sidebar = ({ currentPage, onNavigate, isDarkMode, onToggleDarkMode }) => { const navigate = useNavigate(); const location = useLocation(); @@ -107,6 +117,26 @@ const Sidebar = ({ currentPage, onNavigate, isDarkMode, onToggleDarkMode }) => { + +

+ +
Genres
+
    + {genreItems.map((genre) => { + const path = `/category/${genre.toLowerCase()}`; + const isActive = location.pathname === path; + return ( +
  • + +
  • + ); + })} +
diff --git a/book-review-platform/src/styles/Sidebar.css b/book-review-platform/src/styles/Sidebar.css index 03cd993e..cc6fb8d6 100644 --- a/book-review-platform/src/styles/Sidebar.css +++ b/book-review-platform/src/styles/Sidebar.css @@ -208,3 +208,42 @@ transform: translateX(20px); background: var(--primary-foreground); } + +.sidebar-divider { + height: 1px; + background: var(--sidebar-border); + margin: 1.5rem 0.5rem 1rem; + opacity: 0.5; +} + +.sidebar-section-label { + padding: 0 1rem 0.5rem; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--sidebar-foreground); + opacity: 0.6; +} + +.genre-list { + display: flex !important; + flex-direction: column !important; + gap: 0.25rem !important; +} + +.genre-link { + padding: 0.5rem 1rem !important; + font-size: 0.825rem !important; + opacity: 0.85; +} + +.genre-link:hover { + opacity: 1; +} + +.genre-link.active { + background: var(--sidebar-primary); + color: var(--sidebar-primary-foreground); + opacity: 1; +}