This Project helps any one who wants to search for a book online and build an online library. It provides information details on the author, pages and description for each book. The main focus was the front-end interface of the application.
What I used in my project
Front-End
It is built with react JavaScript framework, react hooks for state management, react-router-dom or client-side routing, Axios to fetch Api and CSS to style the UI.
Back-End
Books in the library were populated with Draftbit books API.
App Features
- Enables user manually browse through the book library
- Enables the user to read more about the book when the cover image is clicked.
- Enable users add books to a favorite list
Project Overview
I created a web library application. It lets you browse through a collection of books and add books to favorite, the favorite tab acts as the user's library.
Setting up the application
The Landing page and setting up the routes
Routing
<Route path="/" element={<LandingPage />}/>
{/* <Route path="/About" element={<About />}/> */}
<Route path="/Library" element={<GeneralLibrary />}>
<Route index element={<Booklist />}/>
<Route path="Favorite-Books" element={<Favorites />}/>
</Route>
<Route path="Favorite-Books" element={<Favorites />}/>
<Route path="/books/:id" element={<BookDetails />} />
Defining context
The context defines add to favorite and remove books from favorite functions.
import React, { useContext, useState, createContext } from "react";
const AppContext = createContext(null);
export const useAppContext = () => {
const context = useContext(AppContext);
if (context === undefined) {
throw new Error("AppContext must be within contextprovider");
}
return context;
};
const AppContextProvider = ({ children }) => {
const [fav, setFavs] = useState([]);
const addToFavorites = (book) => {
const oldFavs = [...fav];
const newFavs = oldFavs.concat(book);
setFavs(newFavs);
};
const removeFromFavorites = (id) => {
const oldFavs = [...fav];
const newFavs = oldFavs.filter((book) => book.id !== id);
setFavs(newFavs);
};
return (
<AppContext.Provider value={{ fav, addToFavorites, removeFromFavorites }}>
{children}
</AppContext.Provider>
);
};
export default AppContextProvider;
The Library
Books in the library were populated with Draftbit books API.
import React, { useState, useEffect } from "react";
import { LIBRARY_URL } from "./LibraryApi/LibraryApi";
import axios from "axios";
import { useAppContext } from "../context/Context";
import { useNavigate } from "react-router-dom";
import "../styles/App.css";
import Loader from "./Loader";
import Pagnation from "./Pagnation";
import AddFavorite from "./interactions/add";
import RemoveFavorite from "./interactions/remove";
const Booklist = () => {
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [books, setBooks] = useState([]);
const [postPerPage, setPostPerPage] = useState([12]);
const [currentPage, setcurrentPage] = useState([1]);
const { fav, addToFavorites, removeFromFavorites } = useAppContext();
const checkFavoriteBooks = (id) => {
const Checker = fav.some((book) => book.id === id);
return Checker;
};
const lastPostIndex = currentPage * postPerPage;
const firstpostIndex = lastPostIndex - postPerPage;
const currentPost = books.slice(firstpostIndex, lastPostIndex);
useEffect(() => {
setLoading(true);
axios
.get(LIBRARY_URL)
.then((response) => {
setBooks(response.data);
setLoading(false);
})
.catch((err) => console.log(err));
}, []);
return (
<section>
<div className="Book-list">
{loading ? (
<Loader />
) : (
currentPost.map((book) => {
return (
<div key={book.id} className="book-cover">
<div>
<img
className="individual-cover"
onClick={() => navigate(`/books/${book.id}`)}
src={book.image_url}
alt={`Cover of ${book.title}`}
/>
</div>
<div className="display-book-details">
<div className="book-title">
<h4> {book.title}</h4>
</div>
<div className="author-display">
<span>{book.num_pages}</span> <span>|</span>{" "}
<span>{book.format}</span>
</div>
<div className="author-display">
{checkFavoriteBooks(book.id) ? (
<button onClick={() => removeFromFavorites(book.id)}>
{<RemoveFavorite />}
</button>
) : (
<button onClick={() => addToFavorites(book)}>
{<AddFavorite />}
</button>
)}
</div>
</div>
</div>
);
})
)}
</div>
<div className="paginate">
<Pagnation
totalPosts={books.length}
postsPerPage={postPerPage}
setCurrentPage={setcurrentPage}
currentPage={currentPage}
/>
</div>
</section>
);
};
export default Booklist;
Defining the user library-Favorite books
import React from "react";
import { useAppContext } from "../context/Context";
import AddFavorite from "./interactions/add";
import RemoveFavorite from "./interactions/remove";
const FavoriteBooks = () => {
const { fav, addToFavorites, removeFromFavorites } = useAppContext();
const checkFavoriteBooks = (id) => {
const Checker = fav.some((book) => book.id === id);
return Checker;
};
return (
<div className="fav-page">
<span className="for-you-title">Your Library</span>
<div className="fav-books-container">
{fav.length > 0 ? (
fav.map((book) => {
return (
<div key={book.id} className="fav-books">
<div>
<img
className="fav-book-img"
src={book.image_url}
alt={`Cover of ${book.title}`}
/>
</div>
<div>
<div className="fav-book-title">
<span> {book.title}</span>
</div>
<div className="fav-button">
{checkFavoriteBooks(book.id) ? (
<button onClick={() => removeFromFavorites(book.id)}>
{<RemoveFavorite />}
</button>
) : (
<button onClick={() => addToFavorites(book)}>
{<AddFavorite />}
</button>
)}
</div>
</div>
</div>
);
})
) : (
<div className="favorite-books-empty"></div>
)}
</div>
</div>
);
};
export default FavoriteBooks;
Getting details to each book
import React from "react";
import "../styles/App.css";
import "../styles/smallScreens.css";
import { useParams, useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import axios from "axios";
import { Book_Details_Url } from "./LibraryApi/LibraryApi";
import NavBar from "./NavBar";
import Footer from "./Footer";
const BookDetails = () => {
const navigate = useNavigate();
const goBack = () => {
navigate("/Library");
};
const { id } = useParams();
const [book, setBook] = useState({});
useEffect(() => {
axios
.get(`${Book_Details_Url}/${id}`)
.then((response) => {
setBook(response.data);
})
.catch((err) => console.log(err));
}, [id]);
return (
<>
<NavBar />
<main className="detail-background">
<button className="detail-button" onClick={goBack}>
{" "}
↖ Go back
</button>
<div className="detail-container">
<div className="detail-card">
<div className="detail-img">
<img src={book?.image_url} alt={book?.title} />
</div>
<div className="detail-text">
<p className="detail-book-titles">{book?.title}</p>
<p className="detail-titles-author">Author | {book?.authors}</p>
<div className="detail-description">
<p className="description-text">{book?.description}</p>
<p className="description-pages">Pages | {book?.num_pages} </p>
<p className="description-genres">
Genres | {book?.genre_list}
</p>
</div>
<div className="detail-quote">
<blockquote>
<p>{book?.Quote1}</p>
<cite>{book?.authors}</cite>
</blockquote>
</div>
</div>
</div>
</div>
</main>
<Footer />
</>
);
};
export default BookDetails;
Some challenges Challenges I faced
During the planning phase of the project, I planned to add a search feature to the app which would have enabled users to search for a book based on the book title or author, this was meant to be done with the goodreads API. I however plan on working on the project further to include the search feature and more book ordering categories.
Find full code in here
Top comments (0)