feat: kundenspezifisches Logo unter Einstellungen

Bisher war das Logo fest im Repository verdrahtet. Damit hat ein Update
das Logo eines Kunden ueberschrieben - beim Sprung auf den modularen
Stand zum Beispiel von logo.svg auf die mitgelieferte logo.png.

Unter Einstellungen > Logo laesst sich jetzt ein eigenes Logo hochladen.
Es erscheint in der Seitenleiste, auf der Login-Seite und auf der
Zentrale-Seite, mit Vorschau auf hellem und dunklem Grund.

- Datei liegt unter static/custom/ und ist per .gitignore von der
  Versionierung ausgenommen, ein git pull ueberschreibt sie also nicht
- PNG, JPG, SVG, WEBP, GIF bis 2 MB; Format und Groesse werden geprueft
- es bleibt immer genau eine Datei liegen, alte Endungen werden entfernt
- fehlt die Datei, greift automatisch das mitgelieferte Logo
- Upload und Zuruecksetzen liegen hinter dem Admin-Login
- logo_url() ist als Jinja-Global registriert, damit auch die Seiten ohne
  _ctx() (Login, Zentrale) dasselbe Logo zeigen

.gitignore ergaenzt: static/custom/ sowie die bisher fehlenden
busylight.db-shm, busylight.db-wal und Status.csv - letztere lagen auf
dem Produktivsystem als untracked herum.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 08:55:16 +00:00
co-authored by Claude Opus 5
parent b02e6702e4
commit 09d9837dec
8 changed files with 178 additions and 3 deletions
+77
View File
@@ -211,6 +211,83 @@
</button>
</div>
</form>
<!-- Logo: eigener Abschnitt, weil ein Datei-Upload nicht in das obige Formular passt -->
<div class="row g-4 mt-1">
<div class="col-lg-6">
<div class="card">
<div class="card-header">
<h6 class="mb-0"><i class="bi bi-image"></i> Logo</h6>
</div>
<div class="card-body">
{% if request.query_params.get('logo_saved') %}
<div class="alert alert-success py-2"><small><i class="bi bi-check-circle"></i> Logo gespeichert.</small></div>
{% elif request.query_params.get('logo_reset') %}
<div class="alert alert-success py-2"><small><i class="bi bi-check-circle"></i> Standardlogo wiederhergestellt.</small></div>
{% elif request.query_params.get('logo_error') == 'format' %}
<div class="alert alert-danger py-2"><small><i class="bi bi-x-circle"></i> Nicht unterstuetztes Format. Erlaubt: PNG, JPG, SVG, WEBP, GIF.</small></div>
{% elif request.query_params.get('logo_error') == 'size' %}
<div class="alert alert-danger py-2"><small><i class="bi bi-x-circle"></i> Datei ist zu gross (maximal 2 MB).</small></div>
{% elif request.query_params.get('logo_error') == 'empty' %}
<div class="alert alert-danger py-2"><small><i class="bi bi-x-circle"></i> Die Datei war leer.</small></div>
{% endif %}
<div class="mb-3">
<label class="form-label">Aktuelles Logo</label>
<div class="d-flex gap-2">
<div class="border rounded p-3 bg-white text-center flex-fill">
<img src="{{ logo_url() }}" alt="Logo" style="max-height:40px; max-width:100%;">
<div><small class="text-muted">auf hell</small></div>
</div>
<div class="border rounded p-3 bg-dark text-center flex-fill">
<img src="{{ logo_url() }}" alt="Logo" style="max-height:40px; max-width:100%;">
<div><small class="text-secondary">auf dunkel (Seitenleiste)</small></div>
</div>
</div>
<small class="text-muted">
{% if settings.logo_file %}
Kundenlogo aktiv: <code>{{ settings.logo_file }}</code>
{% else %}
Standardlogo aus dem Repository
{% endif %}
</small>
</div>
<form method="post" action="/api/settings/logo" enctype="multipart/form-data" class="mb-2">
<label class="form-label">Eigenes Logo hochladen</label>
<div class="input-group">
<input type="file" name="logo" class="form-control"
accept=".png,.jpg,.jpeg,.svg,.webp,.gif" required>
<button type="submit" class="btn btn-primary">
<i class="bi bi-upload"></i> Hochladen
</button>
</div>
<small class="text-muted">
PNG, JPG, SVG, WEBP oder GIF, maximal 2 MB. Wird in der Seitenleiste, auf der
Login-Seite und auf der Zentrale-Seite angezeigt. Eine Hoehe von etwa 40 Pixeln
wird dargestellt - die Seitenleiste ist dunkel, ein Logo mit heller Schrift oder
transparentem Hintergrund wirkt dort am besten.
</small>
</form>
{% if settings.logo_file %}
<form method="post" action="/api/settings/logo/reset">
<button type="submit" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-counterclockwise"></i> Zurueck zum Standardlogo
</button>
</form>
{% endif %}
<div class="border rounded p-3 bg-light mt-3">
<small class="text-muted">
Das Logo liegt unter <code>static/custom/</code> und ist von der Versionierung
ausgenommen. Ein <code>git pull</code> ueberschreibt es also nicht.
</small>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}