Projektleiter-Rolle, Papierkorb, neue Login-Seite und Benutzerverwaltung
- Neue Rolle "Projektleiter": Projekte anlegen und löschen nur noch für Projektleiter und Administratoren (serverseitig in /save-week geprüft) - Papierkorb: gelöschte Projekte 30 Tage durch Admins wiederherstellbar - Benutzerverwaltung: Aktionen im Drei-Punkte-Menü mit Rückfrage im Menü, getrennte Felder für Vor- und Nachname, E-Mail-Vorschlag v.nachname@rauschenberg-ing.de, Benutzername = Anfang der E-Mail, Person-ID aus dem Kürzel - Endgültiges Löschen eines Benutzers entfernt ihn auch aus den Projekten und Aufgaben der aktuellen Sitzung; Dateien bleiben als "Gelöschter Benutzer" - Login-Seite neu (Design "Kalenderwoche") mit Logo als Wasserzeichen; Anmeldung ohne Groß-/Kleinschreibung, auch mit voller E-Mail-Adresse Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
93505e0e48
commit
f4fb3c550b
13 files changed
+1214
-116
No files matched your search
+100
-7
@@ -33,12 +33,30 @@ let currentUser = null;
|
||||
function renderUserBar(){
|
||||
const info = document.getElementById('userBarInfo');
|
||||
if(info && currentUser){
|
||||
const roleLabel = currentUser.role === 'admin' ? 'Administrator'
|
||||
: currentUser.role === 'kiosk' ? 'Kiosk' : 'Mitglied';
|
||||
const roleLabel = systemRolleText(currentUser.role);
|
||||
info.textContent = (currentUser.displayName || currentUser.username) + ' (' + roleLabel + ')';
|
||||
}
|
||||
const navAdminSection = document.getElementById('navAdminSection');
|
||||
if(navAdminSection) navAdminSection.style.display = (currentUser && currentUser.role === 'admin') ? '' : 'none';
|
||||
document.querySelectorAll('.nur-projektleitung').forEach(el => { el.hidden = !darfProjektlisteAendern(); });
|
||||
}
|
||||
|
||||
// systemRolleText: Anzeigename einer Systemrolle (users.role). Auch von der
|
||||
// Benutzerverwaltung (js/admin-users.js) genutzt.
|
||||
function systemRolleText(role){
|
||||
return role === 'admin' ? 'Administrator'
|
||||
: role === 'projektleiter' ? 'Projektleiter'
|
||||
: role === 'kiosk' ? 'Kiosk' : 'Mitglied';
|
||||
}
|
||||
|
||||
// darfProjektlisteAendern: ob das angemeldete Konto Projekte anlegen und aus
|
||||
// der Projektliste löschen darf (Projektleiter und Administratoren). Reines
|
||||
// UI-Gate — dieselbe Regel setzt /save-week serverseitig durch
|
||||
// (darfProjektlisteAendern() in server.js), dort mit 403. Knöpfe zum
|
||||
// Anlegen tragen die Klasse .nur-projektleitung und werden von
|
||||
// renderUserBar() ausgeblendet.
|
||||
function darfProjektlisteAendern(){
|
||||
return !!currentUser && (currentUser.role === 'admin' || currentUser.role === 'projektleiter');
|
||||
}
|
||||
|
||||
// logout: beendet die Server-Session und lädt die Seite neu (zeigt danach
|
||||
@@ -48,9 +66,63 @@ async function logout(){
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// showLoginOverlay / hideLoginOverlay: blenden das blockierende Login-Overlay ein/aus.
|
||||
// showLoginOverlay / hideLoginOverlay: blenden die blockierende Login-Seite ein/aus.
|
||||
function showLoginOverlay(){
|
||||
renderLoginWoche();
|
||||
document.getElementById('loginOverlay').style.display = 'flex';
|
||||
const feld = document.getElementById('loginUsername');
|
||||
if(feld && !feld.value) feld.focus();
|
||||
}
|
||||
|
||||
// renderLoginWoche: füllt die rechte Seite der Login-Seite — laufende
|
||||
// Kalenderwoche (getWeekNumber(), js/utils.js), Jahr der ISO-Woche, die
|
||||
// Arbeitstage Mo-Fr mit dem heutigen Tag markiert und das Datum.
|
||||
function renderLoginWoche(){
|
||||
const heute = new Date();
|
||||
const kw = getWeekNumber(heute);
|
||||
// ISO-Jahr: das Jahr des Donnerstags dieser Woche (Ende Dezember/Anfang
|
||||
// Januar weicht es vom Kalenderjahr ab, z. B. KW 01 / 2027 am 30.12.2026).
|
||||
const donnerstag = new Date(heute); donnerstag.setDate(heute.getDate() + 3 - ((heute.getDay() + 6) % 7));
|
||||
const zahl = document.getElementById('loginKwZahl');
|
||||
if(!zahl) return;
|
||||
zahl.textContent = String(kw).padStart(2, '0');
|
||||
document.getElementById('loginKwJahr').textContent = donnerstag.getFullYear();
|
||||
document.getElementById('loginDatum').textContent =
|
||||
heute.toLocaleDateString('de-DE', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
|
||||
const montag = new Date(heute); montag.setDate(heute.getDate() - ((heute.getDay() + 6) % 7));
|
||||
document.getElementById('loginTage').innerHTML = ['Mo', 'Di', 'Mi', 'Do', 'Fr'].map((n, i) => {
|
||||
const d = new Date(montag); d.setDate(montag.getDate() + i);
|
||||
const istHeute = d.toDateString() === heute.toDateString();
|
||||
return '<li' + (istHeute ? ' class="ist-heute" aria-current="date"' : '') + '><span>' + n + '</span><b>' + d.getDate() + '</b></li>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// verdrahteLoginHilfen: „Anzeigen" für das Passwort und der Hinweis auf
|
||||
// eine aktive Feststelltaste. Einmalig.
|
||||
let loginHilfenVerdrahtet = false;
|
||||
function verdrahteLoginHilfen(){
|
||||
if(loginHilfenVerdrahtet) return;
|
||||
loginHilfenVerdrahtet = true;
|
||||
const pw = document.getElementById('loginPassword');
|
||||
const toggle = document.getElementById('loginPwToggle');
|
||||
const caps = document.getElementById('loginCaps');
|
||||
if(toggle) toggle.addEventListener('click', () => {
|
||||
const sichtbar = pw.type === 'text';
|
||||
pw.type = sichtbar ? 'password' : 'text';
|
||||
toggle.textContent = sichtbar ? 'Anzeigen' : 'Verbergen';
|
||||
toggle.setAttribute('aria-pressed', String(!sichtbar));
|
||||
pw.focus();
|
||||
});
|
||||
const capsPruefen = e => { if(caps && e.getModifierState) caps.hidden = !e.getModifierState('CapsLock'); };
|
||||
pw.addEventListener('keydown', capsPruefen);
|
||||
pw.addEventListener('keyup', capsPruefen);
|
||||
pw.addEventListener('blur', () => { if(caps) caps.hidden = true; });
|
||||
['loginUsername', 'loginPassword'].forEach(id => {
|
||||
document.getElementById(id).addEventListener('input', e => {
|
||||
e.target.closest('.login-feld').classList.remove('hat-fehler');
|
||||
document.getElementById('loginError').hidden = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
function hideLoginOverlay(){
|
||||
document.getElementById('loginOverlay').style.display = 'none';
|
||||
@@ -174,24 +246,45 @@ async function checkAuthAndInit(onAuthenticated){
|
||||
return;
|
||||
}
|
||||
showLoginOverlay();
|
||||
verdrahteLoginHilfen();
|
||||
const form = document.getElementById('loginForm');
|
||||
const errEl = document.getElementById('loginError');
|
||||
const btn = document.getElementById('loginSubmit');
|
||||
const zeigeFehler = (text, feldId) => {
|
||||
errEl.textContent = text;
|
||||
errEl.hidden = false;
|
||||
if(feldId){
|
||||
const feld = document.getElementById(feldId);
|
||||
feld.closest('.login-feld').classList.add('hat-fehler');
|
||||
feld.focus();
|
||||
}
|
||||
};
|
||||
form.onsubmit = async function(ev){
|
||||
ev.preventDefault();
|
||||
errEl.textContent = '';
|
||||
const username = document.getElementById('loginUsername').value;
|
||||
errEl.hidden = true;
|
||||
const username = document.getElementById('loginUsername').value.trim();
|
||||
const password = document.getElementById('loginPassword').value;
|
||||
if(!username) return zeigeFehler('Bitte den Benutzernamen eingeben, z. B. m.mustermann.', 'loginUsername');
|
||||
if(!password) return zeigeFehler('Bitte das Passwort eingeben.', 'loginPassword');
|
||||
btn.disabled = true; btn.classList.add('laedt');
|
||||
try {
|
||||
const r = await fetch(baseUrl() + '/login', {
|
||||
method:'POST', headers:{'Content-Type':'application/json'},
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
const d = await r.json();
|
||||
if(!r.ok || !d.ok){ errEl.textContent = d.error || 'Anmeldung fehlgeschlagen.'; return; }
|
||||
if(!r.ok || !d.ok){
|
||||
zeigeFehler(d.error || 'Anmeldung fehlgeschlagen.', 'loginPassword');
|
||||
document.getElementById('loginPassword').select();
|
||||
return;
|
||||
}
|
||||
currentUser = d.user;
|
||||
document.getElementById('loginPassword').value = '';
|
||||
afterAuthenticated(onAuthenticated);
|
||||
} catch(err){
|
||||
errEl.textContent = 'Verbindung zum Server fehlgeschlagen.';
|
||||
zeigeFehler('Der Server ist nicht erreichbar. Bitte später erneut versuchen.');
|
||||
} finally {
|
||||
btn.disabled = false; btn.classList.remove('laedt');
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user