202 lines
9.8 KiB
HTML
202 lines
9.8 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>SysnaptiK Neural – Deine Zukunft beginnt hier</title>
|
||
<link rel="stylesheet" href="/css/theme.css?v=3">
|
||
<script src="/js/theme.js?v=4"></script>
|
||
<link rel="stylesheet" href="/css/auth.css">
|
||
</head>
|
||
<body>
|
||
|
||
<div class="mf-theme-float"><button class="mf-theme"></button></div>
|
||
|
||
<div class="mf-center">
|
||
<div class="auth-stage">
|
||
|
||
<div class="auth-container" id="authContainer">
|
||
|
||
<!-- ===== Sign In ===== -->
|
||
<div class="form-box login">
|
||
<div class="inner">
|
||
<div class="auth-head">
|
||
<span class="mark"></span>
|
||
<b>SYSNAPTIK NEURAL</b>
|
||
<h2>Willkommen zurück</h2>
|
||
<span class="sub">Melde dich an, um weiterzulernen.</span>
|
||
</div>
|
||
|
||
<div th:if="${param.error}" class="mf-alert err">Benutzername oder Passwort sind falsch.</div>
|
||
<div th:if="${param.logout}" class="mf-alert ok">Du hast dich erfolgreich abgemeldet.</div>
|
||
<div th:if="${param.registerSuccess}" class="mf-alert ok">Fast geschafft: Wir haben dir einen Bestätigungslink per E-Mail gesendet.</div>
|
||
<div th:if="${param.emailVerified}" class="mf-alert ok">Deine E-Mail-Adresse wurde bestätigt. Du kannst dich jetzt anmelden.</div>
|
||
<div th:if="${param.passwordReset}" class="mf-alert ok">Dein Passwort wurde geändert. Du kannst dich jetzt anmelden.</div>
|
||
<div th:if="${param.verificationSent}" class="mf-alert ok">Falls ein unbestätigter Account zu dieser Adresse existiert, wurde ein neuer Link versendet.</div>
|
||
<div th:if="${param.verificationInvalid}" class="mf-alert err">Dieser Bestätigungslink ist ungültig oder abgelaufen.</div>
|
||
<div th:if="${param.resetInvalid}" class="mf-alert err">Dieser Passwort-Link ist ungültig oder abgelaufen.</div>
|
||
|
||
<form th:action="@{/login}" method="post">
|
||
<div class="mf-field">
|
||
<label class="mf-label" for="login-username">Benutzername</label>
|
||
<input id="login-username" class="mf-input" type="text" name="username" placeholder="Benutzername" required th:value="${param.username}">
|
||
</div>
|
||
<div class="mf-field">
|
||
<label class="mf-label" for="login-password">Passwort</label>
|
||
<input id="login-password" class="mf-input" type="password" name="password" placeholder="Passwort" required>
|
||
</div>
|
||
<button class="neon-btn" type="submit">Login</button>
|
||
</form>
|
||
|
||
<p class="mobile-switch"><a href="/forgot-password">Passwort vergessen?</a></p>
|
||
|
||
<p class="mobile-switch">Noch keinen Account? <a data-goto="signup">Jetzt registrieren</a></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== Sign Up ===== -->
|
||
<div class="form-box signup">
|
||
<div class="inner">
|
||
<div class="auth-head">
|
||
<span class="mark"></span>
|
||
<b>SYSNAPTIK NEURAL</b>
|
||
<h2>Account erstellen</h2>
|
||
<span class="sub">Kostenlos – und in 30 Sekunden startklar.</span>
|
||
</div>
|
||
|
||
<div th:if="${param.registerError}" class="mf-alert err">Benutzername bereits vergeben.</div>
|
||
<div th:if="${param.passwordMismatch}" class="mf-alert err">Die Passwörter stimmen nicht überein. Bitte erneut eingeben.</div>
|
||
<div th:if="${param.passwordPolicy}" class="mf-alert err">Das Passwort muss mindestens 8 Zeichen lang sein.</div>
|
||
<div th:if="${param.emailInvalid}" class="mf-alert err">Bitte gib eine gültige E-Mail-Adresse an.</div>
|
||
<div th:if="${param.emailTaken}" class="mf-alert err">Diese E-Mail-Adresse wird bereits verwendet.</div>
|
||
<div th:if="${param.registerMailError}" class="mf-alert err">Der Account wurde angelegt, aber die Bestätigungs-E-Mail konnte nicht gesendet werden. Fordere unten einen neuen Link an.</div>
|
||
|
||
<form th:action="@{/register}" method="post">
|
||
<div class="mf-field">
|
||
<label class="mf-label" for="reg-username">Benutzername</label>
|
||
<input id="reg-username" class="mf-input" type="text" name="username" placeholder="Benutzername" required>
|
||
</div>
|
||
<div class="mf-field">
|
||
<label class="mf-label" for="reg-email">E-Mail</label>
|
||
<input id="reg-email" class="mf-input" type="email" name="email" placeholder="E-Mail" required>
|
||
</div>
|
||
<div class="mf-field">
|
||
<label class="mf-label" for="reg-password">Passwort</label>
|
||
<input id="reg-password" class="mf-input" type="password" name="password" placeholder="Passwort (min. 8 Zeichen)" required minlength="8">
|
||
</div>
|
||
<div class="mf-field">
|
||
<label class="mf-label" for="reg-password2">Passwort wiederholen</label>
|
||
<input id="reg-password2" class="mf-input" type="password" name="confirmPassword" placeholder="Passwort wiederholen" required>
|
||
</div>
|
||
<p id="reg-pw-msg" class="mf-alert err" style="display:none">Die Passwörter stimmen nicht überein.</p>
|
||
<button class="neon-btn" type="submit">Account erstellen</button>
|
||
</form>
|
||
|
||
<div class="auth-resend">
|
||
<button id="resend-toggle" class="auth-resend-toggle" type="button"
|
||
aria-expanded="false" aria-controls="resend-form">
|
||
Bestätigungslink erneut senden
|
||
</button>
|
||
<form id="resend-form" th:action="@{/verification/resend}" method="post" hidden>
|
||
<label class="mf-label" for="resend-email">E-Mail-Adresse</label>
|
||
<div class="auth-resend-fields">
|
||
<input id="resend-email" class="mf-input" type="email" name="email" placeholder="E-Mail-Adresse" required autocomplete="email">
|
||
<button class="mf-btn outline" type="submit">Link senden</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<p class="mobile-switch">Schon dabei? <a data-goto="login">Zum Login</a></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== Overlay ===== -->
|
||
<div class="overlay-container">
|
||
<div class="overlay">
|
||
<!-- sichtbar im Signup-Modus -> zurück zum Login -->
|
||
<div class="overlay-panel overlay-left">
|
||
<span class="o-emblem"></span>
|
||
<h3>Schon dabei?</h3>
|
||
<p>Melde dich an und setze dein Lernnetz genau dort fort, wo du aufgehört hast.</p>
|
||
<button type="button" class="ghost-btn" id="toLogin">Zum Login</button>
|
||
</div>
|
||
<!-- sichtbar im Login-Modus -> zum Signup -->
|
||
<div class="overlay-panel overlay-right">
|
||
<span class="o-emblem"></span>
|
||
<h3>Neu bei SysnaptiK Neural?</h3>
|
||
<p>Erstelle deinen kostenlosen Account und schalte die volle Lernplattform frei.</p>
|
||
<button type="button" class="ghost-btn" id="toSignup">Account erstellen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<footer class="mf-footer">
|
||
<a href="#">Support</a>
|
||
<a href="#">Feedback</a>
|
||
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
|
||
<a href="/richtlinien">Richtlinien</a>
|
||
<a href="/datenschutz">Datenschutz</a>
|
||
<a href="/impressum">Impressum</a>
|
||
</footer>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
var c = document.getElementById('authContainer');
|
||
var toSignup = document.getElementById('toSignup');
|
||
var toLogin = document.getElementById('toLogin');
|
||
if (toSignup) toSignup.addEventListener('click', function () { c.classList.add('active'); });
|
||
if (toLogin) toLogin.addEventListener('click', function () { c.classList.remove('active'); });
|
||
|
||
var resendToggle = document.getElementById('resend-toggle');
|
||
var resendForm = document.getElementById('resend-form');
|
||
if (resendToggle && resendForm) {
|
||
resendToggle.addEventListener('click', function () {
|
||
var expand = resendForm.hidden;
|
||
resendForm.hidden = !expand;
|
||
resendToggle.setAttribute('aria-expanded', String(expand));
|
||
if (expand) resendForm.querySelector('input').focus();
|
||
});
|
||
}
|
||
|
||
document.querySelectorAll('[data-goto="signup"]').forEach(function (el) {
|
||
el.addEventListener('click', function (e) { e.preventDefault(); c.classList.add('active'); });
|
||
});
|
||
document.querySelectorAll('[data-goto="login"]').forEach(function (el) {
|
||
el.addEventListener('click', function (e) { e.preventDefault(); c.classList.remove('active'); });
|
||
});
|
||
|
||
// Bei Registrierungs-/Passwortfehler direkt das Signup-Panel öffnen
|
||
var qp = new URLSearchParams(window.location.search);
|
||
if (qp.has('registerError') || qp.has('passwordMismatch') || qp.has('passwordPolicy') || qp.has('emailInvalid') || qp.has('emailTaken') || qp.has('registerMailError')) {
|
||
c.classList.add('active');
|
||
}
|
||
|
||
// Client-seitiger Passwort-Abgleich (sofortiges Feedback; der Server prüft zusätzlich)
|
||
var pw = document.getElementById('reg-password');
|
||
var pw2 = document.getElementById('reg-password2');
|
||
var pwMsg = document.getElementById('reg-pw-msg');
|
||
var signupForm = pw2 ? pw2.closest('form') : null;
|
||
function pwCheck() {
|
||
var mismatch = pw2.value.length > 0 && pw.value !== pw2.value;
|
||
if (pwMsg) pwMsg.style.display = mismatch ? 'block' : 'none';
|
||
pw2.setCustomValidity(pw.value !== pw2.value ? 'Passwörter stimmen nicht überein' : '');
|
||
return !mismatch;
|
||
}
|
||
if (pw && pw2) {
|
||
pw.addEventListener('input', pwCheck);
|
||
pw2.addEventListener('input', pwCheck);
|
||
if (signupForm) signupForm.addEventListener('submit', function (e) {
|
||
if (pw.value !== pw2.value) { e.preventDefault(); pwCheck(); c.classList.add('active'); }
|
||
});
|
||
}
|
||
})();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|