Files
signage/templates/help.html
Erik Thiele 848bcf93ab feat: Willkommensseite auf mehreren Screens anzeigen
Die Willkommensseite ging bisher fest in die Lobby-Playlist. Welche
Screens Begruessungsschirme sind, ist aber eine Eigenschaft der
Installation und nicht des einzelnen Kundenbesuchs - deshalb ein Flag
pro Screen statt einer Auswahl bei jedem Vorgang.

- Neue Screen-Einstellung "welcome_enabled" (Checkbox im Tab
  Einstellungen). Die generierte welcome.html wird in jeden markierten
  Screen des Standorts geschrieben und dort an Position 0 gesetzt.
- welcome_target_screens() faellt nur auf "lobby" zurueck, wenn kein
  Screen des Standorts den Schluessel besitzt. Bestehende Standorte
  verhalten sich damit ohne Konfigurationsaufwand unveraendert.
- add_customer_to_playlists() (frueher add_customer_to_lobby_playlist)
  raeumt welcome.html aus allen Screens des Standorts und fuegt sie nur
  in den Zielscreens wieder ein. Ein abgewaehlter Screen bleibt so nicht
  mit einer alten Begruessung stehen.
- Eingefuegt wird ein Dict statt eines Strings, damit der Eintrag
  enabled und schedule tragen kann.
- Optionales "Anzeigen bis" im Formular: setzt eine Zeitsteuerung, die
  Seite verschwindet danach auf allen Screens von selbst.
- Das Formular zeigt die aktuellen Zielscreens an und meldet
  verstaendlich, wenn keiner markiert ist.
- screens.setdefault("lobby", {}) entfernt - das legte an Standorten
  ohne Lobby stillschweigend einen Screen an. Es werden nur noch
  vorhandene Screens bespielt.

Die Seite referenziert alle Bilder absolut (/static/..., /media/...),
ist also ortsunabhaengig; je Screen liegt eine identische Kopie.

README, AGENTS.md und die Hilfeseite nachgezogen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 12:26:22 +02:00

348 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>CANCOM Simple Signage – Hilfe</title>
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
<script>
(() => {
const stored = window.localStorage.getItem("signage-theme");
const theme = stored === "dark" || stored === "light" ? stored : "light";
document.documentElement.setAttribute("data-bs-theme", theme);
})();
</script>
<link rel="stylesheet"
href="https://unpkg.com/@tabler/core@1.0.0-beta20/dist/css/tabler.min.css">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css">
<script defer
src="https://unpkg.com/@tabler/core@1.0.0-beta20/dist/js/tabler.min.js"></script>
{% include "_styles.html" %}
</head>
<body>
<div class="page">
{% set brand_subtitle = "Hilfe" %}
{% set site_dropdown_url_prefix = "/admin/" %}
{% include "_header.html" %}
<div class="navbar-expand-md">
<div class="collapse navbar-collapse show">
<div class="navbar navbar-dark nav-surface">
<div class="container-xl">
<div class="d-flex flex-wrap gap-2 py-2">
<a class="btn btn-white" href="/admin/{{ current_site }}"><i class="ti ti-arrow-left me-1"></i>Zurück</a>
<a class="btn btn-white active" href="/admin/help"><i class="ti ti-help me-1"></i>Hilfe</a>
</div>
</div>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="container-xl mt-4">
<div class="row g-4">
<!-- Sidebar Navigation -->
<div class="col-md-3">
<div class="card" style="position:sticky;top:1rem;">
<div class="card-header"><h4 class="card-title" style="font-size:0.9rem;"><i class="ti ti-menu-2 me-1"></i>Themen</h4></div>
<div class="card-body p-2">
<nav class="nav flex-column">
<a class="nav-link py-1 px-2 small" href="#help-standort"><i class="ti ti-building-community me-1"></i>Standort</a>
<a class="nav-link py-1 px-2 small" href="#help-screen"><i class="ti ti-devices me-1"></i>Screen</a>
<a class="nav-link py-1 px-2 small" href="#help-playlist"><i class="ti ti-list me-1"></i>Playlist</a>
<a class="nav-link py-1 px-2 small" href="#help-zeitsteuerung"><i class="ti ti-clock me-1"></i>Zeitsteuerung</a>
<a class="nav-link py-1 px-2 small" href="#help-priority"><i class="ti ti-player-play me-1"></i>Priority</a>
<a class="nav-link py-1 px-2 small" href="#help-actions"><i class="ti ti-external-link me-1"></i>Action Button</a>
<a class="nav-link py-1 px-2 small" href="#help-voice"><i class="ti ti-robot me-1"></i>Voice Agent</a>
<a class="nav-link py-1 px-2 small" href="#help-einstellungen"><i class="ti ti-settings me-1"></i>Einstellungen</a>
<a class="nav-link py-1 px-2 small" href="#help-info"><i class="ti ti-info-circle me-1"></i>Info-Tab</a>
<a class="nav-link py-1 px-2 small" href="#help-willkommen"><i class="ti ti-users me-1"></i>Willkommen</a>
<a class="nav-link py-1 px-2 small" href="#help-userverwaltung"><i class="ti ti-shield me-1"></i>User</a>
<a class="nav-link py-1 px-2 small" href="#help-dashboard"><i class="ti ti-dashboard me-1"></i>Dashboard</a>
<a class="nav-link py-1 px-2 small" href="#help-darkmode"><i class="ti ti-moon me-1"></i>Dark Mode</a>
<a class="nav-link py-1 px-2 small" href="#help-passwort"><i class="ti ti-key me-1"></i>Passwort</a>
<a class="nav-link py-1 px-2 small" href="#help-mfa"><i class="ti ti-shield-lock me-1"></i>MFA</a>
</nav>
</div>
</div>
</div>
<!-- Main Content -->
<div class="col-md-9">
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-standort"><i class="ti ti-building-community me-1"></i>Standort anlegen</h3></div>
<div class="card-body">
<p>Ein Standort (Site) gruppiert mehrere Screens, z.&thinsp;B. <code>stuttgart</code> oder <code>karlsruhe</code>.</p>
<ol>
<li><strong>+ Button</strong> im Header (neben dem Standort-Dropdown) klicken</li>
<li>Namen des neuen Standorts eingeben (z.&thinsp;B. <code>berlin</code>)</li>
<li>Der Standort wird sofort angelegt und im Dropdown sichtbar</li>
</ol>
<p class="text-muted small mb-0"><i class="ti ti-info-circle me-1"></i>Nur Admins können Standorte anlegen oder löschen (Löschen über den Button im Standort-Dashboard).</p>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-screen"><i class="ti ti-devices me-1"></i>Screen hinzufügen</h3></div>
<div class="card-body">
<p>Ein Screen ist ein einzelner Player (z.&thinsp;B. <code>lobby</code>, <code>casino</code>), der eine Playlist abspielt.</p>
<ol>
<li>Im Admin-Bereich des Standorts (<code>/admin/&lt;standort&gt;</code>) das <strong>+</strong>-Symbol oben rechts klicken</li>
<li>Namen des Screens eingeben (z.&thinsp;B. <code>lobby</code>)</li>
<li>Der Screen erscheint als neue Card mit Tabs: Playlist, Einstellungen, Aktionen, Digital Voice Agent, Medien</li>
</ol>
<p class="text-muted small mb-0"><i class="ti ti-info-circle me-1"></i>Der Screen-Name wird automatisch in Kleinbuchstaben umgewandelt.</p>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-playlist"><i class="ti ti-list me-1"></i>Playlist verwalten</h3></div>
<div class="card-body">
<p>Jeder Screen hat eine eigene Playlist mit Medien und URLs.</p>
<ul>
<li><strong>Medien hochladen:</strong> Im Tab "Medien" eine Datei auswählen und hochladen. Unterstützt werden Bilder (JPG, PNG) und Videos (MP4).</li>
<li><strong>URL hinzufügen:</strong> Im Tab "Playlist" eine URL eingeben und optional den Zoom-Faktor anpassen.</li>
<li><strong>Sortieren:</strong> Per Drag & Drop die Reihenfolge der Playlist-Einträge ändern (Ziehgriff links).</li>
<li><strong>Deaktivieren:</strong> Mit der Checkbox einzelne Einträge temporär ausblenden.</li>
<li><strong>Löschen:</strong> Über den roten Papierkorb-Button.</li>
<li><strong>Bearbeiten:</strong> Der <em>Edit</em>-Button öffnet einen Dialog. Bei URLs lassen sich dort Adresse, Zoom, Query-Parameter-Passthrough und Zeitsteuerung ändern; bei Bildern, Videos und HTML-Seiten die Zeitsteuerung.</li>
<li><strong>Zeitsteuerung:</strong> Einträge können nur zu bestimmten Zeiten laufen – siehe <a href="#help-zeitsteuerung">Zeitsteuerung</a>. Ein Badge <span class="badge bg-info text-white">Zeit</span> in der Playlist-Zeile zeigt an, dass ein Eintrag zeitgesteuert ist; der Tooltip nennt die Einstellung im Klartext.</li>
<li><strong>Query-Parameter-Passthrough:</strong> Per URL-Edit-Button (Stift-Icon) im Playlist-Eintrag kann pro URL gesteuert werden, ob Query-Parameter von der Player-URL an die Playlist-URL übergeben werden. Standardmäßig <strong>aus</strong> – Checkbox "Query-Parameter an diese URL übergeben" aktivieren wenn gewünscht. Ein Link-Icon in der Playlist-Zeile zeigt den Status: <i class="ti ti-link"></i> = an, <i class="ti ti-link-off"></i> = aus.</li>
</ul>
<p class="text-muted small mb-0"><i class="ti ti-info-circle me-1"></i>Änderungen an der Playlist werden automatisch gespeichert. Der Player lädt die Seite automatisch neu (via Hash-Prüfung).</p>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-zeitsteuerung"><i class="ti ti-clock me-1"></i>Zeitsteuerung</h3></div>
<div class="card-body">
<p>Jeder Playlist-Eintrag – URL, Bild, Video oder HTML-Seite – kann nur zu bestimmten Zeiten laufen. Das gilt für die Playlist eines Screens genauso wie für die Priority-Playlist.</p>
<p><strong>Einstellen:</strong> beim Anlegen im Tab "Medien" unter <em>Zeitsteuerung</em>, oder nachträglich über den <em>Edit</em>-Button des Eintrags. Häkchen bei "Zeitgesteuerte Wiedergabe" setzen und einen Modus wählen:</p>
<table class="table table-sm">
<thead>
<tr><th style="width:110px">Modus</th><th>Bedeutung</th><th>Felder</th></tr>
</thead>
<tbody>
<tr>
<td><strong>Einmalig</strong></td>
<td>Läuft einmal in einem zusammenhängenden Zeitraum – auch über mehrere Tage am Stück.</td>
<td>Start und Ende jeweils mit Datum <em>und</em> Uhrzeit</td>
</tr>
<tr>
<td><strong>Täglich</strong></td>
<td>Läuft jeden Tag im selben Zeitfenster.</td>
<td>Von / Bis als Uhrzeit, optional "Gültig von / bis"</td>
</tr>
<tr>
<td><strong>Wöchentlich</strong></td>
<td>Wie täglich, aber nur an den angehakten Wochentagen.</td>
<td>Von / Bis als Uhrzeit, Wochentage, optional "Gültig von / bis"</td>
</tr>
</tbody>
</table>
<p><strong>Gültig von / bis</strong> ist optional und begrenzt, an welchen <em>Tagen</em> das Zeitfenster überhaupt greift. Beide Grenzen lassen sich einzeln setzen und zählen den genannten Tag mit. Ohne Angabe läuft die Wiedergabe unbefristet.</p>
<p class="mb-1"><strong>Beispiele:</strong></p>
<ul>
<li>Täglich, 08:00 bis 12:00, ohne Gültigkeitszeitraum → jeden Vormittag, unbefristet.</li>
<li>Täglich, 08:00 bis 12:00, gültig vom 10.09. bis 20.09. → nur an diesen elf Tagen.</li>
<li>Wöchentlich, 09:00 bis 17:00, Mo/Mi/Fr → nur an diesen drei Tagen während der Bürozeit.</li>
<li>Täglich, 22:00 bis 02:00 → abends bis in die Nacht hinein, über Mitternacht hinweg.</li>
</ul>
<p class="mb-1"><strong>Gut zu wissen:</strong></p>
<ul>
<li>Bei "Täglich" und "Wöchentlich" zählt nur die <em>Uhrzeit</em> – ein Datum wird ausschliesslich unter "Gültig von / bis" angegeben.</li>
<li>Bei "Täglich" gilt automatisch jeder Wochentag; die Auswahl der Wochentage erscheint deshalb nur bei "Wöchentlich".</li>
<li>Ein Fenster über Mitternacht gehört zum Tag seines <em>Starts</em>: "Mi 22:00–02:00" läuft Mittwochabend bis Donnerstagfrüh.</li>
<li>Die Checkbox links im Playlist-Eintrag wirkt weiterhin unabhängig davon – ein deaktivierter Eintrag läuft auch innerhalb seines Zeitfensters nicht.</li>
<li>Der Player merkt von selbst, wenn ein Zeitfenster beginnt oder endet, und lädt neu. Ein manueller Eingriff am Screen ist nicht nötig.</li>
</ul>
<p class="text-muted small mb-0"><i class="ti ti-info-circle me-1"></i>Es gilt die Uhrzeit des Servers, nicht die des Players.</p>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-priority"><i class="ti ti-player-play me-1"></i>Priority-Playlist</h3></div>
<div class="card-body">
<p>Die Priority-Playlist ist <strong>global</strong> und wird auf <em>allen</em> Playern eines Standorts eingeblendet – zwischen den normalen Playlist-Einträgen.</p>
<ul>
<li>Aufruf über <code>/admin/&lt;standort&gt;/priority</code></li>
<li>Globale Medien und URLs verwalten, ebenfalls per Drag & Drop</li>
<li>Priority muss in der Konfiguration aktiviert sein (<code>config.json → priority.enabled</code>)</li>
<li>Die <a href="#help-zeitsteuerung">Zeitsteuerung</a> steht hier genauso zur Verfügung wie in den Screen-Playlists</li>
</ul>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-actions"><i class="ti ti-external-link me-1"></i>Action Button (Custom-URL)</h3></div>
<div class="card-body">
<p>Der Action Button ist ein konfigurierbarer Button im Player, der eine frei wählbare URL öffnet.</p>
<ul>
<li><strong>Konfiguration:</strong> Im Tab <strong>Aktionen</strong> der Screen-Card</li>
<li><strong>URL:</strong> Beliebige Webadresse (z.&thinsp;B. Intranet-Seite, Dashboard)</li>
<li><strong>Label:</strong> Text, der auf dem Button angezeigt wird (z.&thinsp;B. "Infos")</li>
<li><strong>Position:</strong> 9 mögliche Positionen (oben/mitte/unten × links/mitte/rechts)</li>
<li><strong>Ziel:</strong>
<ul>
<li><code>overlay</code> – URL wird in einem iframe-Overlay geöffnet, Zurück-Button schließt es</li>
<li><code>redirect</code> – direkte Weiterleitung zur URL</li>
</ul>
</li>
</ul>
<p class="text-muted small mb-0"><i class="ti ti-info-circle me-1"></i>Der Button erscheint nur, wenn "Aktiviert" eingeschaltet ist. Bei Positionen unten weicht er automatisch 16px über dem Newsticker aus.</p>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-voice"><i class="ti ti-robot me-1"></i>Digital Voice Agent</h3></div>
<div class="card-body">
<p>Der Digital Voice Agent zeigt einen Button im Player, der eine Typewriter-Animation mit wechselnden mehrsprachigen Texten anzeigt.</p>
<ul>
<li><strong>Globale URL:</strong> Die Ziel-URL wird in <code>config.json</code> (Feld <code>voice_agent_url</code>) gesetzt und ist im Admin sichtbar, aber nicht änderbar</li>
<li><strong>Konfiguration pro Screen:</strong> Im Tab <strong>Digital Voice Agent</strong> der Screen-Card</li>
<li><strong>Label:</strong> Button-Beschriftung (Default: "Digitaler Assistent")</li>
<li><strong>Position:</strong> Wie Action Button – 9 Positionen wählbar</li>
<li><strong>Bild:</strong> Optional kann über dem Button ein Bild (dva.png) angezeigt werden (Beta-Feature)</li>
<li><strong>Typewriter-Tagline:</strong> 11 Phrasen in 5 Sprachen wechseln alle 2,5 Sekunden mit Buchstaben-für-Buchstaben-Effekt</li>
<li><strong>Animierte Balken:</strong> 5 rote Audiobalken neben dem Button signalisieren Aktivität</li>
</ul>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-einstellungen"><i class="ti ti-settings me-1"></i>Screen-Einstellungen</h3></div>
<div class="card-body">
<p>Im Tab <strong>Einstellungen</strong> können pro Screen konfiguriert werden:</p>
<ul>
<li><strong>Intervall:</strong> Anzeigedauer pro Playlist-Eintrag in Sekunden</li>
<li><strong>Bilder / Videos anzeigen:</strong> Jeweils ein-/ausschaltbar</li>
<li><strong>Newsticker:</strong> Text (max. 200 Zeichen) und Aktivierung des Laufbands</li>
<li><strong>Stay on First:</strong> Wenn aktiviert, bleibt der Player auf dem ersten Playlist-Element stehen (kein Durchlauf)</li>
</ul>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-info"><i class="ti ti-info-circle me-1"></i>Info-Tab im Screen</h3></div>
<div class="card-body">
<p>Jeder Screen hat einen <strong>Info-Tab</strong> (Tab 6), der detaillierte Client-Informationen zum aktuell verbundenen Player anzeigt:</p>
<ul>
<li><strong>IP-Adresse</strong> des Players</li>
<li><strong>Browser</strong> (User-Agent) des Players</li>
<li><strong>Bildschirmauflösung</strong> (Breite × Höhe)</li>
<li><strong>Zuletzt gesehen</strong> – Zeitstempel des letzten Heartbeats</li>
</ul>
<p class="text-muted small mb-0"><i class="ti ti-info-circle me-1"></i>Die Daten werden alle 5 Sekunden vom Player gesendet und im Heartbeat-JSON gespeichert. Der Online-Status (grüner/roter Button im Screen-Header) basiert auf dem gleichen Mechanismus.</p>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-willkommen"><i class="ti ti-users me-1"></i>Willkommensseite</h3></div>
<div class="card-body">
<p>Auf der Willkommensseite können bis zu 3 Kundennamen eingegeben werden. Die Logos werden automatisch via OpenAI + Brandfetch gesucht.</p>
<ul>
<li>Aufruf über <code>/willkommen?site=&lt;standort&gt;</code> (oder Menüpunkt)</li>
<li><strong>Zielscreens:</strong> Die generierte <code>welcome.html</code> wird automatisch an den Anfang der Playlist <em>jedes</em> Screens gesetzt, bei dem im Tab "Einstellungen" die Option <em>Willkommensseite hier anzeigen</em> aktiv ist. So lässt sich dieselbe Begrüßung auf mehreren Screens zeigen, ohne sie mehrfach anzulegen. Ist an einem Standort nichts eingestellt, geht sie wie bisher in die Lobby.</li>
<li>Wird ein Screen später abgewählt, verschwindet die Willkommensseite dort beim nächsten Generieren automatisch.</li>
<li><strong>Anzeigen bis:</strong> Optionales Datum im Formular. Ist es gesetzt, verschwindet die Willkommensseite nach diesem Tag auf allen Screens von selbst – kein manuelles Aufräumen nach dem Besuch. Ohne Angabe bleibt sie stehen.</li>
<li>Pro Standort kann ein eigenes Hintergrundbild hochgeladen werden (Tab "Medien" im Standort-Dashboard)</li>
<li>Pro Kunde können <strong>Ansprechpartner</strong> eingegeben werden (Markdown-Format):
<ul>
<li><strong>Fett</strong>: <code>**Text**</code></li>
<li><em>Kursiv</em>: <code>*Text*</code></li>
<li><code>Code</code>: <code>`Code`</code></li>
<li>Aufzählungen: <code>- Punkt</code> oder <code>* Punkt</code></li>
<li>Zeilenumbrüche werden automatisch übernommen</li>
</ul>
</li>
<li><strong>Layout:</strong>
<ul>
<li>Landscape: Logos nebeneinander, Texte linksbündig darunter</li>
<li>Portrait: Texte <em>links</em> vom Logo, Abstand 40px</li>
</ul>
</li>
<li>Reset-Button setzt das Formular und die gespeicherten Daten zurück</li>
</ul>
</div>
</div>
{% if current_user.is_admin %}
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-userverwaltung"><i class="ti ti-shield me-1"></i>User-Verwaltung (Admin)</h3></div>
<div class="card-body">
<p>Admins können über <code>/admin/users</code> die User-Verwaltung aufrufen.</p>
<ul>
<li><strong>User anlegen:</strong> E-Mail, Passwort, Rolle (Admin/Superuser/User) und Standort-Zugriff festlegen</li>
<li><strong>User bearbeiten:</strong> E-Mail, Rolle, Standorte, Profilfelder (Vorname, Nachname, Abteilung) ändern</li>
<li><strong>User löschen:</strong> Der letzte Admin kann nicht gelöscht werden</li>
<li><strong>Passwort-Reset:</strong> Setzt ein temporäres Passwort, der User muss es beim nächsten Login ändern</li>
<li><strong>Rollen:</strong>
<ul>
<li><strong>Admin</strong> – Zugriff auf alle Standorte + Userverwaltung</li>
<li><strong>Superuser</strong> – Zugriff auf alle Standorte, keine Userverwaltung</li>
<li><strong>User</strong> – Zugriff nur auf zugewiesene Standorte</li>
</ul>
</li>
</ul>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-dashboard"><i class="ti ti-dashboard me-1"></i>Admin Dashboard</h3></div>
<div class="card-body">
<p>Das Admin Dashboard (<code>/admin/dashboard</code>) bietet eine Übersicht über das gesamte System:</p>
<ul>
<li><strong>Statistik-Cards:</strong> Anzahl Standorte, Screens, User, Admins und Superuser</li>
<li><strong>Sparkline:</strong> Kleine Liniendiagramme zeigen den Verlauf der letzten Änderungen</li>
<li><strong>Trend:</strong> Pfeil nach oben/unten mit Netto-Änderung aus den letzten 30 Aktionen</li>
<li><strong>Aktivitätsverlauf:</strong> Alle Erstell-, Lösch-, Änderungsaktionen sowie An-/Abmeldungen werden protokolliert</li>
</ul>
</div>
</div>
{% endif %}
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-darkmode"><i class="ti ti-moon me-1"></i>Dark Mode</h3></div>
<div class="card-body">
<p>Der Dark Mode kann über den Mond-Button im Header umgeschaltet werden. Die Einstellung wird im Browser gespeichert (localStorage) und bleibt auch nach Seitenwechsel erhalten.</p>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-passwort"><i class="ti ti-key me-1"></i>Passwort ändern</h3></div>
<div class="card-body">
<p>Das Passwort kann über das User-Menü (oben rechts) → <strong>Passwort ändern</strong> geändert werden. Nach einem Admin-Reset muss der User beim ersten Login ein neues Passwort vergeben.</p>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title" id="help-mfa"><i class="ti ti-shield-lock me-1"></i>MFA (Multi-Faktor-Authentifizierung)</h3></div>
<div class="card-body">
<p>MFA bietet eine optionale zweite Sicherheitsstufe per TOTP (Google Authenticator, Authy).</p>
<ul>
<li><strong>Aktivierung:</strong> Über das User-Menü → <strong>MFA einrichten</strong> → QR-Code mit einer Authenticator-App scannen → 6-stelligen Code eingeben → Bestätigen</li>
<li><strong>Recovery-Codes:</strong> Nach erfolgreicher Aktivierung werden 8 Recovery-Codes (einmalig) angezeigt. Jeder Code kann nur einmal verwendet werden. Nach Einsatz eines Recovery-Codes muss MFA neu eingerichtet werden.</li>
<li><strong>Login mit MFA:</strong> Nach Eingabe von E-Mail + Passwort erscheint ein zweites Formular für den TOTP-Code. Erst nach erfolgreichem Code wird der Zugriff gewährt.</li>
<li><strong>Deaktivierung:</strong> Über <code>/mfa/setup</code> → Button "MFA deaktivieren" → aktuelles Passwort eingeben</li>
<li><strong>MFA-Status:</strong> In der Userliste (<code>/admin/users</code>) wird der MFA-Status als grünes "Aktiv"-Badge oder "–" angezeigt.</li>
</ul>
</div>
</div>
</div> <!-- /col-md-9 main -->
</div> <!-- /row -->
</div> <!-- /container-xl -->
</div> <!-- /page-wrapper -->
{% include "_footer.html" %}
</div> <!-- /page -->
</body>
</html>