├── .gitignore ├── .vscode └── settings.json ├── .hintrc ├── .eslintrc.json ├── .stylelintrc.json ├── LICENSE ├── package.json ├── index.html ├── style.css ├── .github └── workflows │ └── linters.yml ├── README.md └── script.js /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | node_modules/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } 4 | -------------------------------------------------------------------------------- /.hintrc: -------------------------------------------------------------------------------- 1 | { 2 | "connector": { 3 | "name": "local", 4 | "options": { 5 | "pattern": ["**", "!.git/**", "!node_modules/**"] 6 | } 7 | }, 8 | "extends": ["development"], 9 | "formatters": ["stylish"], 10 | "hints": [ 11 | "button-type", 12 | "disown-opener", 13 | "html-checker", 14 | "meta-charset-utf-8", 15 | "meta-viewport", 16 | "no-inline-styles:error" 17 | ] 18 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "jest": true 6 | }, 7 | "parser": "babel-eslint", 8 | "parserOptions": { 9 | "ecmaVersion": 2018, 10 | "sourceType": "module" 11 | }, 12 | "extends": ["airbnb-base"], 13 | "rules": { 14 | "no-shadow": "off", 15 | "no-param-reassign": "off", 16 | "eol-last": "off" 17 | }, 18 | "ignorePatterns": [ 19 | "dist/", 20 | "build/" 21 | ] 22 | } -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["stylelint-config-standard"], 3 | "plugins": ["stylelint-scss", "stylelint-csstree-validator"], 4 | "rules": { 5 | "at-rule-no-unknown": [ 6 | true, 7 | { 8 | "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen"] 9 | } 10 | ], 11 | "scss/at-rule-no-unknown": [ 12 | true, 13 | { 14 | "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen"] 15 | } 16 | ], 17 | "csstree/validator": true 18 | }, 19 | "ignoreFiles": ["build/**", "dist/**", "**/reset*.css", "**/bootstrap*.css", "**/*.js", "**/*.jsx"] 20 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Mussie Kahsay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "babel-eslint": "^10.1.0", 4 | "eslint": "^7.32.0", 5 | "eslint-config-airbnb-base": "^14.2.1", 6 | "eslint-plugin-import": "^2.27.5", 7 | "hint": "^7.1.5", 8 | "stylelint": "^13.13.1", 9 | "stylelint-config-standard": "^21.0.0", 10 | "stylelint-csstree-validator": "^1.9.0", 11 | "stylelint-scss": "^3.21.0" 12 | }, 13 | "name": "awesome-books", 14 | "description": "- [📗 Table of Contents](#-table-of-contents) - [🛠️ Built With ](#-built-with-) - [Tech Stack ](#tech-stack-) - [Key Features](#key-features) - [🚀 Live Demo ](#-live-demo-) - [💻 Getting Started ](#-getting-started-) - [Setup](#setup) - [Prerequisites](#prerequisites) - [Install](#install) - [Usage](#usage) - [Run tests](#run-tests) - [Deployment](#triangular_flag_on_post-deployment) - [👥 Author ](#-author-) - [🔭 Future Features ](#-future-features-) - [🤝 Contributing ](#-contributing-) - [⭐️ Show your support](#support) - [🙏 Acknowledgments ](#-acknowledgments-) - [❓ FAQ (OPTIONAL)](#faq) - [📝 License ](#-license-)", 15 | "version": "1.0.0", 16 | "main": "script.js", 17 | "scripts": { 18 | "test": "echo \"Error: no test specified\" && exit 1" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/MussieTeka/Awesome-Books.git" 23 | }, 24 | "keywords": [], 25 | "author": "", 26 | "license": "ISC", 27 | "bugs": { 28 | "url": "https://github.com/MussieTeka/Awesome-Books/issues" 29 | }, 30 | "homepage": "https://github.com/MussieTeka/Awesome-Books#readme" 31 | } 32 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Awesome Books! 8 | 9 | 10 | 11 |
12 | 17 | 18 | 25 |
26 | 29 | 30 |
31 |
32 |

All Awesome Books

33 |
34 |
35 | 36 |
37 |

Add a New Book

38 |
39 | 40 | 41 | 42 |
43 |
44 | 45 |
46 |

Contact Information

47 |

48 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Natus 49 | doloribus nam atque ea. Exercitationem, aperiam. 50 |

51 |

Address: 123 Main St.

52 |

Phone: 555-1234

53 |

Email: info@awesomebooks.com

54 |
55 |
56 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | .header { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | background-color: #fff; 6 | color: #000; 7 | padding: 10px; 8 | border: 3px solid #000; 9 | } 10 | 11 | .logo { 12 | flex: 1; 13 | } 14 | 15 | .logo a { 16 | text-decoration: none; 17 | } 18 | 19 | .logo h1 { 20 | margin: 0; 21 | color: #000; 22 | } 23 | 24 | .navbar { 25 | flex: 1; 26 | display: flex; 27 | justify-content: flex-end; 28 | align-items: center; 29 | } 30 | 31 | .navbar ul { 32 | list-style-type: none; 33 | margin: 0; 34 | padding: 0; 35 | display: flex; 36 | } 37 | 38 | .navbar li { 39 | margin: 0 10px; 40 | border-right: 2px solid #000; 41 | } 42 | 43 | .navbar li:last-child { 44 | border-right: none; 45 | } 46 | 47 | .navbar a { 48 | color: #000; 49 | text-decoration: none; 50 | padding: 10px; 51 | } 52 | 53 | #current-time { 54 | flex: 1; 55 | display: flex; 56 | justify-content: flex-end; 57 | align-items: center; 58 | } 59 | 60 | #current-time p { 61 | margin: 0; 62 | padding: 10px; 63 | font-size: 18px; 64 | font-weight: bold; 65 | } 66 | 67 | .content-container { 68 | display: flex; 69 | flex-direction: column; 70 | align-items: center; 71 | justify-content: center; 72 | } 73 | 74 | .section { 75 | display: flex; 76 | flex-direction: column; 77 | align-items: center; 78 | justify-content: center; 79 | margin-top: 50px; 80 | width: 100%; 81 | padding: 100px; 82 | } 83 | 84 | .book-list { 85 | border: 1px solid black; 86 | width: 100%; 87 | } 88 | 89 | .book { 90 | width: auto; 91 | display: flex; 92 | align-items: center; 93 | justify-content: center; 94 | padding: 10px; 95 | } 96 | 97 | .add-book { 98 | display: flex; 99 | flex-direction: column; 100 | align-items: center; 101 | justify-content: center; 102 | width: 100%; 103 | } 104 | 105 | .title-input, 106 | .author-input { 107 | width: 80%; 108 | height: 30px; 109 | margin-bottom: 10px; 110 | position: relative; 111 | } 112 | 113 | .add-btn { 114 | position: absolute; 115 | left: 85%; 116 | margin-top: 150px; 117 | padding: 10px; 118 | width: 80px; 119 | background: #fff; 120 | } 121 | 122 | .book-author { 123 | margin-left: 10px; 124 | } 125 | 126 | .footer { 127 | background-color: #fff; 128 | color: #000; 129 | text-align: left; 130 | padding: 10px; 131 | border: 4px solid #000; 132 | } 133 | -------------------------------------------------------------------------------- /.github/workflows/linters.yml: -------------------------------------------------------------------------------- 1 | name: Linters 2 | 3 | on: pull_request 4 | 5 | env: 6 | FORCE_COLOR: 1 7 | 8 | jobs: 9 | lighthouse: 10 | name: Lighthouse 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: "16.x" 17 | - name: Setup Lighthouse 18 | run: npm install -g @lhci/cli@0.7.x 19 | - name: Lighthouse Report 20 | run: lhci autorun --upload.target=temporary-public-storage --collect.staticDistDir=. 21 | webhint: 22 | name: Webhint 23 | runs-on: ubuntu-22.04 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: actions/setup-node@v1 27 | with: 28 | node-version: "16.x" 29 | - name: Setup Webhint 30 | run: | 31 | npm install --save-dev hint@7.x 32 | [ -f .hintrc ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.hintrc 33 | - name: Webhint Report 34 | run: npx hint . 35 | stylelint: 36 | name: Stylelint 37 | runs-on: ubuntu-22.04 38 | steps: 39 | - uses: actions/checkout@v2 40 | - uses: actions/setup-node@v1 41 | with: 42 | node-version: "18.x" 43 | - name: Setup Stylelint 44 | run: | 45 | npm install --save-dev stylelint@13.x stylelint-scss@3.x stylelint-config-standard@21.x stylelint-csstree-validator@1.x 46 | [ -f .stylelintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.stylelintrc.json 47 | - name: Stylelint Report 48 | run: npx stylelint "**/*.{css,scss}" 49 | eslint: 50 | name: ESLint 51 | runs-on: ubuntu-22.04 52 | steps: 53 | - uses: actions/checkout@v2 54 | - uses: actions/setup-node@v1 55 | with: 56 | node-version: "18.x" 57 | - name: Setup ESLint 58 | run: | 59 | npm install --save-dev eslint@7.x eslint-config-airbnb-base@14.x eslint-plugin-import@2.x babel-eslint@10.x 60 | [ -f .eslintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.eslintrc.json 61 | - name: ESLint Report 62 | run: npx eslint . 63 | nodechecker: 64 | name: node_modules checker 65 | runs-on: ubuntu-22.04 66 | steps: 67 | - uses: actions/checkout@v2 68 | - name: Check node_modules existence 69 | run: | 70 | if [ -d "node_modules/" ]; then echo -e "\e[1;31mThe node_modules/ folder was pushed to the repo. Please remove it from the GitHub repository and try again."; echo -e "\e[1;32mYou can set up a .gitignore file with this folder included on it to prevent this from happening in the future." && exit 1; fi 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📗 Table of Contents 2 | 3 | - [📗 Table of Contents](#-table-of-contents) 4 | - [🛠️ Built With ](#-built-with-) 5 | - [Tech Stack ](#tech-stack-) 6 | - [Key Features](#key-features) 7 | - [🚀 Live Demo ](#-live-demo-) 8 | - [💻 Getting Started ](#-getting-started-) 9 | - [Setup](#setup) 10 | - [Prerequisites](#prerequisites) 11 | - [Install](#install) 12 | - [Usage](#usage) 13 | - [Run tests](#run-tests) 14 | - [Deployment](#triangular_flag_on_post-deployment) 15 | - [👥 Author ](#-author-) 16 | - [🔭 Future Features ](#-future-features-) 17 | - [🤝 Contributing ](#-contributing-) 18 | - [⭐️ Show your support](#support) 19 | - [🙏 Acknowledgments ](#-acknowledgments-) 20 | - [❓ FAQ (OPTIONAL)](#faq) 21 | - [📝 License ](#-license-) 22 | 23 | # 📖 Awesome-books 24 | 25 | This is my Awesome-books Project. In which , all of books details are available. 26 | 27 | ## 🛠️ Built With 28 | 29 | ### Tech Stack 30 | 31 | HTML | CSS 32 | 33 | 34 | 35 | ### Key Features 36 | 37 | > My Project Links 38 | > Details of my self 39 | > Contact avaiable 40 | > Animation and Transition 41 | 42 | 43 | 44 | 45 | ## 🚀 Live Demo 46 | 47 | > Visit 48 | 49 | 50 | 51 | ## 💻 Getting Started 52 | 53 | To get a local copy up and running, follow these steps. 54 | 55 | ### Prerequisites 56 | 57 | In order to run this project you needs: 58 | 59 | A GitHub | A code editor | A web browser 60 | 61 | ### Install 62 | 63 | Install all project dependencies by running the command below 64 | 65 | `$ npm install` 66 | 67 | And run `npm start` to launch the project. 68 | 69 | ### Setup 70 | 71 | Clone this repository to your desired folder: 72 | 73 | `git clone https://github.com/MussieTeka/Awesome-Books.git` 74 | 75 | ### Usage 76 | 77 | To run the project, open index.html with your preferred web browser or open it with a live server from your code editor 78 | 79 | ### Run tests 80 | 81 | No tests 82 | 83 | ### Deployment 84 | 85 | You can deploy this project using: 86 | 87 | Get a hosting website and give all of the github files 88 | 89 | 90 | 91 | 92 | ## 👥 Author 93 | 94 | 95 | 👤 **Mussie Teka** 96 | 97 | - GitHub: [@mussieteka](https://github.com/MussieTeka) 98 | - Twitter: [@mussieteka](https://twitter.com/mussieteka) 99 | - LinkedIn: [LinkedIn](https://www.linkedin.com/in/mussieteka/) 100 | 101 | 👤**Joseck Osugo** 102 | 103 | - GitHub: [0sugo](https://github.com/0sugo) 104 | - Twitter: [@0sugo5](https://twitter.com/osugo5) 105 | - LinkedIn: [Joseck Osugo](https://www.linkedin.com/in/joseck-osugo-873b0618a/) 106 | 107 | 108 | 109 | 110 | ## 🔭 Future Features 111 | 112 | > Add animations and transformations to make the site more interactive. 113 | 114 | 115 | 116 | 117 | ## 🤝 Contributing 118 | 119 | Feel free to check [issues page](https://github.com/MussieTeka/Awesome-Books/issues). 120 | 121 | ## ⭐️ Show your support 122 | 123 | If you like this project please leave a ⭐️ 124 | 125 | 126 | 127 | 128 | ## 🙏 Acknowledgments 129 | 130 | Thanks Microverse, learn how to code > [Join Microverse](https://www.microverse.org/?grsf=9m3hq6) 131 | Thanks to all collaborators for making this happen. 132 | 133 | ## 📝 License 134 | 135 | This project is [MIT](./LICENSE) licensed. 136 | 137 |

(back to top)

138 | 139 | 140 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | /* eslint-disable no-unused-vars */ 3 | class BookList { 4 | constructor() { 5 | this.titleInput = document.querySelector('.title-input'); 6 | this.authorInput = document.querySelector('.author-input'); 7 | this.addBtn = document.querySelector('.add-btn'); 8 | this.bookList = document.querySelector('.book-list'); 9 | 10 | // Adding event listeners 11 | this.addBtn.addEventListener('click', this.addBook.bind(this)); 12 | this.bookList.addEventListener('click', this.removeBook.bind(this)); 13 | 14 | // Retrieve from localStorage when the page loads 15 | this.getFromLocalStorage(); 16 | } 17 | 18 | // Function to add a new book to the list 19 | addBook() { 20 | const title = this.titleInput.value.trim(); 21 | const author = this.authorInput.value.trim(); 22 | 23 | if (!title || !author) { 24 | return; 25 | } 26 | 27 | const book = this.createBookElement(title, author); 28 | this.bookList.appendChild(book); 29 | this.bookList.style.display = 'flex'; 30 | this.bookList.style.flexDirection = 'column'; 31 | 32 | const index = this.bookList.children.length; 33 | if (index % 2 === 0) { 34 | book.style.backgroundColor = '#fff'; 35 | } else { 36 | book.style.backgroundColor = '#ddd'; 37 | } 38 | 39 | // Save to localStorage 40 | this.saveToLocalStorage(this.bookList.innerHTML); 41 | 42 | // Clearing the input fields 43 | this.titleInput.value = ''; 44 | this.authorInput.value = ''; 45 | } 46 | 47 | // Function to remove a book from the list 48 | removeBook(event) { 49 | const button = event.target; 50 | 51 | if (!button.classList.contains('remove-btn')) { 52 | return; 53 | } 54 | 55 | const book = button.closest('.book'); 56 | this.bookList.removeChild(book); 57 | 58 | // Save to localStorage 59 | this.saveToLocalStorage(this.bookList.innerHTML); 60 | } 61 | 62 | createBookElement(title, author) { 63 | const book = document.createElement('div'); 64 | book.classList.add('book'); 65 | 66 | const bookTitle = document.createElement('p'); 67 | bookTitle.classList.add('book-title'); 68 | bookTitle.textContent = title; 69 | 70 | const bookAuthor = document.createElement('p'); 71 | bookAuthor.classList.add('book-author'); 72 | bookAuthor.textContent = `by ${author}`; 73 | 74 | const removeBtn = document.createElement('button'); 75 | removeBtn.classList.add('remove-btn'); 76 | removeBtn.textContent = 'Remove'; 77 | 78 | book.appendChild(bookTitle); 79 | book.appendChild(bookAuthor); 80 | 81 | const separator = document.createElement('hr'); 82 | separator.classList.add('book-separator'); 83 | separator.setAttribute('dir', 'rtl'); 84 | book.appendChild(separator); 85 | 86 | book.appendChild(removeBtn); 87 | 88 | return book; 89 | } 90 | 91 | // Function to save the book list to localStorage 92 | saveToLocalStorage() { 93 | localStorage.setItem('bookList', this.bookList.innerHTML); 94 | } 95 | 96 | // Function to retrieve the book list from localStorage 97 | getFromLocalStorage() { 98 | const bookListHtml = localStorage.getItem('bookList'); 99 | if (bookListHtml) { 100 | this.bookList.innerHTML = bookListHtml; 101 | } 102 | } 103 | } 104 | 105 | const myBookList = new BookList(); 106 | 107 | // Define the DOM elements 108 | const elements = { 109 | newBook: document.querySelector('#list'), 110 | bookList: document.querySelector('#books-list'), 111 | introduceBook: document.querySelector('#introduce-book'), 112 | addBookSection: document.querySelector('#add-book-form'), 113 | contactUsBtn: document.querySelector('#contact-us'), 114 | contactUs: document.querySelector('#contact-info'), 115 | }; 116 | 117 | // Define the functions to handle the events 118 | function showBooksList() { 119 | elements.bookList.style.display = 'flex'; 120 | elements.bookList.style.marginTop = '100px'; 121 | elements.bookList.style.marginBottom = '100px'; 122 | elements.addBookSection.style.display = 'none'; 123 | elements.contactUs.style.display = 'none'; 124 | } 125 | 126 | function showAddBookForm() { 127 | elements.bookList.style.display = 'none'; 128 | elements.addBookSection.style.display = 'flex'; 129 | elements.addBookSection.style.marginTop = '100px'; 130 | elements.addBookSection.style.marginBottom = '100px'; 131 | elements.contactUs.style.display = 'none'; 132 | } 133 | 134 | function showContactUs() { 135 | elements.bookList.style.display = 'none'; 136 | elements.addBookSection.style.display = 'none'; 137 | elements.contactUs.style.display = 'flex'; 138 | elements.contactUs.style.marginTop = '100px'; 139 | elements.contactUs.style.marginBottom = '100px'; 140 | } 141 | 142 | // Add the event listeners to the elements 143 | elements.newBook.addEventListener('click', showBooksList); 144 | elements.introduceBook.addEventListener('click', showAddBookForm); 145 | elements.contactUsBtn.addEventListener('click', showContactUs); 146 | 147 | function updateTime() { 148 | const now = new Date(); 149 | const date = now.toLocaleDateString(); 150 | const time = now.toLocaleTimeString(); 151 | const dateTime = `${date} ${time}`; 152 | document.getElementById('current-time').querySelector('p').textContent = dateTime; 153 | } 154 | 155 | updateTime(); 156 | setInterval(updateTime, 1000); 157 | --------------------------------------------------------------------------------