/**
* 🔐 AUTHENTIFIZIERUNG & ZUGANGSSCHUTZ (AUTH GUARD) 🔐
* =======================================================
* IT-Praxisportal – Benutzer-Authentifizierung, Wartungsmodus und Zugangsschutz.
* - Strikter Login-Zwang für den Alpha-Test (sessionStorage als Standard).
* - Vollständiger Wartungsmodus mit Fullscreen-Blockade für Nicht-Admins.
* - Live-Statusabfrage (/api/platform/status) & Ankündigungsbanner.
* - Vollständige Beseitigung unsicherer Offline-/Dummy-Fallbacks.
*/
(function () {
'use strict';
function escapeHtml(text) {
if (!text) return '';
return String(text)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// Session-Speicher auslesen (sessionStorage bevorzugt für Sitzungsbindung im Test)
function getStoredToken() {
return sessionStorage.getItem('auth_token') || localStorage.getItem('auth_token') || null;
}
function getStoredUser() {
try {
const raw = sessionStorage.getItem('auth_user') || localStorage.getItem('auth_user') || 'null';
return JSON.parse(raw);
} catch (e) {
return null;
}
}
// Bereinigung veralteter oder gefälschter Dummy-Tokens
const initialToken = getStoredToken();
const initialUser = getStoredUser();
if ((initialToken && initialToken.startsWith('local_token_')) || (initialUser && initialUser.isGuest)) {
sessionStorage.removeItem('auth_token');
sessionStorage.removeItem('auth_user');
localStorage.removeItem('auth_token');
localStorage.removeItem('auth_user');
}
window.AUTH = {
token: getStoredToken(),
user: getStoredUser(),
platformStatus: { maintenance: false, announcement: '', allow_registration: true },
getToken() {
if (!this.token) {
this.token = getStoredToken();
}
return this.token;
},
getUser() {
if (!this.user) {
this.user = getStoredUser();
}
return this.user;
},
isLoggedIn() {
const token = this.getToken();
const user = this.getUser();
return !!(token && user && !user.isGuest && user.email);
},
isAdmin() {
const user = this.getUser();
return !!(this.isLoggedIn() && user && user.role === 'admin');
},
isTeacher() {
const user = this.getUser();
return !!(this.isLoggedIn() && user && (user.role === 'teacher' || user.role === 'admin'));
},
setSession(token, user, rememberMe = true) {
this.token = token;
this.user = user;
try {
localStorage.setItem('auth_token', token);
localStorage.setItem('auth_user', JSON.stringify(user));
sessionStorage.setItem('auth_token', token);
sessionStorage.setItem('auth_user', JSON.stringify(user));
} catch (e) {}
this.updateUI();
this.handleMaintenanceDisplay();
},
logout(redirect = true) {
this.token = null;
this.user = null;
sessionStorage.removeItem('auth_token');
sessionStorage.removeItem('auth_user');
localStorage.removeItem('auth_token');
localStorage.removeItem('auth_user');
this.updateUI();
if (redirect) {
const p = window.location.pathname;
if (p.includes('/courses/') || p.includes('/lehrpfad_') || p.endsWith('workspace.html') || p.endsWith('dashboard.html') || p.endsWith('teacher.html') || p.endsWith('admin.html')) {
window.location.href = window.location.origin + '/index.html?auth=login';
} else {
this.handleMaintenanceDisplay();
}
}
},
async register(name, email, password, role = 'solo', rememberMe = false, profession = '') {
try {
const res = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email, password, role, profession })
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
return { success: false, error: data.error || 'Registrierung fehlgeschlagen' };
}
this.setSession(data.token, data.user, rememberMe);
return { success: true, user: data.user };
} catch (err) {
return { success: false, error: 'Netzwerkfehler: Backend-Server nicht erreichbar.' };
}
},
async login(email, password, rememberMe = false) {
try {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
return { success: false, error: data.error || 'Anmeldung fehlgeschlagen' };
}
this.setSession(data.token, data.user, rememberMe);
return { success: true, user: data.user };
} catch (err) {
return { success: false, error: 'Netzwerkfehler: Backend-Server nicht erreichbar.' };
}
},
async fetchMe() {
if (!this.token) return;
try {
const res = await fetch('/api/auth/me', {
headers: { 'Authorization': `Bearer ${this.token}` }
});
if (res.ok) {
const data = await res.json();
this.user = data.user;
if (localStorage.getItem('auth_token')) {
localStorage.setItem('auth_user', JSON.stringify(data.user));
} else {
sessionStorage.setItem('auth_user', JSON.stringify(data.user));
}
this.updateUI();
this.handleMaintenanceDisplay();
} else {
const errData = await res.json().catch(() => ({}));
if (res.status === 401 || res.status === 404) {
this.logout(false);
} else if (res.status === 503 && errData.maintenance) {
this.handleMaintenanceDisplay();
}
}
} catch (e) {
// Offline
}
},
async checkStatus() {
try {
const res = await fetch('/api/platform/status');
if (res.ok) {
this.platformStatus = await res.json();
this.handleMaintenanceDisplay();
this.handleAnnouncementDisplay();
}
} catch (e) {
// Ignorieren
}
},
handleMaintenanceDisplay() {
const isMaintenance = this.platformStatus && this.platformStatus.maintenance;
const isAdmin = this.isAdmin();
let screen = document.getElementById('itp-maintenance-screen');
let adminBanner = document.getElementById('itp-maintenance-admin-banner');
if (isMaintenance) {
if (!isAdmin) {
if (!screen) {
screen = document.createElement('div');
screen.id = 'itp-maintenance-screen';
screen.style.cssText = `
position: fixed; inset: 0; z-index: 999999;
background: rgba(10, 15, 29, 0.96); backdrop-filter: blur(12px);
display: flex; align-items: center; justify-content: center;
padding: 24px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
color: #f8fafc; text-align: center;
`;
screen.innerHTML = `
🛠️
Wartungsmodus aktiv
Das IT-Praxisportal wird derzeit planmäßig gewartet, um Sicherheits-Updates und neue Lernmodule einzuspielen. Reguläre Kurszugänge sind vorübergehend pausiert.
🔐 Administrator-Anmeldung
IT-Praxisportal Alpha • Alle Daten sind sicher
`;
document.body.appendChild(screen);
const btn = document.getElementById('maintenance-admin-login-btn');
if (btn) {
btn.onclick = () => {
window.openAuthModal('login', {
allowClose: true,
title: '🔐 Administrator-Login',
message: 'Wartungsmodus aktiv: Der Zugang ist derzeit exklusiv für System-Administratoren freigeschaltet.',
onSuccess: () => {
window.location.reload();
}
});
};
}
}
} else {
if (screen) screen.remove();
if (!adminBanner) {
adminBanner = document.createElement('div');
adminBanner.id = 'itp-maintenance-admin-banner';
adminBanner.style.cssText = `
background: linear-gradient(90deg, #b91c1c, #ea580c);
color: white; padding: 8px 16px; font-size: 0.84rem; font-weight: 700;
display: flex; align-items: center; justify-content: space-between;
position: sticky; top: 0; z-index: 999998; box-shadow: 0 2px 8px rgba(0,0,0,0.3);
`;
adminBanner.innerHTML = `
⚠️ WARTUNGSMODUS AKTIV
| Nur Administratoren haben derzeit Zugriff auf die Plattform.
⚙️ Admin-Panel öffnen
`;
document.body.insertBefore(adminBanner, document.body.firstChild);
}
}
} else {
if (screen) screen.remove();
if (adminBanner) adminBanner.remove();
}
},
handleAnnouncementDisplay() {
const bannerText = this.platformStatus && this.platformStatus.announcement;
let el = document.getElementById('itp-announcement-banner');
if (!bannerText) {
if (el) el.remove();
return;
}
if (sessionStorage.getItem('dismissed_announcement') === bannerText) {
return;
}
if (!el) {
el = document.createElement('div');
el.id = 'itp-announcement-banner';
el.style.cssText = `
background: #0284c7; color: white; padding: 8px 16px; font-size: 0.85rem; font-weight: 600;
display: flex; align-items: center; justify-content: space-between;
position: sticky; top: 0; z-index: 999990;
`;
document.body.insertBefore(el, document.body.firstChild);
}
el.innerHTML = `
📢 ${escapeHtml(bannerText)}
✕
`;
el.querySelector('button').onclick = () => {
sessionStorage.setItem('dismissed_announcement', bannerText);
el.remove();
};
},
requireAuth(options = {}) {
if (this.isLoggedIn()) {
return true;
}
this.showAuthBarrier(options);
return false;
},
showAuthBarrier(options = {}) {
window.openAuthModal(options.mode || 'login', {
allowClose: options.allowClose !== undefined ? options.allowClose : false,
title: options.title || '🔒 Anmeldung erforderlich',
message: options.message || 'Dieser Lernbereich erfordert ein Benutzerkonto. Bitte melde dich an oder erstelle einen kostenlosen Account.',
onSuccess: () => {
if (options.onSuccess) options.onSuccess();
else window.location.reload();
}
});
},
updateUI() {
const loggedIn = this.isLoggedIn();
const user = this.getUser();
// Topbar Elemente in index.html & dashboard.html
const userSpan = document.getElementById('nav-auth-user');
const loginBtn = document.getElementById('nav-btn-login');
const registerBtn = document.getElementById('nav-btn-register');
const guestBtn = document.getElementById('nav-btn-guest');
const teacherLink = document.getElementById('nav-teacher-link');
const heroPrimaryCta = document.getElementById('hero-primary-cta');
// Modern User Avatar Dropdown Elemente
const userDropdown = document.getElementById('portal-user-dropdown');
const userAvatar = document.getElementById('portal-user-avatar');
const userNameEl = document.getElementById('portal-user-name');
const ddFullName = document.getElementById('portal-dropdown-user-fullname');
const ddEmail = document.getElementById('portal-dropdown-user-email');
const ddBadge = document.getElementById('portal-dropdown-user-badge');
const ddTeacher = document.getElementById('dropdown-link-teacher');
const ddAdmin = document.getElementById('dropdown-link-admin');
const ddMgmtTitle = document.getElementById('dropdown-mgmt-title');
const ddMgmtDivider = document.getElementById('dropdown-teacher-divider');
// Mobile Drawer User Header
const mobUserBox = document.getElementById('mobile-drawer-user-info');
const mobAvatar = document.getElementById('mobile-drawer-avatar');
const mobUsername = document.getElementById('mobile-drawer-username');
const mobRole = document.getElementById('mobile-drawer-role');
// Dashboard Links auf index.html / impressum / datenschutz
const navDashLink = document.getElementById('nav-dashboard-link');
const mobDashLink = document.getElementById('mobile-drawer-dash-link');
const landingBanner = document.getElementById('landing-logged-in-banner');
const landingUserName = document.getElementById('landing-user-name');
if (guestBtn) guestBtn.style.display = 'none'; // Gast-Button permanent ausblenden
function computeInitials(str) {
if (!str) return 'IT';
const parts = str.trim().split(/\s+/);
if (parts.length >= 2) {
return (parts[0][0] + parts[1][0]).toUpperCase();
}
return str.substring(0, 2).toUpperCase();
}
const roleLabels = {
'admin': 'Administrator',
'teacher': 'Dozent / Ausbilder',
'student': 'Auszubildender',
'solo': 'Professional'
};
if (loggedIn && user) {
document.body.classList.add('logged-in');
const displayName = user.name || user.email.split('@')[0];
const initials = computeInitials(displayName);
const roleLabel = roleLabels[user.role] || 'Teilnehmer';
const profStr = user.profession ? ` • ${user.profession}` : '';
// 1. User Dropdown befüllen
if (userDropdown) {
userDropdown.style.display = 'inline-block';
}
if (userAvatar) userAvatar.innerText = initials;
if (userNameEl) userNameEl.innerText = displayName;
if (ddFullName) ddFullName.innerText = displayName;
if (ddEmail) ddEmail.innerText = user.email || '';
if (ddBadge) {
ddBadge.innerText = `${roleLabel}${profStr}`;
}
// Mobile Drawer User Header
if (mobUserBox) mobUserBox.style.display = 'flex';
if (mobAvatar) mobAvatar.innerText = initials;
if (mobUsername) mobUsername.innerText = displayName;
if (mobRole) mobRole.innerText = `${roleLabel}${profStr}`;
// Dozent / Admin im Dropdown
const hasTeacher = (user.role === 'teacher' || user.role === 'admin');
const hasAdmin = (user.role === 'admin');
if (ddTeacher) ddTeacher.style.display = hasTeacher ? 'flex' : 'none';
if (ddAdmin) ddAdmin.style.display = hasAdmin ? 'flex' : 'none';
if (ddMgmtTitle) ddMgmtTitle.style.display = hasTeacher ? 'block' : 'none';
if (ddMgmtDivider) ddMgmtDivider.style.display = hasTeacher ? 'block' : 'none';
const mobMgmtTitle = document.getElementById('mobile-drawer-mgmt-title');
const mobMgmtDivider = document.getElementById('mobile-drawer-mgmt-divider');
if (mobMgmtTitle) mobMgmtTitle.style.display = hasTeacher ? 'block' : 'none';
if (mobMgmtDivider) mobMgmtDivider.style.display = hasTeacher ? 'block' : 'none';
if (userSpan) {
userSpan.style.display = 'inline-flex';
userSpan.innerHTML = `${escapeHtml(displayName)}`;
userSpan.style.cursor = 'pointer';
userSpan.title = 'Zum Kurs-Dashboard wechseln';
userSpan.onclick = () => { window.location.href = 'dashboard.html'; };
}
if (loginBtn) {
loginBtn.style.display = 'none'; // Bei aktivem User-Dropdown wird der separate Login-Btn ausgeblendet
}
if (registerBtn) {
registerBtn.style.display = 'none'; // Ebenso
}
if (heroPrimaryCta) {
heroPrimaryCta.innerHTML = 'Zum Dashboard & Kurse wählen → ';
heroPrimaryCta.removeAttribute('onclick');
heroPrimaryCta.onclick = (e) => {
e.preventDefault();
window.location.href = 'dashboard.html';
};
}
// Alle allgemeinen Landing-Page Action Buttons anpassen
document.querySelectorAll('.landing-cta-btn').forEach(btn => {
btn.innerHTML = 'Zum Dashboard & Kurse wählen → ';
btn.style.background = '#10b981';
btn.removeAttribute('onclick');
btn.onclick = (e) => {
e.preventDefault();
window.location.href = 'dashboard.html';
};
});
const footerLoginBtn = document.getElementById('footer-cta-login');
if (footerLoginBtn) footerLoginBtn.style.display = 'none';
// Banner und Dashboard-Links einblenden
if (landingBanner) {
landingBanner.style.display = 'flex';
if (landingUserName) {
landingUserName.innerText = displayName;
}
}
if (navDashLink) navDashLink.style.display = 'none';
if (mobDashLink) mobDashLink.style.display = 'flex';
const mobLogoutBtn = document.getElementById('mobile-drawer-logout-btn');
if (mobLogoutBtn) mobLogoutBtn.style.display = 'flex';
const mobDrawerAuthActions = document.getElementById('mobile-drawer-auth-actions');
if (mobDrawerAuthActions) mobDrawerAuthActions.style.display = 'none';
} else {
document.body.classList.remove('logged-in');
if (userDropdown) userDropdown.style.display = 'none';
if (mobUserBox) mobUserBox.style.display = 'none';
if (userSpan) {
userSpan.style.display = 'none';
userSpan.onclick = null;
}
if (loginBtn) {
loginBtn.style.display = 'inline-flex';
loginBtn.innerText = 'Anmelden';
loginBtn.style.background = 'rgba(255,255,255,0.12)';
loginBtn.style.color = 'white';
loginBtn.style.borderColor = 'rgba(255,255,255,0.2)';
loginBtn.onclick = () => window.openAuthModal('login', { allowClose: true });
}
if (registerBtn) {
registerBtn.style.display = 'inline-flex';
registerBtn.innerHTML = 'Kostenlos registrieren Starten ';
registerBtn.style.background = '#0284c7';
registerBtn.onclick = () => window.openAuthModal('register', { allowClose: true });
}
const mobDrawerAuthActions = document.getElementById('mobile-drawer-auth-actions');
if (mobDrawerAuthActions) mobDrawerAuthActions.style.display = 'flex';
if (mobDashLink) mobDashLink.style.display = 'none';
const mobLogoutBtn = document.getElementById('mobile-drawer-logout-btn');
if (mobLogoutBtn) mobLogoutBtn.style.display = 'none';
if (heroPrimaryCta) {
heroPrimaryCta.innerHTML = 'Jetzt kostenlos registrieren → ';
heroPrimaryCta.onclick = (e) => {
e.preventDefault();
window.openAuthModal('register', { allowClose: true });
};
}
// Alle allgemeinen Landing-Page Action Buttons für Besucher
document.querySelectorAll('.landing-cta-btn').forEach(btn => {
btn.innerHTML = 'Jetzt kostenlos registrieren & Kurse wählen → ';
btn.style.background = '#0284c7';
btn.onclick = (e) => {
e.preventDefault();
window.openAuthModal('register', { allowClose: true });
};
});
const footerLoginBtn = document.getElementById('footer-cta-login');
if (footerLoginBtn) footerLoginBtn.style.display = 'inline-flex';
if (landingBanner) landingBanner.style.display = 'none';
if (navDashLink) navDashLink.style.display = 'none';
}
// 🔒 Strikter Rollen-Filter für Dozenten- und Admin-Funktionen
const isTeacherOrAdmin = loggedIn && user && (user.role === 'teacher' || user.role === 'admin');
const isAdmin = loggedIn && user && user.role === 'admin';
document.querySelectorAll('.teacher-only, #nav-teacher-link, #mobile-drawer-teacher-link, #footer-teacher-link').forEach(el => {
if (isTeacherOrAdmin) {
el.style.display = (el.tagName === 'LI' || el.tagName === 'DIV') ? 'block' : 'inline-flex';
} else {
el.style.display = 'none';
}
});
document.querySelectorAll('.admin-only, #nav-admin-link, #mobile-drawer-admin-link, #footer-admin-link').forEach(el => {
if (isAdmin) {
el.style.display = (el.tagName === 'LI' || el.tagName === 'DIV') ? 'block' : 'inline-flex';
} else {
el.style.display = 'none';
}
});
// Aktualisiere ggf. vorhandene Curriculum-Aktions-Buttons
document.querySelectorAll('.portal-card-action-btn').forEach(btn => {
if (loggedIn) {
btn.innerHTML = 'Im Lernbereich öffnen → ';
btn.classList.remove('locked');
btn.style.background = '#10b981';
btn.onclick = () => { window.location.href = 'dashboard.html'; };
} else {
btn.innerHTML = 'Lehrplan ansehen ';
btn.classList.add('locked');
btn.style.background = '#0284c7';
btn.onclick = (e) => {
e.preventDefault();
window.openAuthModal('register', {
allowClose: true,
title: 'Lehrplan freischalten',
message: 'Erstelle einen kostenlosen Account in unter 30 Sekunden, um sofort vollen Zugriff auf alle Kurse und Module im Lernbereich zu erhalten.'
});
};
}
});
}
};
// Auth Modal UI
window.openAuthModal = function (initialMode = 'login', options = {}) {
if (window.AUTH && window.AUTH.isLoggedIn()) {
window.location.href = 'dashboard.html';
return;
}
const allowClose = options.allowClose !== false;
let modal = document.getElementById('auth-modal-overlay');
if (modal) modal.remove();
const allowReg = window.AUTH.platformStatus.allow_registration !== false && !window.AUTH.platformStatus.maintenance;
modal = document.createElement('div');
modal.id = 'auth-modal-overlay';
modal.className = 'certificate-overlay';
modal.style.zIndex = '999999';
modal.innerHTML = `
${allowClose ? `
✕
` : ''}
IT
${escapeHtml(options.title || (initialMode === 'register' && allowReg ? 'Kostenlos registrieren' : 'Anmelden'))}
IT-PRAXISPORTAL AUTHENTICATION
${escapeHtml(options.message || 'Erhalte sofortigen Zugriff auf 13 Praxis-Kurse, 216 interaktive Module und die Web-IDE.')}
Anmelden
${allowReg ? `
Konto erstellen
` : `
Registrierung pausiert
`}
100% DSGVO-konform • Serverstandort Deutschland • Keine Tracking-Cookies
`;
document.body.appendChild(modal);
let isRegister = (initialMode === 'register' && allowReg);
const tabLog = document.getElementById('auth-tab-login');
const tabReg = document.getElementById('auth-tab-register');
const nameField = document.getElementById('auth-field-name');
const roleField = document.getElementById('auth-field-role');
const profField = document.getElementById('auth-field-profession');
const submitBtn = document.getElementById('auth-submit-btn');
const errorBox = document.getElementById('auth-error-box');
const form = document.getElementById('auth-form');
function switchMode(reg) {
isRegister = reg && allowReg;
if (isRegister) {
if (tabReg) {
tabReg.style.background = '#ffffff';
tabReg.style.color = '#0284c7';
tabReg.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
}
tabLog.style.background = 'transparent';
tabLog.style.color = '#64748b';
tabLog.style.boxShadow = 'none';
if (nameField) nameField.style.display = 'block';
if (roleField) roleField.style.display = 'block';
if (profField) profField.style.display = 'block';
submitBtn.innerText = 'Jetzt kostenlos registrieren →';
submitBtn.style.background = '#0284c7';
} else {
tabLog.style.background = '#ffffff';
tabLog.style.color = '#0284c7';
tabLog.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
if (tabReg) {
tabReg.style.background = 'transparent';
tabReg.style.color = '#64748b';
tabReg.style.boxShadow = 'none';
}
if (nameField) nameField.style.display = 'none';
if (roleField) roleField.style.display = 'none';
if (profField) profField.style.display = 'none';
submitBtn.innerText = 'Anmelden →';
submitBtn.style.background = '#059669';
}
errorBox.style.display = 'none';
}
tabLog.onclick = () => switchMode(false);
if (tabReg) tabReg.onclick = () => switchMode(true);
switchMode(isRegister);
if (allowClose) {
const closeX = document.getElementById('auth-modal-close-x');
if (closeX) closeX.onclick = () => modal.remove();
const cancelBtn = document.getElementById('auth-cancel-btn');
if (cancelBtn) cancelBtn.onclick = () => modal.remove();
modal.onclick = (e) => {
if (e.target === modal) modal.remove();
};
}
form.onsubmit = async (e) => {
e.preventDefault();
errorBox.style.display = 'none';
const email = document.getElementById('auth-input-email').value.trim();
const password = document.getElementById('auth-input-password').value;
const name = document.getElementById('auth-input-name')?.value.trim() || email.split('@')[0];
const role = document.getElementById('auth-select-role')?.value || 'solo';
const profession = document.getElementById('auth-select-profession')?.value || '';
const rememberMe = document.getElementById('auth-remember-me')?.checked || false;
submitBtn.disabled = true;
submitBtn.innerText = 'Bitte warten...';
let result;
if (isRegister) {
result = await window.AUTH.register(name, email, password, role, rememberMe, profession);
} else {
result = await window.AUTH.login(email, password, rememberMe);
}
submitBtn.disabled = false;
submitBtn.innerText = isRegister ? 'Jetzt kostenlos registrieren →' : 'Anmelden →';
if (result.success) {
modal.remove();
if (options.onSuccess) {
options.onSuccess(result.user);
} else {
try { window.AUTH.updateUI(); } catch (e) {}
window.location.href = 'dashboard.html';
}
} else {
errorBox.innerText = result.error || 'Fehler bei der Authentifizierung';
errorBox.style.display = 'block';
}
};
};
// Initialisierung beim Laden des DOMs
function initAuth() {
window.AUTH.updateUI();
window.AUTH.checkStatus();
window.AUTH.fetchMe();
const params = new URLSearchParams(window.location.search);
const authParam = params.get('auth');
if (authParam === 'login' || authParam === 'register') {
if (!window.AUTH.isLoggedIn()) {
window.openAuthModal(authParam, { allowClose: true });
} else {
window.location.replace('dashboard.html');
}
}
}
// Globales User Avatar Dropdown Handling
window.toggleUserDropdown = function (event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
const menu = document.getElementById('portal-dropdown-menu');
const btn = document.getElementById('portal-user-btn');
if (!menu) return;
const isShown = menu.classList.contains('show');
if (isShown) {
menu.classList.remove('show');
if (btn) btn.setAttribute('aria-expanded', 'false');
} else {
menu.classList.add('show');
if (btn) btn.setAttribute('aria-expanded', 'true');
}
};
// Klick außerhalb schließt das Dropdown
document.addEventListener('click', (e) => {
const dd = document.getElementById('portal-user-dropdown');
const menu = document.getElementById('portal-dropdown-menu');
const btn = document.getElementById('portal-user-btn');
if (menu && menu.classList.contains('show')) {
if (!dd || !dd.contains(e.target)) {
menu.classList.remove('show');
if (btn) btn.setAttribute('aria-expanded', 'false');
}
}
});
// Escape-Taste schließt das Dropdown
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const menu = document.getElementById('portal-dropdown-menu');
const btn = document.getElementById('portal-user-btn');
if (menu && menu.classList.contains('show')) {
menu.classList.remove('show');
if (btn) btn.setAttribute('aria-expanded', 'false');
}
}
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initAuth);
} else {
initAuth();
}
})();