From 4f40bb06b35e59424ca38dc2138229078a80ad03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Thu, 14 Aug 2025 23:55:58 +0200 Subject: [PATCH] duzo zmian ux w panelu --- app.py | 64 ++++++-- static/css/style.css | 60 ++++++- static/js/categories_select_admin.js | 11 ++ templates/admin/admin_panel.html | 37 ++--- templates/admin/edit_list.html | 182 +++++++++++----------- templates/admin/list_products.html | 50 ++++-- templates/admin/mass_edit_categories.html | 34 ++-- templates/main.html | 22 +-- 8 files changed, 301 insertions(+), 159 deletions(-) diff --git a/app.py b/app.py index 7273675..852e35e 100644 --- a/app.py +++ b/app.py @@ -2834,21 +2834,53 @@ def edit_list(list_id): @login_required @admin_required def list_products(): - items = Item.query.order_by(Item.id.desc()).all() - users = db.session.query(User).all() + items = Item.query.options( + joinedload(Item.shopping_list), + joinedload(Item.added_by_user), + ).order_by(Item.id.desc()).all() + + users = User.query.all() users_dict = {user.id: user.username for user in users} suggestions = SuggestedProduct.query.order_by(SuggestedProduct.name.asc()).all() - suggestions_dict = {s.name.lower(): s for s in suggestions} + all_suggestions_dict = { + (s.name or "").strip().lower(): s for s in suggestions if s.name and s.name.strip() + } + + seen_names = set() + unique_items = [] + used_suggestion_names = set() + + for item in items: + normalized_name = (item.name or "").strip().lower() + if not normalized_name: + continue + if normalized_name not in seen_names: + seen_names.add(normalized_name) + unique_items.append(item) + used_suggestion_names.add(normalized_name) + + orphan_suggestions = [ + s for s in suggestions + if (s.name or "").strip().lower() not in used_suggestion_names and (s.name or "").strip() + ] + + suggestions_dict = { + name: all_suggestions_dict[name] + for name in used_suggestion_names + if name in all_suggestions_dict + } return render_template( "admin/list_products.html", - items=items, + items=unique_items, users_dict=users_dict, suggestions_dict=suggestions_dict, + orphan_suggestions=orphan_suggestions, ) + @app.route("/admin/sync_suggestion/", methods=["POST"]) @login_required def sync_suggestion_ajax(item_id): @@ -2954,25 +2986,37 @@ def recalculate_filesizes_all(): @admin_required def admin_mass_edit_categories(): lists = ( - ShoppingList.query.options(joinedload(ShoppingList.categories)) + ShoppingList.query.options( + joinedload(ShoppingList.categories), + joinedload(ShoppingList.items), + joinedload(ShoppingList.owner), + ) .order_by(ShoppingList.created_at.desc()) .all() ) + categories = Category.query.order_by(Category.name.asc()).all() + for l in lists: + l.total_count = len(l.items) + l.owner_name = l.owner.username if l.owner else "?" + l.category_count = len(l.categories) + if request.method == "POST": - for lst in lists: - selected_ids = request.form.getlist(f"categories_{lst.id}") - lst.categories.clear() + for l in lists: + selected_ids = request.form.getlist(f"categories_{l.id}") + l.categories.clear() if selected_ids: cats = Category.query.filter(Category.id.in_(selected_ids)).all() - lst.categories.extend(cats) + l.categories.extend(cats) db.session.commit() flash("Zaktualizowano kategorie dla wybranych list", "success") return redirect(url_for("admin_mass_edit_categories")) return render_template( - "admin/mass_edit_categories.html", lists=lists, categories=categories + "admin/mass_edit_categories.html", + lists=lists, + categories=categories ) diff --git a/static/css/style.css b/static/css/style.css index aa9ee3a..92ff3e8 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -352,12 +352,10 @@ input.form-control { background-color: #212529 !important; color: #fff !important; border: 1px solid #495057 !important; -} - -.ts-dropdown { - background-color: #212529 !important; - color: #fff !important; - z-index: 9999 !important; + border-radius: 0.375rem; + min-height: 38px; + padding: 0.25rem 0.5rem; + box-sizing: border-box; } .tom-dark .ts-control .item { @@ -365,4 +363,54 @@ input.form-control { color: #fff !important; border-radius: 0.25rem; padding: 2px 8px; + margin-right: 4px; +} + +.ts-dropdown { + background-color: #212529 !important; + color: #fff !important; + border: 1px solid #495057; + border-radius: 0.375rem; + z-index: 9999 !important; + max-height: 300px; + overflow-y: auto; +} + +.ts-dropdown .active { + background-color: #495057 !important; + color: #fff !important; +} + +td select.tom-dark { + width: 100%; + max-width: 100%; + box-sizing: border-box; +} + +.table-dark.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(255, 255, 255, 0.025); +} + +.table-dark tbody tr:hover { + background-color: rgba(255, 255, 255, 0.04); +} + +.table-dark thead th { + background-color: #1c1f22; + color: #e1e1e1; + font-weight: 500; + border-bottom: 1px solid #3a3f44; +} + +.table-dark td, +.table-dark th { + padding: 0.6rem 0.75rem; + vertical-align: middle; + border-top: 1px solid #3a3f44; +} + +.card .table { + border-radius: 0 !important; + overflow: hidden; + margin-bottom: 0; } \ No newline at end of file diff --git a/static/js/categories_select_admin.js b/static/js/categories_select_admin.js index e69de29..e36d16d 100644 --- a/static/js/categories_select_admin.js +++ b/static/js/categories_select_admin.js @@ -0,0 +1,11 @@ +document.addEventListener("DOMContentLoaded", function () { + document.querySelectorAll("select.tom-dark").forEach(function (el) { + new TomSelect(el, { + plugins: ['remove_button'], + persist: false, + create: false, + hidePlaceholder: true, + dropdownParent: 'body' + }); + }); +}); diff --git a/templates/admin/admin_panel.html b/templates/admin/admin_panel.html index c03b882..2b41c7e 100644 --- a/templates/admin/admin_panel.html +++ b/templates/admin/admin_panel.html @@ -263,18 +263,14 @@
- ✏️ - - 🗑️ + 🗑️
@@ -298,18 +294,17 @@