feat: add teacher system with approval flow

- Teacher registration requires subject selection; account starts pending
- Admin approves/rejects via existing admin panel
- Teacher panel (Materialien, Ankündigungen, Prüfungen, Noten) visible only to approved teachers
- Students see class materials and announcements via sidebar overlays
- Teachers can assign grades to students (scoped to own subject)
- New tables: teacher_materials, teacher_announcements, teacher_exams, teacher_assigned_grades
- subject column added to users; included in JWT and /api/me
- requireTeacher middleware fetches fresh status+subject from DB on every request
- Login hint: username is the part of the school email before the @
This commit is contained in:
Simon
2026-04-17 09:28:55 +02:00
parent db5efd8ed9
commit ae789318ba
7 changed files with 965 additions and 130 deletions
+23 -6
View File
@@ -212,6 +212,7 @@ footer a:hover { color: #2563eb; }
<div class="field">
<label for="l-user">Benutzername</label>
<input type="text" id="l-user" autocomplete="username" placeholder="dein.name" required>
<span style="font-size:11px;color:#9ca3af;margin-top:2px">Der Teil deiner Schul-E-Mail vor dem @</span>
</div>
<div class="field">
<label for="l-pass">Passwort</label>
@@ -232,9 +233,22 @@ footer a:hover { color: #2563eb; }
<div class="notice notice-amber" id="teacher-notice">
<i data-lucide="info" aria-hidden="true"></i> Lehrerkonten werden nach der Registrierung von einem Administrator geprüft und freigeschaltet.
</div>
<div class="field" id="subject-field" style="display:none">
<label for="r-subject">Lehrfach</label>
<select id="r-subject" style="padding:9px 12px;border:1px solid #e5e7eb;border-radius:7px;font-size:14px;font-family:inherit;color:#111827;background:#fff;outline:none;transition:border-color .12s">
<option value=""> Fach auswählen </option>
<option>Deutsch</option><option>Mathematik</option><option>Englisch</option>
<option>Informatik</option><option>Wirtschaft</option><option>Buchführung</option>
<option>BWL</option><option>VWL</option><option>Recht</option>
<option>Rechnungswesen</option><option>Sport</option><option>Religion</option>
<option>Geschichte</option><option>Gemeinschaftskunde</option><option>Physik</option>
<option>Chemie</option><option>Biologie</option><option>Sozialkunde</option>
<option>Ethik</option><option>Sonstiges</option>
</select>
</div>
<div class="field">
<label for="r-email">Schul E-Mail</label>
<input type="email" id="r-email" autocomplete="email" placeholder="vorname.nachname@ifb-schulen.com" required>
<input type="email" id="r-email" autocomplete="email" placeholder="Schul E-Mail-Adresse" required>
</div>
<div class="field">
<label for="r-pass">Passwort</label>
@@ -265,6 +279,7 @@ function selectRole(r) {
document.getElementById('role-teacher').classList.toggle('active', r === 'teacher');
document.getElementById('role-teacher').classList.toggle('teacher', r === 'teacher');
document.getElementById('teacher-notice').classList.toggle('show', r === 'teacher');
document.getElementById('subject-field').style.display = r === 'teacher' ? '' : 'none';
document.getElementById('reg-btn').textContent = r === 'teacher' ? 'Als Lehrer/in registrieren' : 'Account erstellen';
}
@@ -292,12 +307,14 @@ async function doLogin(e) {
async function doRegister(e) {
e.preventDefault(); clearErr('reg-err');
const body = {
email: document.getElementById('r-email').value,
password: document.getElementById('r-pass').value,
role: selectedRole,
};
if (selectedRole === 'teacher') body.subject = document.getElementById('r-subject').value;
const r = await fetch('/api/register', { method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({
email: document.getElementById('r-email').value,
password: document.getElementById('r-pass').value,
role: selectedRole
}) });
body: JSON.stringify(body) });
const d = await r.json();
if (!r.ok) { showErr('reg-err', d.error); return; }
if (d.pending) {