diff --git a/app.py b/app.py index 6ad7fd8..b0a3506 100644 --- a/app.py +++ b/app.py @@ -29,6 +29,41 @@ def submit_isbn(): return f"{book.title} ajouté (plusieurs occurrences)" return f"{book.title} ajouté" +@app.route("/") +def index(): + return render_template("index.html", books=isbn_db.get_all_books()) + + +@app.route("/delete-book", methods=["POST"]) +def delete_book(): + if "isbn" not in request.form: + return "missing isbn" + + isbn_db.delete_book(request.form["isbn"]) + return redirect("/") + +@app.route("/update-book", methods=["POST"]) +def update_book(): + attributes = ["isbn", "count", "title", "author", "publisher", "publish_date"] + if True in [i not in request.form for i in attributes]: + return "missing an attribute" + + form_data = request.form.copy() + + for attribute in attributes: + if form_data[attribute] == "None": + form_data[attribute] = None + + book = Book(form_data["isbn"]) + book._manual_load( + form_data["title"], + publisher=form_data["publisher"], + publish_date=form_data["publish_date"], + author=form_data["author"], + count=int(form_data["count"]) + ) + isbn_db.update_book(book) + return redirect("/") @app.after_request def after_request(response): diff --git a/book.py b/book.py index a9e046b..cae391c 100644 --- a/book.py +++ b/book.py @@ -7,7 +7,7 @@ class Book: return None try: - isbn = isbn.replace("-", "") + isbn = str(isbn).replace("-", "") int(isbn) except ValueError: raise ValueError("ISBN must be an int") diff --git a/isbn_db.py b/isbn_db.py index e82a4dc..4dcce3e 100644 --- a/isbn_db.py +++ b/isbn_db.py @@ -40,6 +40,17 @@ def get_book(isbn): ) return book +def delete_book(isbn): + db = get_db() + + data = db.execute( + """ + DELETE FROM book WHERE isbn=? + """, + (isbn,) + ) + db.commit() + def increment_count(book): if book.count == -1: book = get_book(book.isbn) @@ -74,3 +85,49 @@ def add_book(book): db.commit() return "added" +def update_book(book): + db = get_db() + db.execute( + """ + UPDATE book SET count=?, title=?, author=?, publisher=?, publish_date=? + WHERE isbn=? + """, + (book.count, book.title, book.author, book.publisher, book.publish_date, book.isbn) + ) + db.commit() + return "updated" + +def get_all_books(): + def count_none(book): + count = 0 + if book.title is None: + count += 1 + if book.author is None: + count += 1 + if book.publish_date is None: + count += 1 + if book.publisher is None: + count += 1 + return -count + + db = get_db() + + data = db.execute( + """ + SELECT * FROM book + """ + ).fetchall() + + books = [] + for data_row in data: + book = Book(data_row["isbn"]) + book._manual_load( + data_row["title"], + publisher=data_row["publisher"], + publish_date=data_row["publish_date"], + author=data_row["author"], + count=data_row["count"] + ) + books.append(book) + + return sorted(books, key=count_none) \ No newline at end of file diff --git a/static/main.js b/static/main.js new file mode 100644 index 0000000..f0ee094 --- /dev/null +++ b/static/main.js @@ -0,0 +1,82 @@ +getBookData = (isbn) => { + var table = document.getElementById("books-table"); + + // Find all rows in the table except the header + var rows = table.getElementsByTagName("tr"); + + // Iterate through each row + for (var i = 1; i < rows.length; i++) { // starting from 1 to skip the header row + var cells = rows[i].getElementsByTagName("td"); + + // Check if the ISBN in the current row matches the given ISBN + if (cells[0].innerText == isbn) { + // Extract data from the row + var title = cells[1].innerText; + var author = cells[2].innerText; + var date = cells[3].innerText; + var publisher = cells[4].innerText; + var quantity = cells[5].innerText; + + // Return the data + return { + title: title, + author: author, + date: date, + publisher: publisher, + quantity: quantity + }; + } + } + + // If ISBN is not found, return null + return null; +} + +function edit_book(isbn) { + var bookData = getBookData(isbn); + if (bookData) { + var editDialog = document.getElementById("edit-book-dialog"); + document.getElementById("edit-isbn").value = isbn; + document.getElementById("edit-title").value = bookData.title; + document.getElementById("edit-author").value = bookData.author; + document.getElementById("edit-date").value = bookData.date; + document.getElementById("edit-publisher").value = bookData.publisher; + document.getElementById("edit-quantity").value = bookData.quantity; + editDialog.showModal(); + } else { + alert("Book not found!"); + } +} + +// Function to hide the edit-book dialog +function hideEditBookDialog() { + var editDialog = document.getElementById("edit-book-dialog"); + editDialog.close(); +} + +// Function to show the delete-book dialog +function delete_book(isbn) { + var bookData = getBookData(isbn); + console.log(isbn, bookData) + if (bookData === null) + return; + + var deleteDialog = document.getElementById("delete-book-dialog"); + document.getElementById("delete-isbn").value = isbn; + document.getElementById("delete-book-name").innerText = bookData.title; + if (bookData.title === undefined || bookData.title == "None") + document.getElementById("delete-book-name").innerText = "ISBN:"+isbn; + + + deleteDialog.showModal(); + // Handle cancel delete + document.getElementById("cancel-delete").onclick = function() { + deleteDialog.close(); + }; +} + +// Function to hide the delete-book dialog +function hideDeleteBookDialog() { + var deleteDialog = document.getElementById("delete-book-dialog"); + deleteDialog.close(); +} \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..495ba2e --- /dev/null +++ b/static/style.css @@ -0,0 +1,274 @@ +@import url('/static/font/iosevka.css'); + +/** Color Schemes */ +/* Themes used: Catppuccin Latte & Moccha + * https://github.com/catppuccin/catppuccin */ + +/* Dark theme: Catpuccin Mocha */ +:root { + --color-rosewater: #f5e0dc; + --color-flamingo: #f2cdcd; + --color-pink: #f5c2e7; + --color-mauve: #cba6f7; + --color-red: #f38ba8; + --color-maroon: #eba0ac; + --color-peach: #fab387; + --color-yellow: #f9e2af; + --color-green: #a6e3a1; + --color-teal: #94e2d5; + --color-sky: #89dceb; + --color-sapphire: #74c7ec; + --color-blue: #89b4fa; + --color-lavender: #b4befe; + --color-text: #cdd6f4; + --color-subtext1: #bac2de; + --color-subtext0: #a6adc8; + --color-overlay2: #9399b2; + --color-overlay1: #7f849c; + --color-overlay0: #6c7086; + --color-surface2: #585b70; + --color-surface1: #45475a; + --color-surface0: #313244; + --color-base: #1e1e2e; + --color-mantle: #181825; + --color-crust: #11111b; + --font-family: Iosevka Web; +} + +/* Light theme: Catppuccin Latte */ +@media (prefers-color-scheme: light) { + :root { + --color-rosewater: #dc8a78; + --color-flamingo: #dd7878; + --color-pink: #ea76cb; + --color-mauve: #8839ef; + --color-red: #d20f39; + --color-maroon: #e64553; + --color-peach: #fe640b; + --color-yellow: #df8e1d; + --color-green: #40a02b; + --color-teal: #179299; + --color-sky: #04a5e5; + --color-sapphire: #209fb5; + --color-blue: #1e66f5; + --color-lavender: #7287fd; + --color-text: #4c4f69; + --color-subtext1: #5c5f77; + --color-subtext0: #6c6f85; + --color-overlay2: #7c7f93; + --color-overlay1: #8c8fa1; + --color-overlay0: #9ca0b0; + --color-surface2: #acb0be; + --color-surface1: #bcc0cc; + --color-surface0: #ccd0da; + --color-base: #eff1f5; + --color-mantle: #e6e9ef; + --color-crust: #dce0e8; + } +} + +* { + font-family: var(--font-family); +} + +a { + text-decoration: none; + color: var(--color-blue); +} + +#stats { + display: flex; + background-color: var(--color-crust); + text-align: center; + justify-content: center; + align-items: center; +} + +#stats-used { + margin-left: 10px; + margin-right: 5px; +} + +#stats-available { + margin-left: 5px; + margin-right: 10px; +} + +.stats-block { + align-items: center; + justify-content: center; + border-radius: 3px; + background-color: var(--color-base); + display: flex; + + height: 40px; + padding: 0 15px; + + margin-top: 20px; + margin-bottom: 20px; + + color: var(--color-text); + text-decoration: none; +} + +body { + background-color: var(--color-base); + color: var(--color-text); + text-align: center; +} + +#logout { + margin-bottom: 5px; + color: var(--color-text); +} + +#actions { + margin-top: 5%; +} + +#add-voucher { + margin-bottom: 5%; +} + +form { + margin-top: 5px; +} + +input { + color: inherit; + background-color: var(--color-mantle); + border-radius: 3px; + border-style: solid; + border-color: var(--color-overlay0); + margin-bottom: .2em; + font-family: inherit; + width: -moz-available; + padding: .5em; +} + +input:placeholder-shown { + font-style: oblique; +} + +input:hover { + background-color: var(--color-crust); +} + +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input[type=number] { + -moz-appearance: textfield; +} + + + +.barcode { + width: 80%; + border-radius: 3px; +} + +.table-barcode.barcode { + max-width: 150px; +} + +.voucher { + background-color: var(--color-crust); + border-radius: 5px; + margin-bottom: 30px; + padding-top: 10px; + padding-bottom: 10px; +} + +button { + color: inherit; + background-color: var(--color-crust); + border-color: var(--color-crust); + border-style: solid; + border-radius: 3px; + border-width: 2px; + padding: 5px; + font-family: inherit; +} + +button:hover { + border-color: var(--color-lavender); +} + +.flash { + margin: 1em; + padding: 1em; + background: var(--color-mantle); + border: 2px solid var(--color-lavender); + border-radius: 3px; +} + +.stats-block:hover { + background-color: var(--color-lavender); + color: var(--color-crust); + transition: transform 0.2s ease-in-out; +} + + +pre { + background-color: var(--color-mantle); + border-radius: 4px; + padding: 10px; + font-family: monospace; + font-size: 14px; + line-height: 1.5; + overflow-x: auto; + text-align: left; +} + +code { + font-family: monospace; +} + +.red { + color: var(--color-red); +} + +/** Style for table: alternate colors */ +table { + border-collapse: collapse; + width: 100%; + margin-top: 20px; + border-color: var(--color-crust); + border-width: 2px; + border-style: solid; +} + +.action { + padding: 7px; + margin: 3px; + border-radius: 3px; + box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.2); + background-color: #cdd6f4; +} + +th { + background-color: var(--color-crust); +} + +table tr:nth-child(odd) td{ + background-color: var(--color-mantle); +} +table tr:nth-child(even) td{ + background-color: var(--color-base); +} + +dialog { + /* Geometry */ + border-width: 2px; + border-radius: 3px; + + /* Colors */ + background-color: var(--color-crust); + color: var(--color-text); + + min-width: 75vw; +} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..b898d9a --- /dev/null +++ b/templates/base.html @@ -0,0 +1,24 @@ + + + + + + + + + + {% block title %}{% endblock %} + + + + +
+ {% for message in get_flashed_messages() %} +
{{ message }}
+ {% endfor %} +
+ {% block content %}{% endblock %} +
+
+ + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..3cd298d --- /dev/null +++ b/templates/index.html @@ -0,0 +1,61 @@ +{% extends 'base.html' %} + +{% block title %}Table{% endblock %} + +{% block header %} +

Table des livres

+{% endblock %} + +{% block content %} + +
+ +
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +

Êtes-vous sûr de supprimer ce livre ?

+ Nom du livre... +
+ + +
+ +
+ + + + + + + + + + + {% for book in books %} + + + + + + + + + + {% endfor %} +
ISBNTitreAuteurDateÉditeurQuantitéActions
{{ book.isbn }}

{{ book.title }}

{{ book.author }}

{{ book.publish_date }}

{{ book.publisher }}

{{ book.count }} + + +
+{% endblock %}