Kopfleiste: Benutzer-Menü und gemeinsames Abmelden

- Benutzername und "Abmelden" im eigenen Header entfallen, nur noch
  Rückfall, wenn topbar.js nicht lädt
- Sitzungsanzeige als slot="benutzer" im Benutzer-Menü der Leiste
- hi-abmelden: POST /api/logout beendet die Timer-Sitzung, laufende
  Timer fragen vorher nach (Abbrechen stoppt das ganze Abmelden)
- html data-theme="dark": die Leiste ist dunkel wie die App, auch bei
  hellem System
- README/DEPLOY: Abmelden, Launch-URL /auth/login, CSP-Hinweis

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kerim AtilganandClaude Opus 5.5 committed 2026-10-25 13:12:20 +01:00
1 parent 8f5517a409
commit 0ee9b708a1
5 files changed
+94 -14

No files matched your search

+50 -7
View File
@@ -161,28 +161,71 @@ async function enterApp() {
startLoops();
}
// Zeigt den angemeldeten Authentik-Nutzer oben rechts im Header.
// Zeigt den angemeldeten Authentik-Nutzer: im Benutzer-Menue der
// Portal-Kopfleiste, ohne Leiste oben rechts im eigenen Header.
function renderCurrentUser() {
const el = document.getElementById('currentUser');
if (!el) return;
const u = loggedInUser;
el.textContent = u ? (u.displayName || u.username || '') : '';
const name = u ? (u.displayName || u.username || '') : '';
const el = document.getElementById('currentUser');
el.textContent = name;
el.title = u && u.email ? u.email : 'Angemeldet';
renderTopbarSession(name);
}
async function logout() {
function confirmLogout() {
const running = state.timers.filter(t => t.isRunning).length;
if (running > 0 && !confirm(
return running === 0 || confirm(
`${running} Timer ${running === 1 ? 'läuft' : 'laufen'} noch.\n\n` +
`Sie laufen serverseitig weiter und sind nach dem nächsten Anmelden wieder da. Trotzdem abmelden?`
)) return;
);
}
// Eigener Abmelden-Knopf, nur sichtbar ohne Portal-Kopfleiste
async function logout() {
if (!confirmLogout()) return;
stopLoops();
state.timers = [];
// GET-Navigation: beendet lokale Sitzung und – bei Authentik-Login – auch die SSO-Sitzung
window.location.href = '/auth/logout';
}
// ---------------------------------------------------------------------------
// Portal-Kopfleiste
// ---------------------------------------------------------------------------
// topbar.js laeuft per defer erst nach diesem Skript, die Leiste steht also
// fruehestens ab DOMContentLoaded im DOM. Die Sitzungsanzeige wird einmal
// eingehaengt, sobald die Anmeldung steht, danach aendert sich nur ihr Text.
const topbarSession = document.createElement('div');
topbarSession.slot = 'benutzer';
topbarSession.className = 'topbar-session';
topbarSession.append(Object.assign(document.createElement('span'), { textContent: 'Timer – angemeldet als' }),
document.createElement('strong'));
let topbarSessionName = null; // null = (noch) nicht angemeldet
function renderTopbarSession(name) {
topbarSessionName = name || 'Angemeldet';
topbarSession.lastChild.textContent = topbarSessionName;
if (!topbarSession.isConnected) document.querySelector('hi-topbar')?.append(topbarSession);
}
document.addEventListener('DOMContentLoaded', () => {
if (topbarSessionName !== null) renderTopbarSession(topbarSessionName);
});
// "Abmelden" in der Leiste: erst die eigene Sitzung beenden, danach meldet
// die Leiste bei Portal und Authentik ab. Abbrechen bei laufenden Timern
// verhindert das ganze Abmelden.
document.addEventListener('hi-abmelden', (e) => {
if (!confirmLogout()) {
e.preventDefault();
return;
}
stopLoops();
state.timers = [];
e.detail.warten(fetch('/api/logout', { method: 'POST', credentials: 'same-origin' }));
});
// ---------------------------------------------------------------------------
// Timer-Synchronisation
// ---------------------------------------------------------------------------