├── public
├── robots.txt
├── favicon.ico
├── manifest.json
└── index.html
├── src
├── stylesheets
│ ├── App.css
│ ├── Categories.css
│ ├── AddBook.css
│ ├── index.css
│ ├── Navbar.css
│ └── Books.css
├── redux
│ ├── books
│ │ ├── actionTypes.js
│ │ └── books.js
│ ├── categories
│ │ └── categories.js
│ └── configureStore.js
├── components
│ ├── Navbar.js
│ ├── Categories.js
│ ├── Books.js
│ ├── AddBook.js
│ └── Book.js
├── BooksContainer.js
└── index.js
├── images
├── bkstorecat.png
└── bkstorehome.png
├── .babelrc
├── .stylelintrc.json
├── .gitignore
├── .eslintrc.json
├── MIT.md
├── .github
└── workflows
│ └── linters.yml
├── package.json
└── README.md
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nemwel-Boniface/nemwelbookstore/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/stylesheets/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | margin: 0 5%;
3 | background-color: var(--pale-grey);
4 | }
5 |
--------------------------------------------------------------------------------
/images/bkstorecat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nemwel-Boniface/nemwelbookstore/HEAD/images/bkstorecat.png
--------------------------------------------------------------------------------
/images/bkstorehome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nemwel-Boniface/nemwelbookstore/HEAD/images/bkstorehome.png
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-react"
4 | ],
5 | "plugins": ["@babel/plugin-syntax-jsx"]
6 | }
--------------------------------------------------------------------------------
/src/redux/books/actionTypes.js:
--------------------------------------------------------------------------------
1 | export const ADDBOOK = 'addBook';
2 | export const REMOVEBOOK = 'removeBook';
3 | export const GETBOOK = 'getBook';
4 |
--------------------------------------------------------------------------------
/src/stylesheets/Categories.css:
--------------------------------------------------------------------------------
1 | .categories {
2 | text-align: center;
3 | margin: 40px;
4 | }
5 |
6 | .categories button {
7 | width: 300px;
8 | }
9 |
--------------------------------------------------------------------------------
/src/stylesheets/AddBook.css:
--------------------------------------------------------------------------------
1 |
2 | /* Styling for the form */
3 | .addnewbook {
4 | margin: 40px 4%;
5 | }
6 |
7 | .form input {
8 | width: 24%;
9 | padding: 10px;
10 | margin-right: 30px;
11 | }
12 |
13 | .form select {
14 | width: 25%;
15 | padding: 10px;
16 | margin-right: 30px;
17 | }
18 |
19 | .form button {
20 | width: 12%;
21 | }
22 |
--------------------------------------------------------------------------------
/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["stylelint-config-standard"],
3 | "plugins": ["stylelint-scss", "stylelint-csstree-validator"],
4 | "rules": {
5 | "at-rule-no-unknown": null,
6 | "scss/at-rule-no-unknown": true,
7 | "csstree/validator": true
8 | },
9 | "ignoreFiles": ["build/**", "dist/**", "**/reset*.css", "**/bootstrap*.css", "**/*.js", "**/*.jsx"]
10 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/redux/categories/categories.js:
--------------------------------------------------------------------------------
1 | const CHECKSTATUS = 'checkStatus';
2 |
3 | const categoriesReducer = (state = [], action) => {
4 | switch (action.type) {
5 | case CHECKSTATUS:
6 | return [...state, 'Under construction'];
7 | default:
8 | return state;
9 | }
10 | };
11 |
12 | export const checkStatus = (book) => ({
13 | type: CHECKSTATUS,
14 | book,
15 | });
16 |
17 | export default categoriesReducer;
18 |
--------------------------------------------------------------------------------
/src/redux/configureStore.js:
--------------------------------------------------------------------------------
1 | import { applyMiddleware, combineReducers, createStore } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import booksReducer from './books/books';
4 | import categoriesReducer from './categories/categories';
5 |
6 | const mainReducer = combineReducers({
7 | booksReducer,
8 | categoriesReducer,
9 | });
10 |
11 | const store = createStore(
12 | mainReducer,
13 | applyMiddleware(thunk),
14 | );
15 |
16 | export default store;
17 |
--------------------------------------------------------------------------------
/src/components/Navbar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | const Navbar = () => (
5 |
6 |
14 |
15 | );
16 | export default Navbar;
17 |
--------------------------------------------------------------------------------
/src/stylesheets/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family:
4 | -apple-system,
5 | BlinkMacSystemFont,
6 | 'Segoe UI',
7 | 'Roboto',
8 | 'Oxygen',
9 | 'Ubuntu',
10 | 'Cantarell',
11 | 'Fira Sans',
12 | 'Droid Sans',
13 | 'Helvetica Neue',
14 | sans-serif;
15 | -webkit-font-smoothing: antialiased;
16 | -moz-osx-font-smoothing: grayscale;
17 | }
18 |
19 | code {
20 | font-family:
21 | source-code-pro,
22 | Menlo,
23 | Monaco,
24 | Consolas,
25 | 'Courier New',
26 | monospace;
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/Categories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 | import { checkStatus } from '../redux/categories/categories';
4 |
5 | function Categories() {
6 | const dispatch = useDispatch();
7 | const currentStatus = useSelector((state) => state.categoriesReducer);
8 | return (
9 |
10 |
11 | {currentStatus}
12 |
13 | );
14 | }
15 |
16 | export default Categories;
17 |
--------------------------------------------------------------------------------
/src/BooksContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Routes, Route } from 'react-router-dom';
3 | import './stylesheets/App.css';
4 | import Navbar from './components/Navbar';
5 | import Books from './components/Books';
6 | import Categories from './components/Categories';
7 |
8 | const BooksContainer = () => (
9 |
10 |
11 |
12 | } />
13 | } />
14 |
15 |
16 | );
17 |
18 | export default BooksContainer;
19 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { BrowserRouter } from 'react-router-dom';
4 | import { Provider } from 'react-redux';
5 | import store from './redux/configureStore';
6 | import './stylesheets/index.css';
7 | import './stylesheets/Navbar.css';
8 | import './stylesheets/Books.css';
9 | import './stylesheets/AddBook.css';
10 | import './stylesheets/Categories.css';
11 | import BooksContainer from './BooksContainer';
12 |
13 | ReactDOM.render(
14 |
15 |
16 |
17 |
18 | ,
19 | document.getElementById('root'),
20 | );
21 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "jest": true
6 | },
7 | "parser": "@babel/eslint-parser",
8 | "parserOptions": {
9 | "ecmaFeatures": {
10 | "jsx": true
11 | },
12 | "ecmaVersion": 2018,
13 | "sourceType": "module"
14 | },
15 | "extends": ["airbnb", "plugin:react/recommended"],
16 | "plugins": ["react"],
17 | "rules": {
18 | "react/jsx-filename-extension": ["warn", { "extensions": [".js", ".jsx"] }],
19 | "react/react-in-jsx-scope": "off",
20 | "import/no-unresolved": "off",
21 | "no-shadow": "off"
22 | },
23 | "ignorePatterns": [
24 | "dist/",
25 | "build/"
26 | ]
27 | }
--------------------------------------------------------------------------------
/src/components/Books.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 | import AddBook from './AddBook';
4 | import Book from './Book';
5 | import { getBookFromAPI } from '../redux/books/books';
6 |
7 | const Books = () => {
8 | const books = useSelector((state) => state.booksReducer);
9 | const dispatch = useDispatch();
10 |
11 | useEffect(() => {
12 | dispatch(getBookFromAPI());
13 | }, []);
14 | return (
15 |
16 |
17 | {books.map((book) => (
18 |
19 | ))}
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | export default Books;
27 |
--------------------------------------------------------------------------------
/src/stylesheets/Navbar.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | width: 100%;
3 | height: 150px;
4 | background-color: #fff;
5 | }
6 |
7 | .navbar nav {
8 | display: flex;
9 | position: relative;
10 | }
11 |
12 | .navbar nav h1 {
13 | font-size: 37px;
14 | padding: 15px;
15 | color: #2274ee;
16 | margin-left: 70px;
17 | }
18 |
19 | .navbar nav ul {
20 | display: flex;
21 | margin: auto 10%;
22 | }
23 |
24 | .navbar nav ul li {
25 | list-style-type: none;
26 | margin-right: 20px;
27 | }
28 |
29 | .navbar nav ul li a {
30 | font-size: 19px;
31 | color: #000;
32 | opacity: 0.5;
33 | text-decoration: none;
34 | }
35 |
36 | .navbar nav ul li a:active {
37 | opacity: 1;
38 | }
39 |
40 | .navbar nav ul li a:hover {
41 | opacity: 1;
42 | }
43 |
44 | .navbar nav ul li a:focus {
45 | opacity: 1;
46 | }
47 |
48 | .navbar nav i {
49 | cursor: pointer;
50 | margin: auto 0;
51 | display: flex;
52 | position: relative;
53 | right: -19%;
54 | color: #2274ee;
55 | padding: 15px;
56 | border-radius: 50%;
57 | border: 1px solid #2274ee;
58 | }
59 |
--------------------------------------------------------------------------------
/MIT.md:
--------------------------------------------------------------------------------
1 | ## Copyright 2021, [Nemwel Boniface]
2 |
3 | ###### APP TYPE can be a webpage/website, a web app, a software and so on
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this [APP TYPE] and associated documentation files, to deal in the [APP TYPE] without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the [APP TYPE], and to permit persons to whom the [APP TYPE] is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the [APP TYPE].
8 |
9 | THE [APP TYPE] IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE [APP TYPE] OR THE USE OR OTHER DEALINGS IN THE [APP TYPE].
--------------------------------------------------------------------------------
/.github/workflows/linters.yml:
--------------------------------------------------------------------------------
1 | name: Linters
2 |
3 | on: pull_request
4 |
5 | env:
6 | FORCE_COLOR: 1
7 |
8 | jobs:
9 | eslint:
10 | name: ESLint
11 | runs-on: ubuntu-18.04
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: actions/setup-node@v1
15 | with:
16 | node-version: "12.x"
17 | - name: Setup ESLint
18 | run: |
19 | npm install --save-dev eslint@7.x eslint-config-airbnb@18.x eslint-plugin-import@2.x eslint-plugin-jsx-a11y@6.x eslint-plugin-react@7.x eslint-plugin-react-hooks@4.x @babel/eslint-parser@7.x @babel/core@7.x @babel/plugin-syntax-jsx@7.x @babel/preset-env@7.x @babel/preset-react@7.x
20 | [ -f .eslintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/react-redux/.eslintrc.json
21 | [ -f .babelrc ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/react-redux/.babelrc
22 | - name: ESLint Report
23 | run: npx eslint .
24 | stylelint:
25 | name: Stylelint
26 | runs-on: ubuntu-18.04
27 | steps:
28 | - uses: actions/checkout@v2
29 | - uses: actions/setup-node@v1
30 | with:
31 | node-version: "12.x"
32 | - name: Setup Stylelint
33 | run: |
34 | npm install --save-dev stylelint@13.x stylelint-scss@3.x stylelint-config-standard@21.x stylelint-csstree-validator@1.x
35 | [ -f .stylelintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/react-redux/.stylelintrc.json
36 | - name: Stylelint Report
37 | run: npx stylelint "**/*.{css,scss}"
--------------------------------------------------------------------------------
/src/components/AddBook.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { useDispatch } from 'react-redux';
3 | import { addNewBook } from '../redux/books/books';
4 |
5 | const AddBook = () => {
6 | const dispatch = useDispatch();
7 | const [author, setAuthor] = useState('');
8 | const [title, setTitle] = useState('');
9 |
10 | const handleSubmit = (e) => {
11 | e.preventDefault();
12 | const newBook = {
13 | item_id: Math.floor(Math.random() * 51),
14 | author,
15 | title,
16 | category: document.getElementById('bookgenre').value,
17 | };
18 | dispatch(addNewBook(newBook));
19 | setAuthor('');
20 | setTitle('');
21 | };
22 |
23 | return (
24 |
25 |
ADD NEW BOOK
26 |
41 |
42 | );
43 | };
44 |
45 | export default AddBook;
46 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nemwelbookstore",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.4",
8 | "@testing-library/user-event": "^13.5.0",
9 | "axios": "^0.26.1",
10 | "prop-types": "^15.8.1",
11 | "react": "^17.0.2",
12 | "react-dom": "^17.0.2",
13 | "react-redux": "^7.2.6",
14 | "react-router-dom": "^6.2.2",
15 | "react-scripts": "5.0.0",
16 | "redux": "^4.1.2",
17 | "redux-thunk": "^2.4.1",
18 | "web-vitals": "^2.1.4"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | },
44 | "devDependencies": {
45 | "@babel/core": "^7.17.8",
46 | "@babel/eslint-parser": "^7.17.0",
47 | "@babel/plugin-syntax-jsx": "^7.16.7",
48 | "@babel/preset-react": "^7.16.7",
49 | "eslint": "^7.32.0",
50 | "eslint-config-airbnb": "^18.2.1",
51 | "eslint-plugin-import": "^2.25.4",
52 | "eslint-plugin-jsx-a11y": "^6.5.1",
53 | "eslint-plugin-react": "^7.29.4",
54 | "eslint-plugin-react-hooks": "^4.3.0",
55 | "stylelint": "^13.13.1",
56 | "stylelint-config-standard": "^21.0.0",
57 | "stylelint-csstree-validator": "^1.9.0",
58 | "stylelint-scss": "^3.21.0"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Nemwel Bookstore
4 |
5 | Nemwel Bookstore is a CRUD application for listing books built with React and Redux. It posts and retrieved books information from an API.
6 | It currently allows you to:
7 | - Add a new book allowing you to specify a book's:
8 | - Author
9 | - Title
10 | - Genre
11 | - Remove a specific book from the list
12 |
13 |
14 | Enjoy!
15 |
16 | 
17 | 
18 |
19 | ## Built With
20 |
21 | - HTML5
22 | - CSS
23 | - Javascript
24 | - React
25 | - Redux
26 | - API
27 | - Webpack
28 | - Linters
29 |
30 | ## Live Demo
31 |
32 | This project was deployed to two places:
33 | - [Netlify](https://dazzling-florentine-36e3d5.netlify.app/)
34 | - [Heroku]() none at the moment
35 |
36 |
37 | ## Getting Started
38 |
39 | To get a local copy up and running follow these simple example steps.
40 |
41 | ## Install
42 |
43 | In your terminal, navigate to your current directory and run this code
44 |
45 | `git@github.com:Nemwel-Boniface/nemwelbookstore.git`
46 |
47 | Locate the directory in your file explorer
48 |
49 | `cd nemwelbookstore`
50 |
51 | Install npm or if installed already using this link
52 |
53 | `npm install`
54 |
55 | Start the web dev server depending on your configuration
56 |
57 | `npm start`
58 |
59 | The Project should now be live on your browser
60 |
61 | ## Authors
62 |
63 | 👤 **Author1**
64 |
65 | - GitHub: [@Nemwel-Boniface ](https://github.com/Nemwel-Boniface)
66 | - Twitter: [@nemwel_bonie](https://twitter.com/nemwel_bonie)
67 | - LinkedIn: [LinkedIn](https://www.linkedin.com/in/nemwel-nyandoro-aa1b2620b/)
68 |
69 | Contributions, issues, and feature requests are welcome!
70 |
71 | Feel free to check the [issues page](https://github.com/Nemwel-Boniface/nemwelbookstore/issues).
72 |
73 | ## Show your support
74 |
75 | Give a ⭐️ if you like this project!
76 | ## 📝 License
77 |
78 | This project is [MIT](./MIT.md) licensed.
79 |
--------------------------------------------------------------------------------
/src/redux/books/books.js:
--------------------------------------------------------------------------------
1 | // import axios from 'axios';
2 | import * as actions from './actionTypes';
3 |
4 | const baseURL = 'https://us-central1-bookstore-api-e63c8.cloudfunctions.net/bookstoreApi/apps/jpzpwwUdhjgaVhh6l7RE/books/';
5 |
6 | const bkArray = [];
7 |
8 | const booksReducer = (state = bkArray, action) => {
9 | switch (action.type) {
10 | case actions.ADDBOOK:
11 | return [
12 | ...state, action.payLoad,
13 | ];
14 | case actions.REMOVEBOOK:
15 | return [
16 | ...state.filter((book) => book.id !== action.id),
17 | ];
18 | case actions.GETBOOK:
19 | return [
20 | ...action.payLoad,
21 | ];
22 | default:
23 | return state;
24 | }
25 | };
26 |
27 | export const addNewBook = (book) => (dispatch) => fetch(
28 | baseURL, {
29 | method: 'POST',
30 | headers: {
31 | 'Content-type': 'application/json; charset=UTF-8',
32 | },
33 | body: JSON.stringify(book),
34 | },
35 | )
36 | .then((response) => {
37 | if (response.ok) {
38 | dispatch({
39 | type: actions.ADDBOOK,
40 | payLoad: book,
41 | });
42 | }
43 | });
44 |
45 | export const getBookFromAPI = () => (dispatch) => fetch(baseURL)
46 | .then((res) => res.json()).then((data) => {
47 | const books = Object.keys(data).map((key) => {
48 | const book = data[key][0];
49 | return {
50 | id: key,
51 | ...book,
52 | };
53 | });
54 | dispatch({ type: actions.GETBOOK, payLoad: books });
55 | }).catch(() => {});
56 |
57 | export const removeBook = (bookID) => async (dispatch) => {
58 | const body = {
59 | item_id: bookID,
60 | };
61 | return fetch(
62 | `${baseURL}${bookID}`, {
63 | method: 'DELETE',
64 | headers: {
65 | 'Content-type': 'application/json; charset=UTF-8',
66 | },
67 | body: JSON.stringify(body),
68 | },
69 | )
70 | .then((response) => {
71 | if (response.ok) {
72 | dispatch({
73 | type: actions.REMOVEBOOK,
74 | id: bookID,
75 | });
76 | }
77 | }).catch(() => {});
78 | };
79 |
80 | export default booksReducer;
81 |
--------------------------------------------------------------------------------
/src/components/Book.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Proptypes from 'prop-types';
3 | import { useDispatch } from 'react-redux';
4 | import { removeBook } from '../redux/books/books';
5 |
6 | function Book(props) {
7 | const { book } = props;
8 | const {
9 | category, title, author,
10 | } = book;
11 |
12 | const dispatch = useDispatch();
13 | return (
14 |
15 |
16 |
{category}
17 |
{title}
18 |
{author}
19 |
20 | -
21 |
22 |
23 | -
24 |
25 |
26 | -
27 |
28 |
29 |
30 |
31 |
32 |
33 |
44 |
45 |
46 |
75%
47 |
Completed
48 |
49 |
50 |
51 |
52 |
Current chapter
53 | Chapter 6
54 |
55 |
56 |
57 | );
58 | }
59 |
60 | Book.propTypes = {
61 | book: Proptypes.shape({
62 | category: Proptypes.string.isRequired,
63 | author: Proptypes.string.isRequired,
64 | title: Proptypes.string.isRequired,
65 | id: Proptypes.number.isRequired,
66 | }).isRequired,
67 | };
68 | export default Book;
69 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Nemwel Book store
38 |
39 |
40 |
41 |
42 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/stylesheets/Books.css:
--------------------------------------------------------------------------------
1 | .book {
2 | box-shadow: -1px -1px 15px 2px rgba(192, 181, 181, 1);
3 | margin: 40px 4%;
4 | padding: 15px;
5 | border-radius: 10px;
6 | display: flex;
7 | justify-content: space-between;
8 | }
9 |
10 | .blur {
11 | opacity: 0.5;
12 | }
13 |
14 | /* Styling for the left side of our book panel */
15 | .bookLeft .bookTitle {
16 | font-size: 36px;
17 | }
18 |
19 | .bookLeft .bookAuthor {
20 | color: #2274ee;
21 | }
22 |
23 | .bookLeft ul {
24 | display: flex;
25 | position: relative;
26 | left: -40px;
27 | }
28 |
29 | .bookLeft ul li {
30 | list-style-type: none;
31 | margin-right: 10px;
32 | padding-right: 10px;
33 | }
34 |
35 | .bookLeft ul .bar {
36 | border-right: 2px solid #000;
37 | }
38 |
39 | .leftbtn {
40 | outline: none;
41 | border: none;
42 | background-color: #fff;
43 | color: #227ded;
44 | cursor: pointer;
45 | }
46 |
47 | /* Styling for the middle part of our books panel containing the progress bar */
48 | .bookMiddle {
49 | display: flex;
50 | gap: 30px;
51 | margin: auto 0;
52 | }
53 |
54 | .percentages {
55 | margin-left: 30px;
56 | }
57 |
58 | .percentages h2 {
59 | font-size: 35px;
60 | }
61 |
62 | .circle-wrap {
63 | margin: auto auto;
64 | width: 110px;
65 | height: 110px;
66 | background: lightgray;
67 | border-radius: 50%;
68 | border: 1px solid #cdcbd0;
69 | }
70 |
71 | .mask .fill {
72 | clip: rect(0, 75px, 150px, 0);
73 | background-color: #227ded;
74 | }
75 |
76 | .mask.full,
77 | .circle .fill {
78 | animation: fill ease-in-out 3s;
79 | transform: rotate(135deg);
80 | }
81 |
82 | .circle-wrap .circle .mask,
83 | .circle-wrap .circle .fill {
84 | width: 120px;
85 | height: 120px;
86 | position: absolute;
87 | border-radius: 50%;
88 | }
89 |
90 | .circle-wrap .circle .mask {
91 | clip: rect(0, 150px, 150px, 75px);
92 | }
93 |
94 | .circle-wrap .inside-circle {
95 | width: 103px;
96 | height: 103px;
97 | border-radius: 50%;
98 | background: #fff;
99 | line-height: 120px;
100 | text-align: center;
101 | margin-top: 9px;
102 | margin-left: 9px;
103 | color: #1e51dc;
104 | position: absolute;
105 | z-index: 100;
106 | font-weight: 700;
107 | font-size: 2em;
108 | }
109 |
110 | @keyframes fill {
111 | 0% {
112 | transform: rotate(0deg);
113 | }
114 |
115 | 100% {
116 | transform: rotate(135deg);
117 | }
118 | }
119 |
120 | /* Styling for the book right side */
121 | .bookRight h3 {
122 | margin-bottom: 10px;
123 | }
124 |
125 | .bookbtn {
126 | padding: 10px;
127 | background-color: #227ded;
128 | color: #fff;
129 | margin-top: 20px;
130 | cursor: pointer;
131 | transition: transform 0.5s;
132 | }
133 |
134 | .bookbtn:hover {
135 | transform: translateY(-7px);
136 | }
137 |
--------------------------------------------------------------------------------