Compare commits

...

2 Commits

Author SHA1 Message Date
Erik Thiele
e3afda9a8f chore: Version auf 6.2.9 heben
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 19:47:35 +02:00
Erik Thiele
20055c3b74 feat: Verlauf im Dashboard seitenweise blaettern
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 19:47:35 +02:00
2 changed files with 61 additions and 8 deletions

43
app.py
View File

@@ -50,8 +50,10 @@ MEDIA_DIR = os.path.join(BASE_DIR, "media") # Medien-Dateien je Standort/S
CONFIG_FILE = os.path.join(BASE_DIR, "config.json") # Persistente Konfiguration CONFIG_FILE = os.path.join(BASE_DIR, "config.json") # Persistente Konfiguration
USERS_FILE = os.path.join(BASE_DIR, "users.json") # User-Datenbank USERS_FILE = os.path.join(BASE_DIR, "users.json") # User-Datenbank
HISTORY_FILE = os.path.join(BASE_DIR, "history.json") # Aktivitätsverlauf HISTORY_FILE = os.path.join(BASE_DIR, "history.json") # Aktivitätsverlauf
HISTORY_MAX = 1000 # so viele Einträge werden aufbewahrt
HISTORY_PER_PAGE = 25 # Einträge je Seite im Dashboard-Verlauf
APP_VERSION = "6.2.8" APP_VERSION = "6.2.9"
UPLOAD_EXTENSIONS = {".jpg", ".jpeg", ".png", ".mp4"} UPLOAD_EXTENSIONS = {".jpg", ".jpeg", ".png", ".mp4"}
app = Flask(__name__) app = Flask(__name__)
@@ -183,24 +185,33 @@ def add_history_entry(action, detail, user_email):
except (json.JSONDecodeError, OSError): except (json.JSONDecodeError, OSError):
history = [] history = []
history.insert(0, entry) # Neueste zuerst history.insert(0, entry) # Neueste zuerst
# Maximal 200 Einträge behalten # Maximal HISTORY_MAX Einträge behalten
history = history[:200] history = history[:HISTORY_MAX]
with open(HISTORY_FILE, "w") as f: with open(HISTORY_FILE, "w") as f:
json.dump(history, f, indent=2) json.dump(history, f, indent=2)
def get_history(limit=50): def _load_history():
"""Gibt die letzten history-Einträge zurück.""" """Liest den kompletten Verlauf; bei defekter Datei eine leere Liste."""
if not os.path.exists(HISTORY_FILE): if not os.path.exists(HISTORY_FILE):
return [] return []
try: try:
with open(HISTORY_FILE) as f: with open(HISTORY_FILE) as f:
history = json.load(f) return json.load(f)
return history[:limit]
except (json.JSONDecodeError, OSError): except (json.JSONDecodeError, OSError):
return [] return []
def get_history(limit=50, offset=0):
"""Gibt einen Ausschnitt des Verlaufs zurück (neueste zuerst)."""
return _load_history()[offset:offset + limit]
def get_history_count():
"""Anzahl aller gespeicherten Verlaufseintraege (fuer die Seitennavigation)."""
return len(_load_history())
def get_screen_config(cfg, site, screen): def get_screen_config(cfg, site, screen):
"""Holt die Konfiguration für einen bestimmten Screen. """Holt die Konfiguration für einen bestimmten Screen.
Erzeugt automatisch fehlende Dict-Ebenen (sites → site → screens → screen).""" Erzeugt automatisch fehlende Dict-Ebenen (sites → site → screens → screen)."""
@@ -2080,8 +2091,19 @@ def admin_dashboard():
admin_count = sum(1 for u in users_data.values() if u.get("role") == "admin") admin_count = sum(1 for u in users_data.values() if u.get("role") == "admin")
superuser_count = sum(1 for u in users_data.values() if u.get("role") == "superuser") superuser_count = sum(1 for u in users_data.values() if u.get("role") == "superuser")
# Kennzahlen und Sparklines bleiben bewusst am jüngsten Ausschnitt haengen,
# damit sie sich beim Blaettern im Verlauf nicht veraendern.
history = get_history(30) history = get_history(30)
gesamt = get_history_count()
seiten = max(1, (gesamt + HISTORY_PER_PAGE - 1) // HISTORY_PER_PAGE)
try:
seite = int(request.args.get("page", 1))
except ValueError:
seite = 1
seite = min(max(seite, 1), seiten)
history_page = get_history(HISTORY_PER_PAGE, (seite - 1) * HISTORY_PER_PAGE)
# Sparkline-Daten aus dem Verlauf berechnen # Sparkline-Daten aus dem Verlauf berechnen
def build_sparkline(entries, creates, deletes): def build_sparkline(entries, creates, deletes):
seq = list(reversed(entries)) seq = list(reversed(entries))
@@ -2130,7 +2152,12 @@ def admin_dashboard():
user_trend=user_trend, user_trend=user_trend,
admin_trend=admin_trend, admin_trend=admin_trend,
superuser_trend=superuser_trend, superuser_trend=superuser_trend,
history=history, history=history_page,
history_page=seite,
history_pages=seiten,
history_total=gesamt,
history_from=(seite - 1) * HISTORY_PER_PAGE + 1 if gesamt else 0,
history_to=min(seite * HISTORY_PER_PAGE, gesamt),
site_list=get_accessible_sites(cfg, current_user), site_list=get_accessible_sites(cfg, current_user),
current_site=current_site_for(cfg, current_user), current_site=current_site_for(cfg, current_user),
version=APP_VERSION, version=APP_VERSION,

View File

@@ -121,6 +121,9 @@
<div class="card mt-4"> <div class="card mt-4">
<div class="card-header"> <div class="card-header">
<h3 class="card-title"><i class="ti ti-history me-1"></i>Verlauf</h3> <h3 class="card-title"><i class="ti ti-history me-1"></i>Verlauf</h3>
{% if history_total %}
<span class="card-subtitle text-secondary small ms-auto">{{ history_from }}–{{ history_to }} von {{ history_total }}</span>
{% endif %}
</div> </div>
<div class="card-body p-0"> <div class="card-body p-0">
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
@@ -171,6 +174,29 @@
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
{% if history_pages > 1 %}
<div class="card-footer d-flex align-items-center">
<p class="m-0 text-secondary small">Seite {{ history_page }} von {{ history_pages }}</p>
<ul class="pagination m-0 ms-auto">
<li class="page-item {% if history_page <= 1 %}disabled{% endif %}">
<a class="page-link" href="?page={{ history_page - 1 }}"><i class="ti ti-chevron-left"></i> zurück</a>
</li>
{# Nur ein Fenster um die aktuelle Seite zeigen, sonst wird die Leiste bei vielen Seiten zu lang #}
{% for p in range(1, history_pages + 1) %}
{% if p == 1 or p == history_pages or (p >= history_page - 2 and p <= history_page + 2) %}
<li class="page-item {% if p == history_page %}active{% endif %}">
<a class="page-link" href="?page={{ p }}">{{ p }}</a>
</li>
{% elif p == history_page - 3 or p == history_page + 3 %}
<li class="page-item disabled"><span class="page-link">…</span></li>
{% endif %}
{% endfor %}
<li class="page-item {% if history_page >= history_pages %}disabled{% endif %}">
<a class="page-link" href="?page={{ history_page + 1 }}">weiter <i class="ti ti-chevron-right"></i></a>
</li>
</ul>
</div>
{% endif %}
</div> </div>
</div> </div>