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

+93 -39
View File
@@ -34,12 +34,13 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Projektübersicht</title>
<link rel="stylesheet" href="/styles.css?v=64">
<link rel="stylesheet" href="/styles.css?v=67">
<link rel="stylesheet" href="/plan-styles.css?v=19">
<!-- Fredoka/Nunito Sans: nur für die Wandtafel (js/wand/wand.js), siehe ihren
Dateikopf — fällt ohne Internetzugang automatisch auf die System-Schrift
zurück (font-family-Stack in styles.css), bricht also nichts offline. -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fredoka:wght@500;600;700&family=Nunito+Sans:wght@400;600;700;800&display=swap">
Dateikopf; Outfit: nur für die Kalenderwoche der Login-Seite. Alle fallen
ohne Internetzugang automatisch auf die System-Schrift zurück
(font-family-Stack in styles.css), brechen also nichts offline. -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fredoka:wght@500;600;700&family=Outfit:wght@300;500&family=Nunito+Sans:wght@400;600;700;800&display=swap">
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.10/pdfmake.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.10/vfs_fonts.js"></script>
</head>
@@ -186,7 +187,7 @@
js/pdf.js. Damit fällt die einzige gewerkeübergreifende
Sitzungsübersicht als PDF weg; die einzelnen Gewerke-Berichte
im Wochenbericht bleiben die Ablage. -->
<button class="menu-item sidebar-subitem" onclick="openNewProjWizard()">+ Neues Projekt</button>
<button class="menu-item sidebar-subitem nur-projektleitung" onclick="openNewProjWizard()">+ Neues Projekt</button>
<button class="menu-item sidebar-subitem" onclick="showTab('wand')">Wandtafel</button>
</div>
@@ -212,6 +213,7 @@
<button class="menu-item sidebar-subitem" data-admin-section="vorlagen" onclick="showAdminSection('vorlagen')">Aufgaben-Vorlagen</button>
<button class="menu-item sidebar-subitem" data-admin-section="planchecker" onclick="showAdminSection('planchecker')">Plan-Checker-Merkmale</button>
<button class="menu-item sidebar-subitem" data-admin-section="betrieb" onclick="showAdminSection('betrieb')">Betrieb &amp; Sicherheit</button>
<button class="menu-item sidebar-subitem" data-admin-section="papierkorb" onclick="showAdminSection('papierkorb')">Papierkorb</button>
<button class="menu-item sidebar-subitem" data-admin-section="datenbank" onclick="showAdminSection('datenbank')">Datenbank &amp; Sitzungen</button>
</div>
</div>
@@ -239,7 +241,7 @@
zusätzlich stehen, weil er dort dieselbe Maske öffnet. -->
<div class="bericht-titelzeile">
<div class="section-title" style="margin:0">Projekte</div>
<button type="button" class="secondary small" onclick="openNewProjWizard()">+ Projekt</button>
<button type="button" class="secondary small nur-projektleitung" onclick="openNewProjWizard()">+ Projekt</button>
</div>
<!-- Die Farbe der Kreise richtet sich nach der ROLLE (siehe
.board-avatar.role-* in styles.css), die Füllung nach der Art der
@@ -715,7 +717,7 @@
<thead>
<tr>
<th>Benutzername</th>
<th>Anzeigename</th>
<th>Name</th>
<th>E-Mail</th>
<th>Rolle</th>
<th>Abteilung</th>
@@ -731,6 +733,15 @@
</div>
</section>
<section class="settings-card settings-card-wide" id="adminPapierkorbCard" data-admin-section="papierkorb" style="display:none">
<h3 class="settings-card-title">Papierkorb <span class="settings-scope">nur Administratoren</span></h3>
<p class="hint">Aus der Projektliste gelöschte Projekte — samt Terminplan, Beteiligten und
Leistungsphasen. Sie bleiben 30 Tage hier und lassen sich in die aktuelle Sitzung
zurückholen; danach werden sie endgültig entfernt. Dateien und Protokolle hängen an der
Projektnummer und sind nach dem Wiederherstellen ebenfalls wieder da.</p>
<div id="adminPapierkorbList"><p class="hint">Wird geladen…</p></div>
</section>
<section class="settings-card" id="adminDatenbankCard" data-admin-section="datenbank" style="display:none">
<h3 class="settings-card-title">Datenbank &amp; Sitzungen <span class="settings-scope">nur Administratoren</span></h3>
@@ -1536,22 +1547,30 @@
<div class="modal-bg" id="newUserModal" style="display:none">
<div class="modal">
<h2>Neuer Benutzer</h2>
<div class="form-row">
<label>Benutzername</label>
<input type="text" id="newUserUsername" placeholder="z. B. mmustermann" autocomplete="off">
<div class="form-row-pair">
<div class="form-row">
<label for="newUserFirstName">Vorname</label>
<input type="text" id="newUserFirstName" placeholder="z. B. Max" autocomplete="off" oninput="onNewUserNameInput()">
</div>
<div class="form-row">
<label for="newUserLastName">Nachname</label>
<input type="text" id="newUserLastName" placeholder="z. B. Mustermann" autocomplete="off" oninput="onNewUserNameInput()">
</div>
</div>
<div class="form-row">
<label>Anzeigename</label>
<input type="text" id="newUserDisplayName" placeholder="z. B. Max Mustermann" oninput="onNewUserDisplayNameInput()">
<label for="newUserEmail">E-Mail</label>
<input type="email" id="newUserEmail" placeholder="v.nachname@rauschenberg-ing.de">
<span class="hint" style="margin:4px 0 0">Der Anfang der Adresse ist zugleich der Benutzername für die Anmeldung.</span>
</div>
<div class="form-row">
<label>E-Mail</label>
<input type="email" id="newUserEmail" placeholder="max.mustermann@beispiel.de">
<label for="newUserUsername">Benutzername</label>
<input type="text" id="newUserUsername" placeholder="Anfang der E-Mail, z. B. m.mustermann" autocomplete="off">
</div>
<div class="form-row">
<label>Rolle</label>
<select id="newUserRole" onchange="onUserRoleChange('new')">
<option value="member">Mitglied</option>
<option value="projektleiter">Projektleiter (darf Projekte anlegen und löschen)</option>
<option value="admin">Administrator</option>
<option value="kiosk">Kiosk (Wandtafel-Konto)</option>
</select>
@@ -1563,7 +1582,7 @@
</div>
<div class="form-row">
<label>Kürzel</label>
<input type="text" id="newUserInitials" placeholder="wird aus dem Anzeigenamen vorgeschlagen" maxlength="4">
<input type="text" id="newUserInitials" placeholder="wird aus dem Namen vorgeschlagen" maxlength="4">
<p class="hint">Kurzform fürs Projektboard (2-4 Zeichen), wird vorgeschlagen und kann angepasst werden.</p>
</div>
<div class="form-row">
@@ -1573,7 +1592,7 @@
</div>
<div class="form-row">
<label>Person-ID</label>
<input type="text" id="newUserPersonId" placeholder="wird aus dem Anzeigenamen vorgeschlagen">
<input type="text" id="newUserPersonId" placeholder="wird aus dem Kürzel gebildet, z. B. p_mm">
<p class="hint">Verknüpft den Account mit einem Eintrag in der Projektbeteiligten-Liste (personIdForName). Bei Kollisionen anpassen.</p>
</div>
<div class="form-row">
@@ -1601,18 +1620,25 @@
<div class="modal">
<h2>Benutzer bearbeiten</h2>
<p class="hint" id="editUserUsernameLabel" style="margin-bottom:10px"></p>
<div class="form-row">
<label>Anzeigename</label>
<input type="text" id="editUserDisplayName">
<div class="form-row-pair">
<div class="form-row">
<label for="editUserFirstName">Vorname</label>
<input type="text" id="editUserFirstName" oninput="onEditUserNameInput()">
</div>
<div class="form-row">
<label for="editUserLastName">Nachname</label>
<input type="text" id="editUserLastName" oninput="onEditUserNameInput()">
</div>
</div>
<div class="form-row">
<label>E-Mail</label>
<input type="email" id="editUserEmail">
<input type="email" id="editUserEmail" placeholder="v.nachname@rauschenberg-ing.de">
</div>
<div class="form-row">
<label>Rolle</label>
<select id="editUserRole" onchange="onUserRoleChange('edit')">
<option value="member">Mitglied</option>
<option value="projektleiter">Projektleiter (darf Projekte anlegen und löschen)</option>
<option value="admin">Administrator</option>
<option value="kiosk">Kiosk (Wandtafel-Konto)</option>
</select>
@@ -1748,41 +1774,69 @@
</div>
</div>
<!-- Login-Overlay: blockiert die gesamte App ohne gültige Session (siehe js/auth.js) -->
<div id="loginOverlay" style="display:none;" class="modal-bg">
<div class="modal">
<h2>Anmelden</h2>
<form id="loginForm">
<div class="form-row"><label>Benutzername</label><input type="text" id="loginUsername" autocomplete="username" required></div>
<div class="form-row"><label>Passwort</label><input type="password" id="loginPassword" autocomplete="current-password" required></div>
<div id="loginError" style="color:#c00;"></div>
<div class="toolbar"><button type="submit">Anmelden</button></div>
</form>
</div>
<!-- Login-Seite: blockiert die gesamte App ohne gültige Session (siehe
js/auth.js). Design "Kalenderwoche" (Eriks Wahl B aus dem Vergleich der
Login-Entwürfe): links Logo und Formular, rechts die laufende
Kalenderwoche als große Zahl mit ihren Arbeitstagen — die App denkt in
Sitzungen je KW. Gefüllt von renderLoginWoche(). Das Logo liegt als
blasses Wasserzeichen über die ganze Seite (Variante E aus dem
Logo-Vergleich), ein kleines Logo gibt es bewusst nicht. -->
<div id="loginOverlay" class="login-seite" style="display:none;">
<div class="login-wz-rahmen" aria-hidden="true"><img class="login-wasserzeichen" src="/logo.png" alt=""></div>
<main class="login-raster" aria-label="Anmeldung bei der Projektübersicht">
<section class="login-links">
<h1 class="login-titel">Projektübersicht</h1>
<p class="login-unter">Melde dich mit dem Anfang deiner E-Mail-Adresse an.</p>
<form id="loginForm" class="login-form" novalidate>
<div class="login-feld">
<label for="loginUsername">Benutzername</label>
<input type="text" id="loginUsername" name="username" autocomplete="username" placeholder="z. B. m.mustermann" spellcheck="false" autocapitalize="off">
</div>
<div class="login-feld">
<label for="loginPassword">Passwort</label>
<div class="login-pw">
<input type="password" id="loginPassword" name="password" autocomplete="current-password">
<button type="button" id="loginPwToggle" class="login-pw-toggle" aria-pressed="false" aria-controls="loginPassword">Anzeigen</button>
</div>
<p id="loginCaps" class="login-caps" hidden>Die Feststelltaste ist aktiv.</p>
</div>
<p id="loginError" class="login-fehler" role="alert" hidden></p>
<button type="submit" id="loginSubmit" class="login-btn"><span class="login-spinner" aria-hidden="true"></span><span class="login-btn-text">Anmelden</span></button>
</form>
<p class="login-hilfe">Passwort vergessen? Ein Administrator setzt es dir zurück.</p>
</section>
<section class="login-woche" aria-label="Laufende Kalenderwoche">
<span class="login-kw-label">Kalenderwoche</span>
<span class="login-kw-zahl" id="loginKwZahl"></span>
<span class="login-kw-jahr" id="loginKwJahr"></span>
<ol class="login-tage" id="loginTage"></ol>
<p class="login-datum"><span id="loginDatum"></span>. Die Sitzung dieser Woche ist geöffnet.</p>
</section>
</main>
</div>
<script src="/js/globals.js?v=7"></script>
<script src="/js/utils.js?v=18"></script>
<script src="/js/utils.js?v=19"></script>
<script src="/js/icons.js?v=2"></script>
<script src="/js/ui.js?v=9"></script>
<script src="/js/date-picker.js?v=5"></script>
<script src="/js/server.js?v=6"></script>
<script src="/js/auth.js?v=4"></script>
<script src="/js/auth.js?v=6"></script>
<script src="/js/store.js?v=10"></script>
<script src="/js/weeks.js?v=11"></script>
<script src="/js/board/board.js?v=30"></script>
<script src="/js/board/board.js?v=31"></script>
<script src="/js/wochenbericht/wochenbericht.js?v=20"></script>
<script src="/js/people/people.js?v=7"></script>
<script src="/js/planung/planung.js?v=1"></script>
<script src="/js/planchecker/planchecker.js?v=1"></script>
<script src="/js/projects/projects.js?v=7"></script>
<script src="/js/projects/projects.js?v=8"></script>
<script src="/js/load/load.js?v=18"></script>
<!-- auftrag.js: Auftragsstatus und Dialog "Beauftragung". Nach load.js, weil
der Neues-Projekt-Dialog dort die Statusauswahl mit AUFTRAGSSTATUS füllt. -->
<script src="/js/auftrag.js?v=1"></script>
<!-- Assistent "Neues Projekt": braucht auftrag.js (Auftragsstatus) und
load.js (faktorFormat), steht deshalb dahinter. -->
<script src="/js/projekt-wizard.js?v=1"></script>
<script src="/js/projekt-wizard.js?v=2"></script>
<script src="/js/dashboard/dashboard.js?v=5"></script>
<!-- wand.js braucht berechneAuslastung()/loadAmpelColor()/kapazitaetsFaktor()/
stundenFormat() (load.js), classifyProjectChange()/findPreviousWeek()
@@ -1791,9 +1845,9 @@
bereits vorher, die Reihenfolge hier ist also nur Zuordnung zum
Tab-Ordner, keine neue Abhängigkeit nach vorn. -->
<script src="/js/wand/wand.js?v=12"></script>
<script src="/js/settings/settings.js?v=9"></script>
<script src="/js/admin/admin.js?v=11"></script>
<script src="/js/admin-users.js?v=4"></script>
<script src="/js/settings/settings.js?v=10"></script>
<script src="/js/admin/admin.js?v=12"></script>
<script src="/js/admin-users.js?v=7"></script>
<script src="/js/gantt.js?v=7"></script>
<!-- Module des Projektbereichs (#plan). Sie liefen frueher nur in
project-plan.html; seit der Projektbereich ein Bereich dieser Seite ist,