Topbar auf globalen Prüf-Timer reduziert, Aktionen+Beschreibung über Tabellen, sortierbare Listen

- Topbar zeigt rechts jetzt ausschließlich den "Nächste Prüfung"-Countdown,
  konsistent auf jeder Seite (auch anonymes Dashboard) statt nur auf dem
  Dashboard. Neuer Context-Processor inject_check_timer()/get_last_run_at()
  liefert last_run/interval global, ohne dass jede Route das selbst
  berechnen muss.
- "+ Neu ..."-Buttons (Devices, Switches, Zugangsdaten, Benutzer, Gruppen)
  aus der Topbar entfernt und stattdessen in einen .section-head direkt
  über der jeweiligen Tabelle verschoben, zusammen mit einer kurzen
  Beschreibung der Seite (bisher nur bei Zugangsdaten/Gruppen vorhanden,
  jetzt auch bei Geräte/Switche/Benutzer).
- Live-Log: eigene lokale Timer-Pill entfernt (redundant zum globalen
  Timer), "Aktualisieren"-Button in denselben section-head verschoben.
  Dashboard: Suchfeld aus der Topbar in den Seiteninhalt verschoben, lokale
  Timer-Anzeige entfernt (übernimmt die globale Topbar-Pill), Reload-bei-
  Intervallende-Logik bleibt als separater, unsichtbarer Scheduler erhalten.
- Neue generische Tabellen-Sortierung (app.js: initSortableTables): Klick
  auf eine Spaltenüberschrift mit data-sort-key sortiert die Zeilen anhand
  von data-sort-<key>-Attributen. Unterstützt auch Akkordeon-Tabellen mit
  mehreren <tbody> (Gruppen: Haupt- + Detail-Zeile bleiben als Einheit
  zusammen, die virtuelle "Admin"-Zeile bleibt über data-sort-pinned immer
  oben). Angewendet auf Geräte, Switche, Zugangsdaten, Benutzer, Gruppen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 15:21:30 +02:00
co-authored by Claude Sonnet 5
parent e4cceb4091
commit 0fcb6c94cb
12 changed files with 287 additions and 127 deletions
+34
View File
@@ -651,6 +651,40 @@ def _format_ts(ts_str: str) -> str:
return ts_str
def get_last_run_at():
"""Zeitpunkt des letzten Prüf-Durchlaufs (jüngste Zeile im aktuellsten
Logfile) — Basis für den globalen 'Nächste Prüfung'-Countdown in der
Topbar, unabhängig von einer bestimmten Geräteliste."""
latest_log = _latest_log_file()
if not latest_log:
return None
latest_ts_str = None
with open(latest_log, "r") as f:
for line in f:
m = _LOG_LINE_RE.match(line.strip())
if m and (latest_ts_str is None or m.group(1) > latest_ts_str):
latest_ts_str = m.group(1)
if not latest_ts_str:
return None
try:
return datetime.strptime(latest_ts_str, "%Y-%m-%d %H:%M:%S")
except ValueError:
return None
@app.context_processor
def inject_check_timer():
"""Stellt den 'Nächste Prüfung'-Countdown global für die Topbar bereit
(auf jeder Seite sichtbar, siehe base.html) — unabhängig davon, ob die
jeweilige Route selbst etwas mit Geräten/Logs zu tun hat."""
interval = int(get_setting("interval", 5))
last_run_at = get_last_run_at()
return {
"global_check_interval": interval,
"global_last_run_epoch_ms": int(last_run_at.timestamp() * 1000) if last_run_at else None,
}
def get_device_status(devices):
"""
Ermittelt aus dem aktuellsten Logfile in einem Durchlauf je Gerät: