├── .eslintrc.json ├── .hintrc ├── .github └── workflows │ └── linters.yml ├── .gitignore ├── .stylelintrc.json ├── MIT.md ├── README.md ├── assets ├── .DS_Store ├── css │ └── styles.css ├── images │ └── ibnas.png └── js │ ├── data.js │ ├── date.js │ ├── hardworker.js │ └── navigation.js ├── index.html ├── package-lock.json └── package.json / .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": [ 1, { 18 | "js": "always", "json": "always" 19 | }] 20 | }, 21 | "ignorePatterns": [ 22 | "dist/", 23 | "build/" 24 | ] 25 | } -------------------------------------------------------------------------------- / .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 | } -------------------------------------------------------------------------------- /.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-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 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-18.04 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: actions/setup-node@v1 27 | with: 28 | node-version: "12.x" 29 | - name: Setup Webhint 30 | run: | 31 | npm install --save-dev hint@6.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-18.04 38 | steps: 39 | - uses: actions/checkout@v2 40 | - uses: actions/setup-node@v1 41 | with: 42 | node-version: "12.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-18.04 52 | steps: 53 | - uses: actions/checkout@v2 54 | - uses: actions/setup-node@v1 55 | with: 56 | node-version: "12.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-18.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | node_modules/ -------------------------------------------------------------------------------- /.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, [ibrahim naseer] 2 | 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this [website] and associated documentation files, to deal in the [website] without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the [website], and to permit persons to whom the [website] is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the [website]. 7 | 8 | THE [website] 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 [website] OR THE USE OR OTHER DEALINGS IN THE [website]. 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://img.shields.io/badge/Microverse-blueviolet) 2 | 3 | # Awesome books: with ES6 4 | 5 | "Awesome books" is a simple website that displays a list of books and allows you to add and remove books from that list. see how to manage data using JavaScript. built with medium-fidelity wireframe for UI. 6 | 7 | ![screenshot](assets/images/ibnas.png) 8 | 9 | ## Built With 10 | 11 | - HTML 12 | - CSS 13 | - Javascript 14 | - HTML&CSS Linter 15 | - Git 16 | 17 | [LIVE DEMO LINK]( https://ibrahim777764.github.io/week2-es6-project/) 18 | 19 | # 20 | 21 | ## Authors 22 | 23 | 👤 **Author1** 24 | 25 | - GitHub: [@https://github.com/ibrahim777764](https://https://github.com/ibrahim777764) 26 | 27 | 28 | # Get Started 29 | 30 | Please get your browser update. 31 | To get a local copy up and running follow these simple steps. 32 | 33 | Run the command below from the Terminal: 34 | 35 | git clone in your terminal 36 | 37 | cd awesome-books 38 | 39 | start index.html 40 | ## 🤝 Contributing 41 | 42 | Contributions, issues, and feature requests are welcome! 43 | 44 | ## Show your support 45 | 46 | Give a ⭐️ if you like this project! 47 | 48 | ## Acknowledgments 49 | 50 | Microverse Community 51 | 52 | ## 📝 License 53 | 54 | This project is [MIT](./MIT.md) licensed. 55 | -------------------------------------------------------------------------------- /assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibrahim777764/week2-es6-project/620fc12123775df004f37c998c219ee86acfd737/assets/.DS_Store -------------------------------------------------------------------------------- /assets/css/styles.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css?family=Poppins:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i"); 2 | 3 | body { 4 | font-family: 'Poppins', sans-serif; 5 | background-color: rgb(73, 126, 206); 6 | } 7 | 8 | ul { 9 | padding: 0; 10 | margin: 0; 11 | } 12 | 13 | h1 { 14 | text-align: center; 15 | } 16 | 17 | li { 18 | list-style: none; 19 | padding: 5px; 20 | font-size: 17px; 21 | } 22 | 23 | .nav-bar ul li { 24 | margin: 2px; 25 | } 26 | 27 | .list_present li:nth-child(odd) { 28 | background: #9bb1ca; 29 | } 30 | 31 | .list_present { 32 | max-height: 500px; 33 | width: 800px; 34 | overflow-x: scroll; 35 | border: solid; 36 | margin: 0 auto; 37 | } 38 | 39 | .removeBook { 40 | margin-right: 5px; 41 | float: right; 42 | height: 25px; 43 | box-shadow: 5px 5px; 44 | } 45 | 46 | .divider { 47 | width: 200px; 48 | border: solid; 49 | margin: 30px auto; 50 | } 51 | 52 | .addBookForm { 53 | text-align: center; 54 | } 55 | 56 | .addBookForm input[type=text] { 57 | width: 100%; 58 | border: solid 2px; 59 | border-radius: 5px; 60 | height: 30px; 61 | } 62 | 63 | .addBookForm ul { 64 | margin: 0 auto; 65 | } 66 | 67 | .addBookForm input[type=button] { 68 | width: 50px; 69 | box-shadow: 5px 5px; 70 | margin-right: 0; 71 | float: right; 72 | } 73 | 74 | .wrapText { 75 | width: 400px; 76 | word-break: break-all; 77 | } 78 | 79 | .container { 80 | display: flex; 81 | min-height: 100vh; 82 | flex-direction: column; 83 | justify-content: space-between; 84 | } 85 | 86 | footer { 87 | border: solid 2px black; 88 | padding: 10px; 89 | position: relative; 90 | } 91 | 92 | .wrapper { 93 | border: solid 2px black; 94 | padding-left: 10px; 95 | padding-right: 10px; 96 | } 97 | 98 | .nav-bar ul { 99 | display: flex; 100 | flex-direction: row; 101 | width: 100%; 102 | } 103 | 104 | .nav-bar ul li a { 105 | text-decoration: none; 106 | color: black; 107 | } 108 | 109 | .nav-bar ul li:nth-child(2) { 110 | border-right: solid 2px black; 111 | } 112 | 113 | .nav-bar ul li:nth-child(3) { 114 | border-right: solid 2px black; 115 | } 116 | 117 | .appname { 118 | flex: 5; 119 | } 120 | 121 | .todaysDate { 122 | text-align: right; 123 | width: 100%; 124 | margin-bottom: 50px; 125 | } 126 | 127 | .contactSection { 128 | margin-top: 30px; 129 | text-align: center; 130 | } 131 | 132 | .contactSection, 133 | .addBookSection { 134 | display: none; 135 | } 136 | 137 | .addBookSection { 138 | width: 600px; 139 | margin: 0 auto; 140 | } 141 | 142 | .msg { 143 | color: green; 144 | display: none; 145 | transition: 2s; 146 | } 147 | -------------------------------------------------------------------------------- /assets/images/ibnas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibrahim777764/week2-es6-project/620fc12123775df004f37c998c219ee86acfd737/assets/images/ibnas.png -------------------------------------------------------------------------------- /assets/js/data.js: -------------------------------------------------------------------------------- 1 | class Books { 2 | constructor(element, storedItems) { 3 | this.showBooksElement = element; 4 | this.booksData = storedItems; 5 | } 6 | 7 | updateStorage() { 8 | localStorage.setItem('bookData', JSON.stringify(this.booksData)); 9 | } 10 | 11 | static createBookDisplay(book, empty = false) { 12 | const li = document.createElement('li'); 13 | if (!empty) { 14 | li.innerHTML = `

15 | "${book.title}" by - ${book.author} 16 | 17 |

`; 18 | } else { 19 | li.innerHTML = '

No books available

'; 20 | } 21 | 22 | return li; 23 | } 24 | 25 | updateUI() { 26 | while (this.showBooksElement.firstChild) { 27 | this.showBooksElement.removeChild(this.showBooksElement.firstChild); 28 | } 29 | 30 | if (this.booksData.length > 0) { 31 | this.booksData.forEach((book) => { 32 | this.showBooksElement.appendChild(Books.createBookDisplay(book)); 33 | }); 34 | } else { 35 | this.showBooksElement.appendChild(Books.createBookDisplay({}, true)); 36 | } 37 | } 38 | 39 | add(title, author) { 40 | const bookId = Math.random().toString(36).slice(2); 41 | this.booksData.unshift({ 42 | id: bookId, 43 | title, 44 | author, 45 | }); 46 | this.updateStorage(); 47 | this.updateUI(); 48 | } 49 | 50 | remove(bookId) { 51 | this.booksData = this.booksData.filter((books) => books.id !== bookId); 52 | this.updateStorage(); 53 | this.updateUI(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /assets/js/date.js: -------------------------------------------------------------------------------- 1 | const formatAMPM = (date) => { 2 | const day = date.toDateString(); 3 | let hours = date.getHours(); 4 | let minutes = date.getMinutes(); 5 | let seconds = date.getSeconds(); 6 | const ampm = hours >= 12 ? 'pm' : 'am'; 7 | hours %= 12; 8 | hours = hours || 12; // the hour '0' should be '12' 9 | minutes = minutes < 10 ? `0${minutes}` : minutes; 10 | seconds = seconds < 10 ? `0${seconds}` : seconds; 11 | const strTime = `${day}, ${hours}:${minutes}:${seconds} ${ampm}`; 12 | return strTime; 13 | }; 14 | /* eslint-disable no-undef */ 15 | dateContainer.innerHTML = formatAMPM(new Date()); -------------------------------------------------------------------------------- /assets/js/hardworker.js: -------------------------------------------------------------------------------- 1 | const removeBtn = document.querySelectorAll('.removeBook'); 2 | const titleInput = document.querySelector('#book_title'); 3 | const authorInput = document.querySelector('#book_author'); 4 | const addBookBtn = document.querySelector('#add_book'); 5 | const addForm = document.querySelector('.addBookForm'); 6 | const bookList = document.querySelector('.list_present'); 7 | const msg = document.querySelector('.msg'); 8 | 9 | /* Navigation controls */ 10 | const listLink = document.querySelector('.list'); 11 | const addLink = document.querySelector('.add'); 12 | const contactLink = document.querySelector('.contact'); 13 | 14 | const listSection = document.querySelector('.bookListSection'); 15 | const addSection = document.querySelector('.addBookSection'); 16 | const contactSection = document.querySelector('.contactSection'); 17 | /* Navigation controls end */ 18 | 19 | const dateContainer = document.querySelector('.todaysDate'); 20 | 21 | let localStorageData = JSON.parse(localStorage.getItem('bookData')); 22 | 23 | if (localStorageData === null) { 24 | localStorageData = []; 25 | } 26 | const booksBinding = new Books(bookList, localStorageData); 27 | const addBook = () => { 28 | const title = titleInput.value; 29 | const author = authorInput.value; 30 | if (title !== '' && author !== '') { 31 | booksBinding.add(title, author); 32 | msg.style.display = 'block'; 33 | setTimeout(() => { 34 | msg.style.display = 'none'; 35 | }, 2000); 36 | addForm.reset(); 37 | } 38 | }; 39 | const removeBook = (bookId) => { 40 | booksBinding.remove(bookId); 41 | }; 42 | 43 | addBookBtn.addEventListener('click', addBook); 44 | 45 | window.onresize = booksBinding.updateUI(); 46 | -------------------------------------------------------------------------------- /assets/js/navigation.js: -------------------------------------------------------------------------------- 1 | const setAllLinksColorBlack = () => { 2 | const getAllLinks = document.querySelectorAll('.nav-bar ul a'); 3 | getAllLinks.forEach((link) => { 4 | link.style.color = 'black'; 5 | }); 6 | }; 7 | 8 | const manipulateDom = (elemArrayParam, elemToShow, linkClicked) => { 9 | setAllLinksColorBlack(); 10 | elemArray = elemArrayParam; 11 | elemArray.forEach((elem) => { 12 | elem.style.display = 'none'; 13 | }); 14 | elemToShow.style.display = 'block'; 15 | linkClicked.style.color = 'blue'; 16 | }; 17 | 18 | listLink.addEventListener('click', () => { 19 | const elemArray = document.querySelectorAll( 20 | '.addBookSection, .contactSection', 21 | ); 22 | manipulateDom(elemArray, listSection, listLink); 23 | }); 24 | 25 | addLink.addEventListener('click', () => { 26 | const elemArray = document.querySelectorAll( 27 | '.bookListSection, .contactSection', 28 | ); 29 | manipulateDom(elemArray, addSection, addLink); 30 | }); 31 | 32 | contactLink.addEventListener('click', () => { 33 | const elemArray = document.querySelectorAll( 34 | '.bookListSection, .addBookSection', 35 | ); 36 | manipulateDom(elemArray, contactSection, contactLink); 37 | }); 38 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Awesome books - Navigation 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 |

All awesome Books

32 |
    33 |
  • 34 |

    No books available yet!

    35 |
  • 36 |
37 |
38 |
39 |
40 |

Add a new book

41 |
    42 |
  • 43 | 44 |
  • 45 |
  • 46 | 47 |
  • 48 |
  • 49 | 50 |
  • 51 |
52 |

Book Added Successfully, click List to view

53 |
54 |
55 |
56 |

Contact Information

57 |

Do have any questions or you just want to say "Hello"?
58 | You can reach out to us! 59 |

60 |
    61 |
  • Our e-mail: mail@mail.com
  • 62 |
  • Our phone number: 123456789
  • 63 |
  • Our address: karachi 22, 84503 City, Country
  • 64 |
65 |
66 | 67 |
68 |

© IBRAHIM

69 |
70 |
71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "week2-es6-project", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/ibrahim777764/week2-es6-project.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/ibrahim777764/week2-es6-project/issues" 18 | }, 19 | "homepage": "https://github.com/ibrahim777764/week2-es6-project#readme", 20 | "devDependencies": { 21 | "babel-eslint": "^10.1.0", 22 | "eslint": "^7.32.0", 23 | "eslint-config-airbnb-base": "^14.2.1", 24 | "eslint-plugin-import": "^2.26.0", 25 | "stylelint": "^13.13.1", 26 | "stylelint-config-standard": "^21.0.0", 27 | "stylelint-csstree-validator": "^1.9.0", 28 | "stylelint-scss": "^3.21.0" 29 | } 30 | } 31 | --------------------------------------------------------------------------------