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:
erikandClaude Opus 5.5 committed 2026-10-22 09:25:33 +02:00
1 parent 93505e0e48
commit f4fb3c550b
13 files changed
+1214 -116

No files matched your search

+44
View File
@@ -104,6 +104,50 @@ function initialsForName(name){
return words.slice(0, 4).map(w => w[0]).join('').toUpperCase();
}
// EMAIL_DOMAIN: Domain der Firmen-Adressen, Grundlage von emailVorschlag().
const EMAIL_DOMAIN = 'rauschenberg-ing.de';
// emailVorschlag: schlägt aus einem Namen die Firmen-Adresse nach dem Schema
// „Anfangsbuchstabe Vorname . Nachname @rauschenberg-ing.de" vor, klein
// geschrieben, Umlaute umschrieben (ä→ae, ß→ss), übrige Sonderzeichen
// entfernt: "Miriam Körner" → "m.koerner@rauschenberg-ing.de". Vorname ist
// das erste, Nachname das letzte Wort. Nur ein Vorschlag — bleibt
// überschreibbar. Parameter: name (String) — oder, wenn `nachname` gegeben
// ist, Vorname und Nachname getrennt (dann zählt der ganze Nachname, z. B.
// "von Plum" → "vonplum"). Rückgabe: Adresse oder ''.
function emailVorschlag(name, nachname){
if(nachname !== undefined){
const v = String(name || '').trim(), n = String(nachname || '').trim();
if(!v || !n) return emailVorschlag((v + ' ' + n).trim());
const l = emailVorschlag(v.split(/\s+/)[0] + ' ' + n.replace(/\s+/g, ''));
return l;
}
const teil = s => String(s).toLowerCase()
.replace(/ä/g,'ae').replace(/ö/g,'oe').replace(/ü/g,'ue').replace(/ß/g,'ss')
.normalize('NFD').replace(/[\u0300-\u036f]/g,'')
.replace(/[^a-z0-9-]/g,'');
const woerter = String(name || '').trim().split(/\s+/).map(teil).filter(Boolean);
if(woerter.length === 0) return '';
const lokal = woerter.length === 1 ? woerter[0] : woerter[0][0] + '.' + woerter[woerter.length - 1];
return lokal + '@' + EMAIL_DOMAIN;
}
// benutzernameVorschlag: Benutzername eines neuen Kontos = Anfang der
// E-Mail-Adresse, also "Anfangsbuchstabe Vorname . Nachname" in der
// Schreibweise von emailVorschlag() ("Miriam Körner" → "m.koerner").
// Angemeldet wird mit genau diesem Namen. Rückgabe: String oder ''.
function benutzernameVorschlag(vorname, nachname){
const adresse = emailVorschlag(vorname, nachname);
return adresse ? adresse.slice(0, adresse.indexOf('@')) : '';
}
// grossAnfang: erster Buchstabe groß, Leerraum zusammengefasst — für Vor-
// und Nachnamen beim Speichern ("miriam" → "Miriam").
function grossAnfang(s){
s = String(s || '').trim().replace(/\s+/g, ' ');
return s ? s.charAt(0).toUpperCase() + s.slice(1) : '';
}
// formatWeekDate: wandelt ein ISO-Datum (YYYY-MM-DD) in deutsches Format DD.MM.YYYY um
// Parameter: dateString (String). Rückgabe: formatierter String oder leer.
function formatWeekDate(dateString){