├── .vscode └── settings.json ├── .gitignore ├── .hintrc ├── .eslintrc.json ├── .stylelintrc.json ├── MIT.md ├── index.html ├── styles.css ├── .github └── workflows │ └── linters.yml ├── script.js └── README.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5503 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | dist/*.html 4 | dist/*.css 5 | 6 | test.md 7 | 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /.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 | "import/extensions": [ 18 | 1, 19 | { 20 | "js": "always", 21 | "json": "always" 22 | } 23 | ], 24 | "no-use-before-define": ["error", { "functions": false }] 25 | }, 26 | "ignorePatterns": ["dist/", "build/"] 27 | } 28 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /MIT.md: -------------------------------------------------------------------------------- 1 | ## Copyright 2021, [YOUR NAME] 2 | 3 | ###### Please delete this line and the next one 4 | 5 | ###### APP TYPE can be a webpage/website, a web app, a software and so on 6 | 7 | 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: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the [APP TYPE]. 10 | 11 | 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]. 12 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Book-List 9 | 10 | 11 |
12 |

Awesome Books

13 | 20 | 21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | 30 |

Add new book

31 |
32 |
33 | 39 |
40 |
41 |
42 | 48 |
49 |
50 |
51 | 52 |
53 |
54 |
55 |
56 |
57 |

Contact Info

58 |

Email: contact@example.com

59 |

Phone: 123-456-7890

60 |
61 |
62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | font-family: "Lato", sans-serif; 9 | } 10 | 11 | header { 12 | background-color: #333; 13 | color: #fff; 14 | padding: 10px; 15 | display: flex; 16 | justify-content: space-between; 17 | align-items: center; 18 | } 19 | 20 | h1 { 21 | text-align: center; 22 | margin-right: auto; 23 | } 24 | 25 | nav ul { 26 | list-style-type: none; 27 | display: flex; 28 | align-items: center; 29 | } 30 | 31 | nav li { 32 | margin: 0 10px; 33 | } 34 | 35 | nav a { 36 | color: #fff; 37 | text-decoration: none; 38 | } 39 | 40 | nav a:hover { 41 | text-decoration: underline; 42 | } 43 | 44 | main { 45 | padding: 20px; 46 | } 47 | 48 | .content-section { 49 | display: none; 50 | } 51 | 52 | .content-section.active { 53 | display: block; 54 | } 55 | 56 | .content-section h3 { 57 | margin-bottom: 10px; 58 | } 59 | 60 | .input-container { 61 | display: flex; 62 | flex-direction: column; 63 | } 64 | 65 | .inputs { 66 | width: 100%; 67 | padding: 5px; 68 | margin-bottom: 10px; 69 | } 70 | 71 | .button { 72 | display: flex; 73 | justify-content: flex-end; 74 | } 75 | 76 | .separation-line { 77 | width: 15%; 78 | border: solid 2px black; 79 | margin: 36px auto; 80 | } 81 | 82 | #book-list { 83 | margin-top: 10px; 84 | border: solid 2px black; 85 | } 86 | 87 | #book-list-container { 88 | border: 2px solid black; 89 | padding: 10px; 90 | } 91 | 92 | .book-item { 93 | display: flex; 94 | justify-content: space-between; 95 | align-items: center; 96 | margin-bottom: 10px; 97 | background-color: #fff; 98 | padding: 10px; 99 | } 100 | 101 | .book-item:nth-child(even) { 102 | background-color: #f2f2f2; 103 | } 104 | 105 | .book-item p { 106 | margin: 0; 107 | } 108 | 109 | .book-item button { 110 | box-shadow: 3px 3px; 111 | } 112 | 113 | #datetime { 114 | text-align: right; 115 | margin-top: 10px; 116 | font-size: 14px; 117 | } 118 | -------------------------------------------------------------------------------- /.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@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: "18.x" 17 | - name: Setup Lighthouse 18 | run: npm install -g @lhci/cli@0.11.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@v3 26 | - uses: actions/setup-node@v3 27 | with: 28 | node-version: "18.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@v3 40 | - uses: actions/setup-node@v3 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@v3 54 | - uses: actions/setup-node@v3 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@v3 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 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | class BookList { 2 | constructor() { 3 | this.bookListContainer = document.getElementById('book-list-container'); 4 | this.inputTitle = document.getElementById('title'); 5 | this.inputAuthor = document.getElementById('author'); 6 | this.inputButton = document.getElementById('add-button'); 7 | this.books = []; 8 | 9 | this.loadBooksFromLocalStorage(); 10 | this.inputButton.addEventListener('click', this.addBook.bind(this)); 11 | 12 | this.setupNavigation(); 13 | } 14 | 15 | displayBooks() { 16 | const bookList = document.getElementById('book-list'); 17 | bookList.innerHTML = ''; 18 | 19 | this.books.forEach((book, index) => { 20 | const listItem = document.createElement('div'); 21 | listItem.classList.add('book-item'); 22 | 23 | const titleElement = document.createElement('p'); 24 | titleElement.textContent = `Title: ${book.title}`; 25 | 26 | const authorElement = document.createElement('p'); 27 | authorElement.textContent = `Author: ${book.author}`; 28 | 29 | const removeButton = document.createElement('button'); 30 | removeButton.textContent = 'Remove'; 31 | removeButton.addEventListener('click', () => { 32 | this.removeBook(index); 33 | }); 34 | 35 | listItem.appendChild(titleElement); 36 | listItem.appendChild(authorElement); 37 | listItem.appendChild(removeButton); 38 | 39 | bookList.appendChild(listItem); 40 | }); 41 | 42 | if (this.books.length === 0) { 43 | this.bookListContainer.style.display = 'none'; 44 | } else { 45 | this.bookListContainer.style.display = 'block'; 46 | } 47 | } 48 | 49 | loadBooksFromLocalStorage() { 50 | const storedBooks = localStorage.getItem('books'); 51 | if (storedBooks) { 52 | this.books = JSON.parse(storedBooks); 53 | } 54 | this.displayBooks(); 55 | } 56 | 57 | saveBooksToLocalStorage() { 58 | localStorage.setItem('books', JSON.stringify(this.books)); 59 | } 60 | 61 | addBook(event) { 62 | event.preventDefault(); 63 | const title = this.inputTitle.value; 64 | const author = this.inputAuthor.value; 65 | if (title !== '' && author !== '') { 66 | const book = { title, author }; 67 | this.books.unshift(book); 68 | this.saveBooksToLocalStorage(); 69 | this.displayBooks(); 70 | this.inputTitle.value = ''; 71 | this.inputAuthor.value = ''; 72 | } 73 | } 74 | 75 | removeBook(index) { 76 | this.books.splice(index, 1); 77 | this.saveBooksToLocalStorage(); 78 | this.displayBooks(); 79 | } 80 | 81 | setupNavigation() { 82 | const navLinks = document.querySelectorAll('.nav-links li a'); 83 | navLinks.forEach((link) => { 84 | link.addEventListener('click', (event) => { 85 | event.preventDefault(); 86 | const targetSectionId = link.getAttribute('href').substring(1); 87 | this.showSection(targetSectionId); 88 | }); 89 | }); 90 | } 91 | 92 | showSection = (sectionId) => { 93 | const contentSections = document.querySelectorAll('.content-section'); 94 | contentSections.forEach((section) => { 95 | if (section.id === sectionId) { 96 | section.classList.add('active'); 97 | } else { 98 | section.classList.remove('active'); 99 | } 100 | }); 101 | 102 | const navLinks = document.querySelectorAll('.nav-links li a'); 103 | navLinks.forEach((link) => { 104 | if (link.getAttribute('href').substring(1) === sectionId) { 105 | link.classList.add('active'); 106 | } else { 107 | link.classList.remove('active'); 108 | } 109 | }); 110 | } 111 | } 112 | 113 | document.addEventListener('DOMContentLoaded', () => { 114 | const bookList = new BookList(); 115 | bookList.displayBooks(); 116 | updateDateTime(); 117 | 118 | function updateDateTime() { 119 | const datetimeElement = document.getElementById('datetime'); 120 | const now = new Date(); 121 | 122 | const options = { 123 | month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', 124 | }; 125 | const dateTimeString = now.toLocaleString('en-US', options); 126 | 127 | const formattedDateTime = dateTimeString.replace(',', ''); 128 | 129 | datetimeElement.textContent = formattedDateTime; 130 | } 131 | 132 | setInterval(updateDateTime, 1000); 133 | }); 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 26 | # 📗 Table of Contents 27 | 28 | - [📖 About the Project](#about-project) 29 | - [🛠 Built With](#built-with) 30 | - [Tech Stack](#tech-stack) 31 | - [Key Features](#key-features) 32 | - [🚀 Live Demo](#live-demo) 33 | - [💻 Getting Started](#getting-started) 34 | - [Setup](#setup) 35 | - [Prerequisites](#prerequisites) 36 | - [Install](#install) 37 | - [Usage](#usage) 38 | - [Run tests](#run-tests) 39 | - [Deployment](#deployment) 40 | - [👥 Authors](#authors) 41 | - [🔭 Future Features](#future-features) 42 | - [🤝 Contributing](#contributing) 43 | - [⭐️ Show your support](#support) 44 | - [🙏 Acknowledgements](#acknowledgements) 45 | - [❓ FAQ (OPTIONAL)](#faq) 46 | - [📝 License](#license) 47 | 48 | 49 | 50 | # 📖 [book_list] 51 | 52 | **[book_list]** 53 | This exercise involves the creation of a book list application that enables users to add and remove books. The application is built using HTML, JavaScript, and leverages the Local Storage feature of web browsers to store the book data persistently. It provides a user-friendly interface with a form where users can input the title and author of a book. Upon submitting the form, the book is added to the list, which is dynamically updated and displayed on the page. The application also allows users to easily remove books from the list with a single click. The use of Local Storage ensures that the book collection remains intact even if the page is refreshed or reopened. This project showcases the implementation of basic CRUD (Create, Read, Update, Delete) operations using web technologies, providing a practical example of data management and persistence within the browser environment. 54 | 55 | ## 🛠 Built With 56 | 57 | ### Tech Stack 58 | 59 |
60 | HTML 61 |
62 | 63 |
64 | JavaScript 65 |
66 | 67 | 68 | ### Key Features 69 | 70 | - **[Branch creation]** 71 | - **[Version control]** 72 | - **[GitHub flow]** 73 | - **[Pull request]** 74 | - **[Descriptive commits]** 75 | - **[Add Books]** 76 | - **[Remove Books]** 77 | - **[Data Persistence]** 78 | - **[Interactive User Basic Interface]** 79 | - **[JavaScript Functionality]** 80 | - **[Code Maintenance]** 81 | 82 |

(back to top)

83 | 84 | 85 | 86 | 87 | 88 | ## 💻 Getting Started 89 | 90 | ### Setup 91 | 92 | Clone this repository to your local machine: 93 | 94 | git clone git@github.com:ClaudiaRojasSoto/book_list.git 95 | 96 | ### Prerequisites 97 | 98 | In order to use this project, you need to have Node.js and npm installed on your local machine. 99 | 100 | ### Install 101 | 102 | Install the necessary dependencies by running: 103 | 104 | npm install 105 | 106 | 113 | 114 | ### Usage 115 | 116 | To start the project, run the following command: 117 | 118 | npm start 119 | 120 | 126 | 127 | ### Run tests 128 | 129 | To run tests, run the following command: 130 | 131 | npm test 132 | 133 | 139 | 140 | ### Deployment 141 | 142 | You can deploy this project using: https://claudiarojassoto.github.io/book_list/ 143 | 144 | 149 | 150 |

(back to top)

151 | 152 | 153 | 154 | ## 👥 Authors 155 | 156 | 👤 **Claudia Rojas** 157 | 158 | - GitHub: [@ClaudiaRojasSoto](https://github.com/ClaudiaRojasSoto) 159 | - LinkedIn: [LinkedIn](https://www.linkedin.com/in/claudia-soto-260504208/) 160 | 161 |

(back to top)

162 | 163 | 164 | 165 | ## 🔭 Future Features 166 | 167 | -**[Classes in JavaScript]** 168 | 169 |

(back to top)

170 | 171 | 172 | 173 | ## 🤝 Contributing 174 | 175 | Feel free to check the [issues page](https://github.com/ClaudiaRojasSoto/book_list). 176 | 177 |

(back to top)

178 | 179 | 180 | 181 | ## ⭐️ Show your support 182 | 183 | If you like this project, please give it a ⭐️! 184 | 185 |

(back to top)

186 | 187 | 188 | 189 | ## 🙏 Acknowledgments 190 | 191 | I would like to thank... 192 | 193 |

* Microverse for providing the opportunity to learn Git and GitHub in a collaborative environment.

194 |

* GitHub Docs for providing a wealth of information on Git and GitHub.

195 | 196 |

(back to top)

197 | 198 | 199 | 200 |

(back to top)

201 | 202 | 203 | 204 | ## 📝 License 205 | 206 | This project is [MIT](https://spdx.org/licenses/MIT.html) licensed. 207 | 208 |

(back to top)

209 | Footer 210 | © 2023 GitHub, Inc. 211 | Footer navigation 212 | Terms 213 | Privacy 214 | Security 215 | Status 216 | Docs 217 | Contact GitHub 218 | Pricing 219 | API 220 | Training 221 | Blog 222 | About 223 | --------------------------------------------------------------------------------