Klick öffnet /api/auth/forgot-password → 302 zur Keycloak-Reset-Page mit client_id + redirect_uri (auf eigene Domain). Keycloak schickt Mail mit Reset-Link, User setzt neues Passwort, kommt zurück. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
186 lines
9.7 KiB
HTML
186 lines
9.7 KiB
HTML
{#
|
|
auth_modal.html — Login- und Registrierungs-Modal für v2
|
|
Einbinden via: {% include "v2/components/auth_modal.html" %}
|
|
Öffnen via: document.getElementById('v2-auth-modal').style.display = 'flex'
|
|
#}
|
|
{% from "v2/components/icon.html" import icon %}
|
|
|
|
<!-- ── v2 Auth Modal ──────────────────────────────────────────────────── -->
|
|
<div id="v2-auth-modal"
|
|
role="dialog" aria-modal="true" aria-labelledby="v2-auth-modal-title"
|
|
style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:400;justify-content:center;align-items:center;"
|
|
onclick="if(event.target===this)v2AuthModalClose()">
|
|
|
|
<div style="background:var(--paper);border-radius:8px;padding:var(--space-6);max-width:440px;width:90%;box-shadow:0 8px 32px rgba(0,0,0,0.22);font-family:var(--font-sans);">
|
|
|
|
<!-- Header -->
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--space-5);">
|
|
<h3 id="v2-auth-modal-title"
|
|
style="margin:0;font-family:var(--font-sans);font-size:1.1rem;font-weight:700;color:var(--ecg-blue);letter-spacing:0.03em;">
|
|
Anmelden
|
|
</h3>
|
|
<button onclick="v2AuthModalClose()"
|
|
aria-label="Modal schließen"
|
|
style="background:none;border:none;cursor:pointer;color:var(--ecg-dark);opacity:0.6;font-size:1.2rem;line-height:1;padding:0;">✕</button>
|
|
</div>
|
|
|
|
<!-- Tabs -->
|
|
<div style="display:flex;border-bottom:2px solid var(--hairline);margin-bottom:var(--space-5);">
|
|
<button id="v2-auth-tab-login"
|
|
onclick="v2AuthSwitchTab('login')"
|
|
style="flex:1;padding:var(--space-2) var(--space-3);border:none;background:none;cursor:pointer;font-family:var(--font-sans);font-size:0.9rem;font-weight:700;color:var(--ecg-blue);border-bottom:2px solid var(--ecg-blue);margin-bottom:-2px;">
|
|
Anmelden
|
|
</button>
|
|
<button id="v2-auth-tab-register"
|
|
onclick="v2AuthSwitchTab('register')"
|
|
style="flex:1;padding:var(--space-2) var(--space-3);border:none;background:none;cursor:pointer;font-family:var(--font-sans);font-size:0.9rem;font-weight:400;color:var(--ecg-light);margin-bottom:-2px;">
|
|
Registrieren
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Login Form -->
|
|
<form id="v2-auth-login-form" onsubmit="v2AuthSubmitLogin(event)"
|
|
style="display:flex;flex-direction:column;gap:var(--space-3);">
|
|
<input name="username" placeholder="Benutzername" required autocomplete="username"
|
|
style="padding:var(--space-2) var(--space-3);border:1px solid var(--hairline);border-radius:4px;font-family:var(--font-sans);font-size:0.95rem;background:var(--surface);color:var(--ecg-dark);outline:none;">
|
|
<input name="password" type="password" placeholder="Passwort" required autocomplete="current-password"
|
|
style="padding:var(--space-2) var(--space-3);border:1px solid var(--hairline);border-radius:4px;font-family:var(--font-sans);font-size:0.95rem;background:var(--surface);color:var(--ecg-dark);outline:none;">
|
|
<button type="submit"
|
|
style="padding:var(--space-3);background:var(--ecg-blue);color:#fff;border:none;border-radius:4px;cursor:pointer;font-family:var(--font-sans);font-size:0.95rem;font-weight:700;letter-spacing:0.04em;">
|
|
Anmelden
|
|
</button>
|
|
<a href="/api/auth/forgot-password" target="_blank" rel="noopener"
|
|
style="font-family:var(--font-mono);font-size:0.78rem;color:var(--ecg-blue);text-align:right;text-decoration:none;border-bottom:1px solid rgba(0,157,165,0.35);align-self:flex-end;">
|
|
Passwort vergessen?
|
|
</a>
|
|
</form>
|
|
|
|
<!-- Register Form -->
|
|
<form id="v2-auth-register-form" onsubmit="v2AuthSubmitRegister(event)"
|
|
style="display:none;flex-direction:column;gap:var(--space-3);">
|
|
<input name="firstName" placeholder="Vorname" required autocomplete="given-name"
|
|
style="padding:var(--space-2) var(--space-3);border:1px solid var(--hairline);border-radius:4px;font-family:var(--font-sans);font-size:0.95rem;background:var(--surface);color:var(--ecg-dark);outline:none;">
|
|
<input name="lastName" placeholder="Nachname" required autocomplete="family-name"
|
|
style="padding:var(--space-2) var(--space-3);border:1px solid var(--hairline);border-radius:4px;font-family:var(--font-sans);font-size:0.95rem;background:var(--surface);color:var(--ecg-dark);outline:none;">
|
|
<input name="email" type="email" placeholder="E-Mail-Adresse" required autocomplete="email"
|
|
style="padding:var(--space-2) var(--space-3);border:1px solid var(--hairline);border-radius:4px;font-family:var(--font-sans);font-size:0.95rem;background:var(--surface);color:var(--ecg-dark);outline:none;">
|
|
<input name="username" placeholder="Benutzername (frei wählbar)" required autocomplete="username"
|
|
style="padding:var(--space-2) var(--space-3);border:1px solid var(--hairline);border-radius:4px;font-family:var(--font-sans);font-size:0.95rem;background:var(--surface);color:var(--ecg-dark);outline:none;">
|
|
<button type="submit"
|
|
style="padding:var(--space-3);background:var(--ecg-green);color:#fff;border:none;border-radius:4px;cursor:pointer;font-family:var(--font-sans);font-size:0.95rem;font-weight:700;letter-spacing:0.04em;">
|
|
Freischaltung beantragen
|
|
</button>
|
|
<p style="margin:0;font-size:0.78rem;color:var(--ecg-light);">
|
|
Nach Freischaltung erhalten Sie eine E-Mail zum Passwort setzen.
|
|
</p>
|
|
</form>
|
|
|
|
<!-- Status-Anzeige (Fehler / Erfolg) -->
|
|
<div id="v2-auth-status" style="margin-top:var(--space-3);font-size:0.875rem;min-height:1.2em;"></div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
/* ── v2 Auth Modal ───────────────────────────────────────────────────── */
|
|
(function () {
|
|
|
|
function open() {
|
|
const modal = document.getElementById('v2-auth-modal');
|
|
if (modal) modal.style.display = 'flex';
|
|
// Status leeren bei jedem Öffnen
|
|
const status = document.getElementById('v2-auth-status');
|
|
if (status) status.innerHTML = '';
|
|
}
|
|
|
|
function close() {
|
|
const modal = document.getElementById('v2-auth-modal');
|
|
if (modal) modal.style.display = 'none';
|
|
}
|
|
|
|
function switchTab(tab) {
|
|
const loginForm = document.getElementById('v2-auth-login-form');
|
|
const registerForm = document.getElementById('v2-auth-register-form');
|
|
const tabLogin = document.getElementById('v2-auth-tab-login');
|
|
const tabRegister = document.getElementById('v2-auth-tab-register');
|
|
const title = document.getElementById('v2-auth-modal-title');
|
|
const status = document.getElementById('v2-auth-status');
|
|
|
|
if (!loginForm || !registerForm) return;
|
|
|
|
const isLogin = tab === 'login';
|
|
loginForm.style.display = isLogin ? 'flex' : 'none';
|
|
registerForm.style.display = isLogin ? 'none' : 'flex';
|
|
|
|
// Tab-Stile
|
|
const activeStyle = 'color:var(--ecg-blue);font-weight:700;border-bottom:2px solid var(--ecg-blue);margin-bottom:-2px;';
|
|
const inactiveStyle = 'color:var(--ecg-light);font-weight:400;margin-bottom:-2px;';
|
|
tabLogin.style.cssText += isLogin ? activeStyle : inactiveStyle;
|
|
tabRegister.style.cssText += isLogin ? inactiveStyle : activeStyle;
|
|
|
|
if (title) title.textContent = isLogin ? 'Anmelden' : 'Registrieren';
|
|
if (status) status.innerHTML = '';
|
|
}
|
|
|
|
async function submitLogin(e) {
|
|
e.preventDefault();
|
|
const form = e.target;
|
|
const status = document.getElementById('v2-auth-status');
|
|
status.innerHTML = '<span style="color:var(--ecg-blue);">Anmeldung läuft\u2026</span>';
|
|
try {
|
|
const resp = await fetch('/api/auth/login', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams(new FormData(form)).toString()
|
|
});
|
|
const data = await resp.json();
|
|
if (resp.ok && data.authenticated) {
|
|
status.innerHTML = '<span style="color:var(--ecg-green);">Angemeldet.</span>';
|
|
close();
|
|
location.reload();
|
|
} else {
|
|
status.innerHTML = '<span style="color:#c33;">' + (data.detail || 'Anmeldung fehlgeschlagen') + '</span>';
|
|
}
|
|
} catch (err) {
|
|
status.innerHTML = '<span style="color:#c33;">' + err.message + '</span>';
|
|
}
|
|
}
|
|
|
|
async function submitRegister(e) {
|
|
e.preventDefault();
|
|
const form = e.target;
|
|
const status = document.getElementById('v2-auth-status');
|
|
status.innerHTML = '<span style="color:var(--ecg-blue);">Wird registriert\u2026</span>';
|
|
try {
|
|
const resp = await fetch('/api/auth/register', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams(new FormData(form)).toString()
|
|
});
|
|
const data = await resp.json();
|
|
if (resp.ok) {
|
|
status.innerHTML = '<span style="color:var(--ecg-green);">Freischaltung beantragt. Sie erhalten eine E-Mail, sobald Ihr Konto aktiviert ist.</span>';
|
|
form.reset();
|
|
} else {
|
|
status.innerHTML = '<span style="color:#c33;">' + (data.detail || 'Registrierung fehlgeschlagen') + '</span>';
|
|
}
|
|
} catch (err) {
|
|
status.innerHTML = '<span style="color:#c33;">' + err.message + '</span>';
|
|
}
|
|
}
|
|
|
|
// ESC schließt Modal
|
|
document.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Escape') close();
|
|
});
|
|
|
|
// Globale API für Topbar-Button und externe Aufrufer
|
|
window.v2AuthModalOpen = open;
|
|
window.v2AuthModalClose = close;
|
|
window.v2AuthSwitchTab = switchTab;
|
|
window.v2AuthSubmitLogin = submitLogin;
|
|
window.v2AuthSubmitRegister = submitRegister;
|
|
|
|
})();
|
|
</script>
|