1e51ef7def
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/ - 12 code-workspace-Files - STATUS-*.md - viele M/D/R-Änderungen an bereits getrackten Files - .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
232 lines
8.1 KiB
HTML
232 lines
8.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
<title>GeoGraSim V2 — Anmelden</title>
|
|
<link rel="stylesheet" href="./assets/css/design-system-v2.css">
|
|
<style>
|
|
body {
|
|
background: linear-gradient(135deg, var(--ggs-fjord-dark), var(--ggs-fjord));
|
|
min-height: 100dvh;
|
|
display: flex; align-items: center; justify-content: center;
|
|
padding: max(16px, env(safe-area-inset-top)) 16px max(16px, env(safe-area-inset-bottom));
|
|
}
|
|
.login-card {
|
|
background: var(--ggs-white);
|
|
border-radius: var(--ggs-radius-lg);
|
|
box-shadow: var(--ggs-shadow-lg);
|
|
max-width: 440px; width: 100%;
|
|
padding: var(--ggs-gap-xl);
|
|
}
|
|
.login-header {
|
|
text-align: center;
|
|
margin-bottom: var(--ggs-gap-lg);
|
|
}
|
|
.login-header h1 {
|
|
font-size: 1.4rem; color: var(--ggs-fjord-dark);
|
|
margin-bottom: var(--ggs-gap-xs);
|
|
}
|
|
.login-header .sub {
|
|
font-size: 0.85rem; color: var(--ggs-text-muted);
|
|
}
|
|
.tabs {
|
|
display: flex; gap: var(--ggs-gap-xs);
|
|
border-bottom: 1px solid var(--ggs-border);
|
|
margin-bottom: var(--ggs-gap-lg);
|
|
}
|
|
.tab-btn {
|
|
flex: 1; padding: var(--ggs-gap-sm) var(--ggs-gap-md);
|
|
background: transparent; border: none; cursor: pointer;
|
|
font-size: 0.9rem; font-family: inherit; font-weight: 600;
|
|
color: var(--ggs-text-muted);
|
|
border-bottom: 3px solid transparent;
|
|
min-height: var(--ggs-touch-min);
|
|
}
|
|
.tab-btn.active {
|
|
color: var(--ggs-fjord-dark);
|
|
border-bottom-color: var(--ggs-fjord-dark);
|
|
}
|
|
.panel { display: none; }
|
|
.panel.active { display: block; }
|
|
.form-row { margin-bottom: var(--ggs-gap-md); }
|
|
.form-row label {
|
|
display: block; font-size: 0.78rem; font-weight: 600;
|
|
color: var(--ggs-text); margin-bottom: var(--ggs-gap-xs);
|
|
}
|
|
.form-row input { width: 100%; }
|
|
.msg {
|
|
padding: var(--ggs-gap-sm) var(--ggs-gap-md);
|
|
border-radius: var(--ggs-radius-md);
|
|
font-size: 0.85rem; margin-bottom: var(--ggs-gap-md);
|
|
display: none;
|
|
}
|
|
.msg.error { background: var(--ggs-coral-light); color: var(--ggs-coral); display: block; }
|
|
.msg.info { background: var(--ggs-fjord-light); color: var(--ggs-fjord-dark); display: block; }
|
|
.actions-row { margin-top: var(--ggs-gap-lg); display: flex; gap: var(--ggs-gap-md); }
|
|
.footer {
|
|
text-align: center; margin-top: var(--ggs-gap-lg);
|
|
font-size: 0.78rem; color: var(--ggs-text-muted);
|
|
}
|
|
.footer a { color: var(--ggs-fjord-dark); text-decoration: none; }
|
|
.footer a:hover { text-decoration: underline; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<form class="login-card" id="loginForm" autocomplete="on" novalidate>
|
|
<div class="login-header">
|
|
<h1>GeoGraSim V2 — Anmelden</h1>
|
|
<p class="sub">Diese Beta-Version läuft parallel zur Hauptseite.</p>
|
|
</div>
|
|
|
|
<div class="tabs" role="tablist">
|
|
<button type="button" class="tab-btn active" id="tab-teacher" role="tab" aria-selected="true" aria-controls="panel-teacher">Lehrperson</button>
|
|
<button type="button" class="tab-btn" id="tab-student" role="tab" aria-selected="false" aria-controls="panel-student">Schüler*in</button>
|
|
</div>
|
|
|
|
<div class="msg" id="msg" role="status" aria-live="polite"></div>
|
|
|
|
<!-- Lehrperson-Panel -->
|
|
<div class="panel active" id="panel-teacher" role="tabpanel" aria-labelledby="tab-teacher">
|
|
<div class="form-row">
|
|
<label for="t-email">E-Mail oder Benutzername</label>
|
|
<input class="ggs-input" type="text" id="t-email" name="email"
|
|
autocomplete="username" required>
|
|
</div>
|
|
<div class="form-row">
|
|
<label for="t-pass">Passwort</label>
|
|
<input class="ggs-input" type="password" id="t-pass" name="password"
|
|
autocomplete="current-password" required>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Schüler*in-Panel -->
|
|
<div class="panel" id="panel-student" role="tabpanel" aria-labelledby="tab-student" hidden>
|
|
<div class="form-row">
|
|
<label for="s-user">Benutzername</label>
|
|
<input class="ggs-input" type="text" id="s-user" name="username" autocomplete="username">
|
|
</div>
|
|
<div class="form-row">
|
|
<label for="s-pass">Passwort</label>
|
|
<input class="ggs-input" type="password" id="s-pass" name="password" autocomplete="current-password">
|
|
</div>
|
|
<div class="form-row" id="row-classcode" hidden>
|
|
<label for="s-code">Klassencode (nur falls du in mehreren Klassen bist)</label>
|
|
<input class="ggs-input" type="text" id="s-code" name="classCode"
|
|
autocomplete="off" inputmode="text" style="text-transform:uppercase">
|
|
</div>
|
|
<div class="form-row" id="row-classpick" hidden>
|
|
<label for="s-class">Welche Klasse?</label>
|
|
<select class="ggs-select" id="s-class"></select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="actions-row">
|
|
<button type="submit" class="ggs-btn ggs-btn-primary ggs-btn-major" id="submitBtn" style="flex:1">
|
|
Anmelden
|
|
</button>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
<p>Geht zur <a href="../">Hauptseite (V1)</a> oder zur <a href="../barrierefreiheit.html">Barrierefreiheitserklärung</a>.</p>
|
|
</div>
|
|
</form>
|
|
|
|
<script type="module">
|
|
import { auth } from './assets/js/auth-client.js';
|
|
|
|
// Wenn schon eingeloggt: Direkt-Redirect je nach Rolle
|
|
if (auth.token) {
|
|
location.href = (auth.role === 'student') ? './schueler.html' : './teacher.html';
|
|
}
|
|
|
|
const $ = id => document.getElementById(id);
|
|
let mode = 'teacher';
|
|
|
|
function switchTab(newMode) {
|
|
mode = newMode;
|
|
['teacher','student'].forEach(m => {
|
|
$('tab-' + m).classList.toggle('active', m === mode);
|
|
$('tab-' + m).setAttribute('aria-selected', m === mode ? 'true' : 'false');
|
|
const panel = $('panel-' + m);
|
|
panel.classList.toggle('active', m === mode);
|
|
panel.hidden = m !== mode;
|
|
});
|
|
hideMsg();
|
|
}
|
|
$('tab-teacher').addEventListener('click', () => switchTab('teacher'));
|
|
$('tab-student').addEventListener('click', () => switchTab('student'));
|
|
|
|
function showMsg(text, type) {
|
|
const m = $('msg');
|
|
m.textContent = text;
|
|
m.className = 'msg ' + type;
|
|
}
|
|
function hideMsg() {
|
|
const m = $('msg');
|
|
m.className = 'msg';
|
|
}
|
|
|
|
$('loginForm').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
hideMsg();
|
|
$('submitBtn').disabled = true;
|
|
$('submitBtn').textContent = 'Anmelden …';
|
|
|
|
try {
|
|
let result;
|
|
if (mode === 'teacher') {
|
|
result = await auth.login('teacher', {
|
|
email: $('t-email').value.trim(),
|
|
password: $('t-pass').value,
|
|
});
|
|
} else {
|
|
// Klassencode: aus Picker oder Textfeld nehmen
|
|
const codeFromPick = $('s-class').value || '';
|
|
const codeFromText = $('s-code').value.trim().toUpperCase();
|
|
result = await auth.login('student', {
|
|
classCode: codeFromPick || codeFromText,
|
|
username: $('s-user').value.trim(),
|
|
password: $('s-pass').value,
|
|
});
|
|
}
|
|
showMsg('Angemeldet — wird weitergeleitet …', 'info');
|
|
setTimeout(() => {
|
|
location.href = (result.role === 'student') ? './schueler.html' : './teacher.html';
|
|
}, 300);
|
|
} catch (err) {
|
|
// Spezialfall: Server sagt „mehrere Klassen, bitte auswählen"
|
|
if (err.message && err.message.includes('mehreren Klassen')) {
|
|
try {
|
|
// Liste aus dem Response holen — auth.login wirft mit message, wir holen nochmal direkt
|
|
const r = await fetch('./php/api/auth/login.php', {
|
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
role: 'student',
|
|
username: $('s-user').value.trim(),
|
|
password: $('s-pass').value,
|
|
}),
|
|
});
|
|
const data = await r.json();
|
|
if (data.classes?.length) {
|
|
const sel = $('s-class');
|
|
sel.innerHTML = data.classes.map(c =>
|
|
`<option value="${c.joinCode}">${c.className} (${c.joinCode})</option>`
|
|
).join('');
|
|
$('row-classpick').hidden = false;
|
|
showMsg('Username gibt es in mehreren Klassen — bitte wählen.', 'info');
|
|
}
|
|
} catch { /* fallback unten */ }
|
|
} else {
|
|
showMsg(err.message || 'Anmeldung fehlgeschlagen.', 'error');
|
|
}
|
|
$('submitBtn').disabled = false;
|
|
$('submitBtn').textContent = 'Anmelden';
|
|
}
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|