Skip to content
This repository was archived by the owner on Jun 28, 2022. It is now read-only.

committing reads project with enables user to be interactive #156

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"bootstrap": "4.6.0",
"prop-types": "^15.6.2",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-router-dom": "^5.3.0",
"react-scripts": "2.1.1"
},
"scripts": {
Expand Down
329 changes: 138 additions & 191 deletions src/App.js

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions src/components/book.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from "react";
import { Link } from "react-router-dom";

/**
* functional component for rendering book details
*
* has function contains event firing at book sehlf selection change
*
* @param {*} props sending from component parent
* @returns final book rendering product
*
*/
const Book = (props) => {

//handel images if not define so return alt text or black book image
const {book, shelfName, bookSender } = {...props};
const imagePath = (book.bookImage !=='alt')? `${(book.bookImage)}`: '../icons/1021547.png';

return <div className="book">
<div className="book-top">

<Link to={bookSender === 'search'?`bookDetails/${book.id}`:`components/bookDetails/${book.id}` } >
<div className="book-cover" style={{ width: 128, height: 193, backgroundImage: `url(${imagePath})` }} />
</Link>

<div className="book-shelf-changer">
<select onChange={(ev) => props.onUpdateBookShelf(ev.target.value, props.book)} defaultValue={`${shelfName}`}>
<option value="move" disabled>
Move to...
</option>
<option value="currentlyReading">Currently Reading</option>
<option value="wantToRead">Want to Read</option>
<option value="read">Read</option>
<option value="none">None</option>
</select>
</div>
</div>
<div className="book-title">{book.title}</div>
<div className="book-authors"> {[...book.authors]} </div>
</div>;
};

export default Book;
101 changes: 101 additions & 0 deletions src/components/bookDetails.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { Component } from 'react';
import { get as getBook} from '../BooksAPI';
import 'bootstrap/dist/css/bootstrap.min.css';

class BookDetails extends Component {

state={
book:{
title: '',
authors: [],
publisher: '',
description: '',
shelf: '',
image: '',
categories: [],
pageCount: '',
language: ''
},
}

componentDidMount = () => {
const { match } = { ...this.props };
const id = match.params.id;
getBook(id).then((res) => {
const categories = res.categories ? [...res.categories] : [];
const book = {
id: res.id,
title: res.title,
authors: [...res.authors],
publisher: res.publisher,
description: res.description,
shelf: res.shelf,
image: res.imageLinks.thumbnail,
categories: [...categories],
pageCount: res.pageCount,
language: res.language
}
this.setState({ book });
});
};

render() {
const book = {...this.state.book};
return <div className='container'>
<div className="row d-flex justify-content-center m-3 p-3 bg-light" >
<div className="row col-md-6">
<h5 className="card-title bd-title" style={{fontSize:"calc(1.425rem + 2.1vw)", fontWeight:"bold"}}>{book.title}</h5>
<div className="card-text row">
<div className="">
<p>description: {book.description.slice(0, 250)} </p>
<div className='d-flex justify-content-between pt-4'>
<span>
<p >
publisher {book.publisher}
</p>
</span>
<span>
<p >
page Count: {book.pageCount}
</p>
</span>
</div>
<div className='d-flex justify-content-between pr-1'>
<p >
authors: {book.authors}
</p>
<p >
categories: {book.categories}
</p>
</div>
<p style={{ display: "inline" }}>
language: {book.language}
</p>
</div>
</div>
<div className="col-1" />
</div>
<div className='row col-5 d-flex justify-content-end align-items-end mb-5 pb-4'>
<div className="d-absolute " style={{ }}>
<div className='d-flex align-items-center'>
<img className="img-thumbnail" src={book.image} alt="Card cap" />
<div className="book-shelf-changer d-absolute">
<select onChange={(ev) => this.props.onUpdateBookShelf(ev.target.value, book)} value={book.shelf}>
<option value="move" disabled>
Move to...
</option>
<option value="currentlyReading">Currently Reading</option>
<option value="wantToRead">Want to Read</option>
<option value="read">Read</option>
<option value="none">None</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
}
}

export default BookDetails;
108 changes: 108 additions & 0 deletions src/components/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import SearchBooks from "./searchBooksRender";
import * as BooksAPI from "../BooksAPI";

/**
* componet form handle search books and childs is searchBooksRender which render the search result books
*/
class Search extends Component {
/**
*
* @param {*} props hold bookd of parent and merge it with search books to define its shelfes
*/
constructor(props) {
super(props);
this.state.books = props.books;
}
//books for holding search result books
state = { query: "", books: [] };


/**
* handle chaning in search text
* @param {*} ev is event parameter that hold event firing info
*/
getSearchText = (ev) => {
const searchTxt = ev.target.value.trim();
if (searchTxt !== "") {
//fetch API for getting matched book after 500ms
BooksAPI.search(searchTxt)
.then((res) => {
if (res.length > 0) {
let [books, shelfs] = [[], []];
res.forEach((cur) => {
if (!shelfs.includes(cur.shelf)) shelfs.push(cur.shelf);

const categories = cur.categories ? [...cur.categories] : [];
const authors = cur.authors ? [...cur.authors] : [];
const img = cur.imageLinks ? cur.imageLinks.smallThumbnail : "alt";

const book = {
id: cur.id,
title: cur.title,
shelf: cur.shelf,
bookImage: img,
categories: [...categories],
authors: [...authors],
};
const index = this.state.books.indexOf(book);
index === -1 && books.push(book);
});
this.setState({ books });
}
else
{
this.setState({ books: [] });
alert("No Books matched the search");
}
})
.catch((er) => {
console.log(er);
// alert("invalid searche");
});
}
this.setState({ query: searchTxt });
};

/**
* calling parent event handler for shelf chaning
* @param {*} bookState : current shelf name
* @param {*} book : book object
*/
onUpdateBookShelf = (bookState, book) => {
this.props.onUpdateBookShelf(bookState, book);
};

render() {
return (
<div className="search-books">
<div className="search-books-bar">
<Link to="/">
<button className="close-search"> Close </button>
</Link>
<div className="search-books-input-wrapper">
<input
type="text"
onChange={this.getSearchText}
placeholder="Search by title or author"
/>
</div>
</div>

{this.state.query !== "" && (
<div className="search-books-results">
<ol className="books-grid" />
<SearchBooks
books={this.state.books}
shelfs={this.props.shelfs}
onUpdateBookShelf={this.onUpdateBookShelf}
/>
</div>
)}
</div>
);
}
}

export default Search;
60 changes: 60 additions & 0 deletions src/components/searchBooksRender.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { Component } from "react";

import Book from "./book";

/**
* rendering search result books
*/
class SearchBooks extends Component {

/**
* handle shelfs books and defined each shelf for each book
* @param {*} bookShelf sending Book shelf for shefed books
* @returns shelf or none if shelf doesn't define
*/
getShelf = ( bookShelf) => {
if (!bookShelf)
return 'none';
const [shelf] = [...this.props.shelfs.filter(shelf => shelf === bookShelf)];
return shelf;
}

/**
* calling parent event handler for shelf chaning
* @param {*} bookState : current shelf name
* @param {*} book : book object
*/
onUpdateBookShelf =( bookState ,book )=>{
this.props.onUpdateBookShelf( bookState ,book);
}

render() {
const { books } = { ...this.props };
return (
<div className="list-books-content">
<div className="list-books-content">
<div className="bookshelf-books">
<ol className="books-grid">
{books.map((book) => {
const shelf = this.getShelf(book.shelf);
return (
<li key={book.id}>
<Book
book={book}
shelfName = {shelf}
bookSender = 'search'
// authors={this.getAuthorsByID(book.authors)}
onUpdateBookShelf = {this.onUpdateBookShelf}
/>
</li>
);
})}
</ol>
</div>
</div>
</div>
);
}
}

export default SearchBooks;
Loading