Agw-a
Posted on December 12, 2022
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
Posted on December 12, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.