├── .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 |
26 |
29 |
30 |
31 |
32 | All Awesome Books
33 |
34 |
35 |
36 |
44 |
45 |
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 |
--------------------------------------------------------------------------------