diff --git a/srv/poe_manager/app.py b/srv/poe_manager/app.py index cafd6db..64a4abb 100644 --- a/srv/poe_manager/app.py +++ b/srv/poe_manager/app.py @@ -343,10 +343,13 @@ def _nav_key_visible(key, user): return False -@app.context_processor -def inject_nav(): - if not current_user.is_authenticated: - return {} +def _ordered_nav_items(): + """Komplette Navbar-Struktur (Top-Level + Unterpunkte je Gruppe) in der + gespeicherten Reihenfolge, OHNE Sichtbarkeitsfilter — Grundlage sowohl + für die Sidebar (dort wird danach noch nach Berechtigung gefiltert) als + auch für den Reihenfolge-Editor auf der Account-Seite (der die + komplette Navbar zeigen soll, unabhängig davon, was der bearbeitende + Admin selbst sehen dürfte — als Admin ohnehin alles).""" try: stored_order = json.loads(get_setting("nav_order", "") or "[]") except (ValueError, TypeError): @@ -354,14 +357,36 @@ def inject_nav(): ordered_keys = [k for k in stored_order if k in NAV_ITEMS_BY_KEY] ordered_keys += [k for k in DEFAULT_NAV_ORDER if k not in ordered_keys] - nav_items_ordered = [] + try: + stored_child_order = json.loads(get_setting("nav_child_order", "") or "{}") + except (ValueError, TypeError): + stored_child_order = {} + + items = [] for k in ordered_keys: item = NAV_ITEMS_BY_KEY[k] + if item.get("children"): + children_by_key = {c["key"]: c for c in item["children"]} + stored = stored_child_order.get(k, []) + child_keys = [ck for ck in stored if ck in children_by_key] + child_keys += [c["key"] for c in item["children"] if c["key"] not in child_keys] + items.append({**item, "children": [children_by_key[ck] for ck in child_keys]}) + else: + items.append(item) + return items + + +@app.context_processor +def inject_nav(): + if not current_user.is_authenticated: + return {} + nav_items_ordered = [] + for item in _ordered_nav_items(): if item.get("children"): visible_children = [c for c in item["children"] if _nav_key_visible(c["key"], current_user)] if visible_children: nav_items_ordered.append({**item, "children": visible_children}) - elif _nav_key_visible(k, current_user): + elif _nav_key_visible(item["key"], current_user): nav_items_ordered.append(item) return {"nav_items_ordered": nav_items_ordered} @@ -880,7 +905,13 @@ def logout(): @app.route("/account") @login_required def account(): - return render_template("account.html") + # Volle, ungefilterte Navbar-Struktur für den Reihenfolge-Editor (nur + # für Admins sichtbar/relevant) — anders als das automatisch injizierte + # nav_items_ordered (das nach Berechtigung des jeweils eingeloggten + # Benutzers filtert), damit auch Unterpunkte editierbar sind, die der + # bearbeitende Admin selbst nicht als Nicht-Admin sehen würde. + full_nav_items = _ordered_nav_items() if current_user.is_admin else [] + return render_template("account.html", full_nav_items=full_nav_items) @app.route("/profile", methods=["POST"]) @@ -1692,6 +1723,19 @@ def save_nav_order(): order = [k for k in request.form.getlist("nav_order") if k in NAV_ITEMS_BY_KEY] order += [k for k in DEFAULT_NAV_ORDER if k not in order] set_setting("nav_order", json.dumps(order)) + + # Unterpunkt-Reihenfolge je Gruppe — ein Feld "nav_child_order_" + # pro Gruppe mit Kindern, Werte in der gewünschten Reihenfolge. + child_order = {} + for group_key, item in NAV_ITEMS_BY_KEY.items(): + if not item.get("children"): + continue + valid_child_keys = {c["key"] for c in item["children"]} + submitted = [k for k in request.form.getlist(f"nav_child_order_{group_key}") if k in valid_child_keys] + submitted += [c["key"] for c in item["children"] if c["key"] not in submitted] + child_order[group_key] = submitted + set_setting("nav_child_order", json.dumps(child_order)) + log_action("settings.update", "Navbar-Reihenfolge") flash("Navbar-Reihenfolge gespeichert.", "success") return redirect(url_for("account")) diff --git a/srv/poe_manager/static/css/style.css b/srv/poe_manager/static/css/style.css index e9b3019..2ff0ded 100644 --- a/srv/poe_manager/static/css/style.css +++ b/srv/poe_manager/static/css/style.css @@ -217,9 +217,6 @@ button { font-family: inherit; } margin-bottom: 16px; } .nav-order-list li { - display: flex; - align-items: center; - justify-content: space-between; padding: 8px 12px; background: var(--bg-card); border: 1px solid var(--border-soft); @@ -227,8 +224,20 @@ button { font-family: inherit; } font-size: 13.5px; font-weight: 550; } +.nav-order-row { display: flex; align-items: center; justify-content: space-between; } .nav-order-list .nav-order-actions { display: flex; gap: 4px; } .nav-order-list .icon-btn { width: 28px; height: 28px; } +/* Unterpunkte je Gruppe — eingerückt, innerhalb ihrer Gruppe umsortierbar + (moveNavItem() bewegt nur innerhalb der jeweiligen