├── README.md
├── darkmode.js
├── index.html
├── script.js
└── style.css
/README.md:
--------------------------------------------------------------------------------
1 | # dicoding-bookshelf-apps
2 | Submission dicoding kelas Belajar Membuat Front-End Web untuk Pemula https://www.dicoding.com/academies/315
3 | Starter project dari Dicoding
4 | Demo https://andrarstn.github.io/dicoding-bookshelf-apps/index.html
5 |
--------------------------------------------------------------------------------
/darkmode.js:
--------------------------------------------------------------------------------
1 | const displayLocalStorageKey = "DARK_MODE"
2 |
3 | const darkButton = document.querySelector("#darkButton")
4 |
5 | if (localStorage.getItem(displayLocalStorageKey) === null) {
6 | localStorage.setItem(displayLocalStorageKey, 'light');
7 | }else{
8 | if (localStorage.getItem(displayLocalStorageKey) == 'dark') {
9 | toDark()
10 | }
11 | }
12 |
13 | darkButton.addEventListener("click", function(){
14 | toDark()
15 | switchDisplay()
16 | })
17 |
18 | function toDark() {
19 | darkButton.classList.toggle("light-button")
20 | if(darkButton.innerHTML != "Light Mode"){
21 | darkButton.innerHTML = "Light Mode"
22 | }else{
23 | darkButton.innerHTML = "Dark Mode"
24 | }
25 | document.querySelector("body").classList.toggle("body-dark")
26 | document.querySelector("header").classList.toggle("head_bar-dark")
27 |
28 | document.querySelector("#inputSection").classList.toggle("border-dark")
29 | document.querySelector("#searchSection").classList.toggle("border-dark")
30 | document.querySelector("#bookShelfCompleted").classList.toggle("border-dark")
31 | document.querySelector("#bookShelfUncompleted").classList.toggle("border-dark")
32 |
33 | document.querySelector("#h2InputSection").classList.toggle("color-dark")
34 | document.querySelector("#searchSectionH2").classList.toggle("color-dark")
35 | document.querySelector("#completedH2").classList.toggle("color-dark")
36 | document.querySelector("#completeBookshelfList").classList.toggle("color-dark")
37 | document.querySelector("#uncompletedH2").classList.toggle("color-dark")
38 |
39 | let bookItem = document.getElementsByClassName("book_item")
40 | for (let index = 0; index < bookItem.length; index++) {
41 | bookItem[index].classList.toggle("border-dark")
42 | }
43 |
44 | document.querySelector("#inputBookTitle").labels[0].classList.toggle("color-dark")
45 | document.querySelector("#inputBookAuthor").labels[0].classList.toggle("color-dark")
46 | document.querySelector("#inputBookYear").labels[0].classList.toggle("color-dark")
47 | document.querySelector("#inputBookIsComplete").labels[0].classList.toggle("color-dark")
48 | }
49 |
50 | function switchDisplay() {
51 | if (localStorage.getItem(displayLocalStorageKey) == 'light') {
52 | localStorage.setItem(displayLocalStorageKey, 'dark');
53 | }else{
54 | localStorage.setItem(displayLocalStorageKey, 'light');
55 | }
56 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 | Bookshelf Apps
11 |
12 |
16 |
17 |
18 |
19 |
22 |
23 |
48 |
49 |
50 |
51 |
52 | Cari Buku
53 |
58 |
59 |
60 |
61 |
62 |
63 | Belum selesai dibaca
64 |
65 |
66 |
67 |
68 |
69 | Selesai dibaca
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/script.js:
--------------------------------------------------------------------------------
1 | const localStorageKey = "BOOKS_DATA"
2 |
3 | const title = document.querySelector("#inputBookTitle")
4 | const errorTitle = document.querySelector("#errorTitle")
5 | const sectionTitle = document.querySelector("#sectionTitle")
6 |
7 | const author = document.querySelector("#inputBookAuthor")
8 | const errorAuthor = document.querySelector("#errorAuthor")
9 | const sectionAuthor = document.querySelector("#sectionAuthor")
10 |
11 | const year = document.querySelector("#inputBookYear")
12 | const errorYear = document.querySelector("#errorYear")
13 | const sectionYear = document.querySelector("#sectionYear")
14 |
15 | const readed = document.querySelector("#inputBookIsComplete")
16 |
17 | const btnSubmit = document.querySelector("#bookSubmit")
18 |
19 | const searchValue = document.querySelector("#searchBookTitle")
20 | const btnSearch = document.querySelector("#searchSubmit")
21 |
22 | let checkInput = []
23 | let checkTitle = null
24 | let checkAuthor = null
25 | let checkYear = null
26 |
27 | window.addEventListener("load", function(){
28 | if (localStorage.getItem(localStorageKey) !== null) {
29 | const booksData = getData()
30 | showData(booksData)
31 | }
32 | })
33 |
34 | btnSearch.addEventListener("click",function(e) {
35 | e.preventDefault()
36 | if (localStorage.getItem(localStorageKey) == null) {
37 | return alert("Tidak ada data buku")
38 | }else{
39 | const getByTitle = getData().filter(a => a.title == searchValue.value.trim());
40 | if (getByTitle.length == 0) {
41 | const getByAuthor = getData().filter(a => a.author == searchValue.value.trim());
42 | if (getByAuthor.length == 0) {
43 | const getByYear = getData().filter(a => a.year == searchValue.value.trim());
44 | if (getByYear.length == 0) {
45 | alert(`Tidak ditemukan data dengan kata kunci: ${searchValue.value}`)
46 | }else{
47 | showSearchResult(getByYear);
48 | }
49 | }else{
50 | showSearchResult(getByAuthor);
51 | }
52 | }else{
53 | showSearchResult(getByTitle);
54 | }
55 | }
56 |
57 | searchValue.value = ''
58 | })
59 |
60 | btnSubmit.addEventListener("click", function() {
61 | if (btnSubmit.value == "") {
62 | checkInput = []
63 |
64 | title.classList.remove("error")
65 | author.classList.remove("error")
66 | year.classList.remove("error")
67 |
68 | errorTitle.classList.add("error-display")
69 | errorAuthor.classList.add("error-display")
70 | errorYear.classList.add("error-display")
71 |
72 | if (title.value == "") {
73 | checkTitle = false
74 | }else{
75 | checkTitle = true
76 | }
77 |
78 | if (author.value == "") {
79 | checkAuthor = false
80 | }else{
81 | checkAuthor = true
82 | }
83 |
84 | if (year.value == "") {
85 | checkYear = false
86 | }else{
87 | checkYear = true
88 | }
89 |
90 | checkInput.push(checkTitle,checkAuthor,checkYear)
91 | let resultCheck = validation(checkInput)
92 |
93 | if (resultCheck.includes(false)) {
94 | return false
95 | }else{
96 | const newBook = {
97 | id: +new Date(),
98 | title: title.value.trim(),
99 | author: author.value.trim(),
100 | year: year.value,
101 | isCompleted: readed.checked
102 | }
103 | insertData(newBook)
104 |
105 | title.value = ''
106 | author.value = ''
107 | year.value = ''
108 | readed.checked = false
109 | }
110 | }else{
111 | const bookData = getData().filter(a => a.id != btnSubmit.value);
112 | localStorage.setItem(localStorageKey,JSON.stringify(bookData))
113 |
114 | const newBook = {
115 | id: btnSubmit.value,
116 | title: title.value.trim(),
117 | author: author.value.trim(),
118 | year: year.value,
119 | isCompleted: readed.checked
120 | }
121 | insertData(newBook)
122 | btnSubmit.innerHTML = "Masukkan Buku"
123 | btnSubmit.value = ''
124 | title.value = ''
125 | author.value = ''
126 | year.value = ''
127 | readed.checked = false
128 | alert("Buku berhasil diedit")
129 | }
130 | })
131 |
132 | function validation(check) {
133 | let resultCheck = []
134 |
135 | check.forEach((a,i) => {
136 | if (a == false) {
137 | if (i == 0) {
138 | title.classList.add("error")
139 | errorTitle.classList.remove("error-display")
140 | resultCheck.push(false)
141 | }else if (i == 1) {
142 | author.classList.add("error")
143 | errorAuthor.classList.remove("error-display")
144 | resultCheck.push(false)
145 | }else{
146 | year.classList.add("error")
147 | errorYear.classList.remove("error-display")
148 | resultCheck.push(false)
149 | }
150 | }
151 | });
152 |
153 | return resultCheck
154 | }
155 |
156 | function insertData(book) {
157 | let bookData = []
158 |
159 |
160 | if (localStorage.getItem(localStorageKey) === null) {
161 | localStorage.setItem(localStorageKey, 0);
162 | }else{
163 | bookData = JSON.parse(localStorage.getItem(localStorageKey))
164 | }
165 |
166 | bookData.unshift(book)
167 | localStorage.setItem(localStorageKey,JSON.stringify(bookData))
168 |
169 | showData(getData())
170 | }
171 |
172 | function getData() {
173 | return JSON.parse(localStorage.getItem(localStorageKey)) || []
174 | }
175 |
176 | function showData(books = []) {
177 | const inCompleted = document.querySelector("#incompleteBookshelfList")
178 | const completed = document.querySelector("#completeBookshelfList")
179 |
180 | inCompleted.innerHTML = ''
181 | completed.innerHTML = ''
182 |
183 | books.forEach(book => {
184 | if (book.isCompleted == false) {
185 | let el = `
186 |
187 | ${book.title}
188 | Penulis: ${book.author}
189 | Tahun: ${book.year}
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 | `
198 |
199 | inCompleted.innerHTML += el
200 | }else{
201 | let el = `
202 |
203 | ${book.title}
204 | Penulis: ${book.author}
205 | Tahun: ${book.year}
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | `
214 | completed.innerHTML += el
215 | }
216 | });
217 | }
218 |
219 | function showSearchResult(books) {
220 | const searchResult = document.querySelector("#searchResult")
221 |
222 | searchResult.innerHTML = ''
223 |
224 | books.forEach(book => {
225 | let el = `
226 |
227 | ${book.title}
228 | Penulis: ${book.author}
229 | Tahun: ${book.year}
230 | ${book.isCompleted ? 'Sudah dibaca' : 'Belum dibaca'}
231 |
232 | `
233 |
234 | searchResult.innerHTML += el
235 | });
236 | }
237 |
238 | function readedBook(id) {
239 | let confirmation = confirm("Pindahkan ke selesai dibaca?")
240 |
241 | if (confirmation == true) {
242 | const bookDataDetail = getData().filter(a => a.id == id);
243 | const newBook = {
244 | id: bookDataDetail[0].id,
245 | title: bookDataDetail[0].title,
246 | author: bookDataDetail[0].author,
247 | year: bookDataDetail[0].year,
248 | isCompleted: true
249 | }
250 |
251 | const bookData = getData().filter(a => a.id != id);
252 | localStorage.setItem(localStorageKey,JSON.stringify(bookData))
253 |
254 | insertData(newBook)
255 | }else{
256 | return 0
257 | }
258 | }
259 |
260 | function unreadedBook(id) {
261 | let confirmation = confirm("Pindahkan ke belum selesai dibaca?")
262 |
263 | if (confirmation == true) {
264 | const bookDataDetail = getData().filter(a => a.id == id);
265 | const newBook = {
266 | id: bookDataDetail[0].id,
267 | title: bookDataDetail[0].title,
268 | author: bookDataDetail[0].author,
269 | year: bookDataDetail[0].year,
270 | isCompleted: false
271 | }
272 |
273 | const bookData = getData().filter(a => a.id != id);
274 | localStorage.setItem(localStorageKey,JSON.stringify(bookData))
275 |
276 | insertData(newBook)
277 | }else{
278 | return 0
279 | }
280 | }
281 |
282 | function editBook(id) {
283 | const bookDataDetail = getData().filter(a => a.id == id);
284 | title.value = bookDataDetail[0].title
285 | author.value = bookDataDetail[0].author
286 | year.value = bookDataDetail[0].year
287 | bookDataDetail[0].isCompleted ? readed.checked = true:readed.checked = false
288 |
289 | btnSubmit.innerHTML = "Edit buku"
290 | btnSubmit.value = bookDataDetail[0].id
291 | }
292 |
293 | function deleteBook(id) {
294 | let confirmation = confirm("Yakin akan menghapusnya?")
295 |
296 | if (confirmation == true) {
297 | const bookDataDetail = getData().filter(a => a.id == id);
298 | const bookData = getData().filter(a => a.id != id);
299 | localStorage.setItem(localStorageKey,JSON.stringify(bookData))
300 | showData(getData())
301 | alert(`Buku ${bookDataDetail[0].title} telah terhapus`)
302 | }else{
303 | return 0
304 | }
305 | }
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | margin: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | body,
8 | input,
9 | button {
10 | font-family: "Open Sans", sans-serif;
11 | /* background-color: #2c3e50; */
12 | }
13 |
14 | input,
15 | button {
16 | font-size: 16px;
17 | }
18 |
19 | .head_bar {
20 | padding: 12px;
21 | display: flex;
22 | align-items: center;
23 | justify-content: center;
24 | background-color: #2980b9;
25 | color: white;
26 | }
27 |
28 | main {
29 | max-width: 800px;
30 | width: 80%;
31 | margin: 0 auto;
32 | padding: 16px;
33 | }
34 |
35 | .input_section {
36 | display: flex;
37 | flex-direction: column;
38 | padding: 16px;
39 | border: 1px solid black;
40 | border-radius: 10px;
41 | }
42 |
43 | .input_section > h2 {
44 | text-align: center;
45 | color: #2980b9;
46 | }
47 |
48 | .input_section > form > .input {
49 | margin: 8px 0;
50 | }
51 |
52 | .input_section > form > button {
53 | background-color: #3498db;
54 | color: white;
55 | border: 0;
56 | border-radius: 5px;
57 | display: block;
58 | width: 100%;
59 | padding: 8px;
60 | cursor: pointer;
61 | }
62 |
63 | .input_section > form > button > span {
64 | font-weight: bold;
65 | }
66 |
67 | .input_section > form > .input > input {
68 | display: block;
69 | width: 100%;
70 | padding: 8px;
71 | border-radius: 5px;
72 | }
73 |
74 | .input_section > form > .input > label {
75 | color: #2980b9;
76 | font-weight: bold;
77 | }
78 |
79 | .input_section > form > .input_inline {
80 | margin: 12px 0;
81 | display: flex;
82 | align-items: center;
83 | }
84 |
85 | .input_section > form > .input_inline > label {
86 | color: #2980b9;
87 | font-weight: bold;
88 | margin-right: 10px;
89 | }
90 |
91 | .search_section {
92 | margin: 16px 0;
93 | display: flex;
94 | flex-direction: column;
95 | align-items: center;
96 | padding: 16px;
97 | border: 1px solid black;
98 | border-radius: 10px;
99 | }
100 |
101 | .search_section > h2 {
102 | color: #2980b9;
103 | }
104 |
105 | .search_section > form {
106 | padding: 16px;
107 | width: 100%;
108 | display: grid;
109 | grid-template-columns: auto 1fr 0.5fr;
110 | grid-gap: 10px;
111 | }
112 |
113 | .search_section > form > label {
114 | display: flex;
115 | align-items: center;
116 | }
117 |
118 | .search_section > form > input {
119 | padding: 5px;
120 | border-radius: 5px;
121 | }
122 |
123 | .search_section > form > button {
124 | background-color: #2980b9;
125 | color: white;
126 | border: 0;
127 | border-radius: 5px;
128 | cursor: pointer;
129 | }
130 |
131 | .book_shelf {
132 | margin: 16px 0 0 0;
133 | border: 1px solid black;
134 | padding: 16px;
135 | border-radius: 10px;
136 | }
137 |
138 | .book_shelf > h2 {
139 | color: #2980b9;
140 | }
141 |
142 | .book_shelf > .book_list {
143 | padding: 16px;
144 | }
145 |
146 | .book_shelf > .book_list > .book_item {
147 | padding: 8px 16px 16px 16px;
148 | border: 1px solid black;
149 | border-radius: 5px;
150 | margin: 10px 0;
151 | }
152 |
153 | .book_shelf > .book_list > .book_item > h3,
154 | p {
155 | margin: 8px 0;
156 | }
157 |
158 | .book_shelf > .book_list > .book_item > .action > button {
159 | border: 0;
160 | padding: 5px;
161 | margin: 0 5px 0 0;
162 | border-radius: 5px;
163 | cursor: pointer;
164 | }
165 |
166 | .book_shelf > .book_list > .book_item > .action > .green {
167 | background-color: #2ecc71;
168 | color: white;
169 | }
170 |
171 | .book_shelf > .book_list > .book_item > .action > .yellow {
172 | background-color: #f1c40f;
173 | color: white;
174 | }
175 |
176 | .book_shelf > .book_list > .book_item > .action > .red {
177 | background-color: #e74c3c;
178 | color: white;
179 | }
180 |
181 | .error {
182 | border: 2px solid red;
183 | }
184 |
185 | small {
186 | color: red;
187 | }
188 |
189 | .error-display {
190 | display: none;
191 | }
192 |
193 | .dark-button {
194 | background-color: #2c3e50;
195 | color: white;
196 | border: 0;
197 | border-radius: 5px;
198 | display: block;
199 | width: 100%;
200 | padding: 8px;
201 | cursor: pointer;
202 | margin-top: 16px;
203 | }
204 |
205 | .light-button {
206 | background-color: #ecf0f1;
207 | color: #2c3e50;
208 | border: 0;
209 | border-radius: 5px;
210 | display: block;
211 | width: 100%;
212 | padding: 8px;
213 | cursor: pointer;
214 | margin-top: 16px;
215 | }
216 |
217 | .body-dark {
218 | background-color: #34495e;
219 | color: #ecf0f1;
220 | }
221 |
222 | .head_bar-dark {
223 | background-color: #2c3e50;
224 | }
225 |
226 | .color-dark {
227 | color: #ecf0f1 !important;
228 | }
229 |
230 | .border-dark {
231 | border: 1px solid #ecf0f1 !important;
232 | }
233 |
--------------------------------------------------------------------------------