diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index 23acfa71..359c2e2d 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -1,96 +1,108 @@ - - - - - - - - - - - - -
-

Library

-

Add books to your virtual library

-
- - - + + + My Book Library + + + + + + + + +
+
+
+

My Book Library

+

Add books to your virtual library and track your reading progress

+
+
+ +
+
+ +
+ + + +
+
-
- - - - - - -
+ + + + + + + + + + + + +
TitleAuthorPagesRead StatusActions
+
+
+ + + + + + \ No newline at end of file diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 75ce6c1d..23239dfa 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -1,103 +1,356 @@ let myLibrary = []; -window.addEventListener("load", function (e) { +window.addEventListener("load", function () { + console.log("Page loaded, initializing library..."); populateStorage(); render(); + const form = document.getElementById("bookForm"); + if (form) { + form.addEventListener("submit", function(event) { + event.preventDefault(); + addBook(); + }); + } }); +function Book(title, author, pages, isRead) { + this.title = title; + this.author = author; + this.pages = parseInt(pages) || 0; + this.isRead = isRead || false; +} + function populateStorage() { - if (myLibrary.length == 0) { - let book1 = new Book("Robison Crusoe", "Daniel Defoe", "252", true); - let book2 = new Book( - "The Old Man and the Sea", - "Ernest Hemingway", - "127", - true - ); - myLibrary.push(book1); - myLibrary.push(book2); - render(); + if (myLibrary.length === 0) { + let book1 = new Book("Robinson Crusoe", "Daniel Defoe", 252, true); + let book2 = new Book("The Old Man and the Sea", "Ernest Hemingway", 127, true); + let book3 = new Book("How to Code", "Seddiq Azam", 324, false); + + myLibrary.push(book1, book2, book3); + console.log("Added initial books:", myLibrary); } } -const title = document.getElementById("title"); -const author = document.getElementById("author"); -const pages = document.getElementById("pages"); -const check = document.getElementById("check"); - -//check the right input from forms and if its ok -> add the new book (object in array) -//via Book function and start render function -function submit() { - if ( - title.value == null || - title.value == "" || - pages.value == null || - pages.value == "" - ) { - alert("Please fill all fields!"); - return false; - } else { - let book = new Book(title.value, title.value, pages.value, check.checked); - library.push(book); - render(); +function addBook() { + + const titleInput = document.getElementById("title"); + const authorInput = document.getElementById("author"); + const pagesInput = document.getElementById("pages"); + const checkInput = document.getElementById("check"); + + if (!titleInput || !authorInput || !pagesInput || !checkInput) { + showAlert("Form elements not found! Please refresh the page.", "danger"); + return; + } + + const title = titleInput.value.trim(); + const author = authorInput.value.trim(); + const pages = pagesInput.value.trim(); + const isRead = checkInput.checked; + + console.log("Form values:", { title, author, pages, isRead }); + + if (!title || !author || !pages) { + showAlert("Please fill in all required fields (Title, Author, and Pages)!", "warning"); + highlightEmptyFields(title, author, pages); + return; + } + + if (isNaN(pages) || parseInt(pages) <= 0) { + showAlert("Please enter a valid number of pages (greater than 0)!", "warning"); + pagesInput.classList.add("is-invalid"); + return; + } + + const duplicate = myLibrary.find( + (book) => + book.title.toLowerCase() === title.toLowerCase() && + book.author.toLowerCase() === author.toLowerCase() + ); + + if (duplicate) { + showAlert(`This book "${title}" by ${author} is already in your library!`, "info"); + return; + } + + const newBook = new Book(title, author, pages, isRead); + console.log("Created new book:", newBook); + + myLibrary.push(newBook); + console.log("Library after adding book:", myLibrary.length, "total books"); + + render(); + clearForm(); + + showAlert(`Successfully added "${title}" by ${author} to your library!`, "success"); + + if (typeof $ !== 'undefined') { + $("#demo").collapse('hide'); } } -function Book(title, author, pages, check) { - this.title = title; - this.author = author; - this.pages = pages; - this.check = check; +function highlightEmptyFields(title, author, pages) { + const titleInput = document.getElementById("title"); + const authorInput = document.getElementById("author"); + const pagesInput = document.getElementById("pages"); + + [titleInput, authorInput, pagesInput].forEach(input => { + input.classList.remove("is-invalid", "is-valid"); + }); + + if (!title) titleInput.classList.add("is-invalid"); + if (!author) authorInput.classList.add("is-invalid"); + if (!pages) pagesInput.classList.add("is-invalid"); +} + +function clearForm() { + const form = document.getElementById("bookForm"); + if (!form) { + console.error("Form element not found!"); + return; + } + + form.reset(); + + form.querySelectorAll(".is-invalid, .is-valid").forEach(input => { + input.classList.remove("is-invalid", "is-valid"); + }); + + console.log("Form cleared using .reset()"); } function render() { - let table = document.getElementById("display"); - let rowsNumber = table.rows.length; - //delete old table - for (let n = rowsNumber - 1; n > 0; n-- { - table.deleteRow(n); - } - //insert updated row and cells - let length = myLibrary.length; - for (let i = 0; i < length; i++) { - let row = table.insertRow(1); - let titleCell = row.insertCell(0); - let authorCell = row.insertCell(1); - let pagesCell = row.insertCell(2); - let wasReadCell = row.insertCell(3); - let deleteCell = row.insertCell(4); - titleCell.innerHTML = myLibrary[i].title; - authorCell.innerHTML = myLibrary[i].author; - pagesCell.innerHTML = myLibrary[i].pages; - - //add and wait for action for read/unread button - let changeBut = document.createElement("button"); - changeBut.id = i; - changeBut.className = "btn btn-success"; - wasReadCell.appendChild(changeBut); - let readStatus = ""; - if (myLibrary[i].check == false) { - readStatus = "Yes"; - } else { - readStatus = "No"; + console.log("Rendering library with", myLibrary.length, "books"); + clearTable(); + + if (typeof $ !== 'undefined') { + $("#demo").collapse('hide'); + } else { + const demoElement = document.getElementById("demo"); + if (demoElement && demoElement.classList.contains("show")) { + demoElement.classList.remove("show"); } - changeBut.innerText = readStatus; + } + + const tbody = document.querySelector("#display tbody"); + if (!tbody) { + console.error("Could not find table body element!"); + return; + } + + if (myLibrary.length === 0) { + showEmptyState("Your library is empty. Add some books to get started!"); + return; + } + + myLibrary.forEach((book, index) => { + const row = tbody.insertRow(); + + const titleCell = row.insertCell(0); + titleCell.textContent = book.title; + titleCell.style.fontWeight = "bold"; + + const authorCell = row.insertCell(1); + authorCell.textContent = book.author; + + const pagesCell = row.insertCell(2); + pagesCell.textContent = book.pages; + + const statusCell = row.insertCell(3); + const statusBtn = createStatusButton(book, index); + statusCell.appendChild(statusBtn); + + const actionCell = row.insertCell(4); + const deleteBtn = createDeleteButton(book, index); + actionCell.appendChild(deleteBtn); + }); +} - changeBut.addEventListener("click", function () { - myLibrary[i].check = !myLibrary[i].check; - render(); - }); +function createStatusButton(book, index) { + const btn = document.createElement("button"); + btn.className = book.isRead ? "btn btn-success btn-sm" : "btn btn-danger btn-sm"; + btn.textContent = book.isRead ? "Read" : "Not Read"; + btn.title = `Mark "${book.title}" as ${book.isRead ? "unread" : "read"}`; + btn.setAttribute("aria-label", btn.title); + btn.onclick = () => toggleReadStatus(index); + return btn; +} + +function createDeleteButton(book, index) { + const btn = document.createElement("button"); + btn.className = "btn btn-warning btn-sm"; + btn.textContent = "Delete"; + btn.title = `Delete "${book.title}"`; + btn.setAttribute("aria-label", `Delete ${book.title}`); + + btn.dataset.bookIndex = index; + btn.onclick = function() { + const bookIndex = parseInt(this.dataset.bookIndex); + deleteBook(bookIndex); + }; + + return btn; +} + +function clearTable() { + const tbody = document.querySelector("#display tbody"); + if (tbody) { + tbody.innerHTML = ""; + } +} + +function showEmptyState(message) { + const tbody = document.querySelector("#display tbody"); + if (tbody) { + const row = tbody.insertRow(); + const cell = row.insertCell(0); + cell.colSpan = 5; + cell.className = "text-center text-muted"; + cell.textContent = message; + } +} + +function toggleReadStatus(index) { + const book = myLibrary[index]; + if (book) { + book.isRead = !book.isRead; + console.log(`Toggled read status for "${book.title}" to ${book.isRead ? "Read" : "Not Read"}`); + render(); + + const statusMessage = book.isRead ? "marked as read" : "marked as unread"; + showAlert(`"${book.title}" ${statusMessage}!`, "info"); + } +} - //add delete button to every row and render again - let delButton = document.createElement("button"); - delBut.id = i + 5; - deleteCell.appendChild(delBut); - delBut.className = "btn btn-warning"; - delBut.innerHTML = "Delete"; - delBut.addEventListener("clicks", function () { - alert(`You've deleted title: ${myLibrary[i].title}`); - myLibrary.splice(i, 1); +function deleteBook(index) { + console.log("Delete function called with index:", index); + const book = myLibrary[index]; + console.log("Book to delete:", book); + + if (book) { + if (confirm(`Are you sure you want to delete "${book.title}" from your library?`)) { + const deletedBook = myLibrary.splice(index, 1)[0]; + console.log(`Deleted book "${deletedBook.title}" from library`); render(); - }); + showAlert(`"${deletedBook.title}" has been removed from your library!`, "warning"); + } + } else { + console.error("Book not found at index:", index); + } +} + +function viewReadBooks() { + console.log("Viewing read books only"); + clearTable(); + + // Hide the form when switching to read books view + if (typeof $ !== 'undefined') { + $("#demo").collapse('hide'); + } else { + const demoElement = document.getElementById("demo"); + if (demoElement && demoElement.classList.contains("show")) { + demoElement.classList.remove("show"); + } + } + + const tbody = document.querySelector("#display tbody"); + if (!tbody) { + console.error("Could not find table body element!"); + return; + } + + const readBooks = myLibrary.filter(book => book.isRead); + + if (readBooks.length === 0) { + showEmptyState("You haven't read any books yet!"); + return; + } + + readBooks.forEach((book) => { + const row = tbody.insertRow(); + + const titleCell = row.insertCell(0); + titleCell.textContent = book.title; + titleCell.style.fontWeight = "bold"; + + const authorCell = row.insertCell(1); + authorCell.textContent = book.author; + + const pagesCell = row.insertCell(2); + pagesCell.textContent = book.pages; + + const statusCell = row.insertCell(3); + const statusBtn = document.createElement("button"); + statusBtn.className = "btn btn-success btn-sm"; + statusBtn.textContent = "Read"; + statusBtn.title = `Mark "${book.title}" as unread`; + statusBtn.onclick = () => { + book.isRead = !book.isRead; + render(); // Switch back to full view + }; + statusCell.appendChild(statusBtn); + + const actionCell = row.insertCell(4); + const deleteBtn = document.createElement("button"); + deleteBtn.className = "btn btn-warning btn-sm"; + deleteBtn.textContent = "Delete"; + deleteBtn.title = `Delete "${book.title}"`; + deleteBtn.onclick = () => { + if (confirm(`Are you sure you want to delete "${book.title}" from your library?`)) { + const index = myLibrary.indexOf(book); + if (index !== -1) { + myLibrary.splice(index, 1); + viewReadBooks(); // Refresh the filtered view + } + } + }; + actionCell.appendChild(deleteBtn); + }); +} + +function showAlert(message, type) { + const alertContainer = document.getElementById("alertContainer"); + if (!alertContainer) { + console.error("Alert container not found!"); + return; } + + const alert = document.createElement("div"); + alert.className = `alert alert-${type} alert-dismissible fade show`; + alert.innerHTML = ` + ${getAlertIcon(type)} + ${message} + + `; + + alertContainer.appendChild(alert); + + setTimeout(() => { + alert.classList.remove("show"); + setTimeout(() => { + alertContainer.removeChild(alert); + }, 150); + }, 5000); } + +function getAlertIcon(type) { + switch (type) { + case "success": return ' '; + case "warning": return ' '; + case "danger": return ' '; + case "info": return ' '; + default: return ''; + } +} + +document.addEventListener('keydown', function(event) { + if (event.key === 'Escape') { + if (typeof $ !== 'undefined') { + $("#demo").collapse('hide'); + } + } +}); + +console.log("Book Library JavaScript loaded successfully!"); \ No newline at end of file diff --git a/debugging/book-library/style.css b/debugging/book-library/style.css index 302950cb..1e39d411 100644 --- a/debugging/book-library/style.css +++ b/debugging/book-library/style.css @@ -1,12 +1,16 @@ + .form-group { - width: 400px; - height: 300px; - align-self: left; - padding-left: 20px; + max-width: 400px; + padding: 20px; + margin: auto; +} + +button.btn { + margin: 10px; } -.btn { - display: block; +.row .btn { + margin-right: 10px; } .form-check-label { @@ -14,6 +18,153 @@ margin: 5px 0px 5px 0px; } -button.btn-info { - margin: 20px; +.jumbotron { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + margin-bottom: 2rem; + border-radius: 0.5rem; +} + +.collapse { + background: white; + padding: 2rem; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + margin-bottom: 2rem; + border: 1px solid #e9ecef; } + +.container { + background: #f8f9fa; + min-height: 100vh; + padding-bottom: 2rem; + padding-top: 1rem; +} + +.table { + margin-top: 1rem; + background: white; + border-radius: 0.5rem; + overflow: hidden; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +.table-responsive { + border-radius: 0.5rem; +} + +.table-striped > tbody > tr:nth-of-type(odd) > td { + background-color: rgba(0, 123, 255, 0.05); +} + +.btn:hover { + transform: translateY(-1px); + transition: all 0.2s ease; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +#alertContainer { + position: relative; + z-index: 1000; +} + +.alert { + margin-bottom: 1rem; + border-radius: 0.5rem; +} + +nav[role="navigation"] { + background: white; + padding: 1rem; + border-radius: 0.5rem; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + margin-bottom: 1rem; +} + +.form-control:focus { + border-color: #667eea; + box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25); +} + +@media (max-width: 768px) { + .btn { + margin: 5px 2px; + font-size: 0.9rem; + padding: 0.375rem 0.75rem; + } + + .form-group { + max-width: 100%; + padding: 15px; + } + + .table-responsive { + font-size: 0.9rem; + } + + .jumbotron { + padding: 1.5rem 1rem; + } + + .jumbotron h1 { + font-size: 2rem; + } + + .collapse { + padding: 1rem; + } + + nav[role="navigation"] { + padding: 0.5rem; + } +} + +@media (max-width: 576px) { + .btn { + width: 100%; + margin: 5px 0; + } + + .table { + font-size: 0.8rem; + } + + .btn-sm { + font-size: 0.7rem; + padding: 0.2rem 0.5rem; + } +} + +.btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.form-control.is-valid { + border-color: #28a745; +} + +.form-control.is-invalid { + border-color: #dc3545; +} + +.btn-primary { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border: none; +} + +.btn-primary:hover { + background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%); + border: none; +} + +.table .btn-sm { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + margin: 0 2px; +} + +.text-muted { + font-style: italic; + padding: 2rem; +} \ No newline at end of file