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

+74
View File
@@ -61,6 +61,7 @@ const ADMIN_SECTIONS = [
{ key: 'vorlagen', title: 'Aufgaben-Vorlagen' },
{ key: 'planchecker', title: 'Plan-Checker-Merkmale' },
{ key: 'betrieb', title: 'Betrieb & Sicherheit' },
{ key: 'papierkorb', title: 'Papierkorb' },
{ key: 'datenbank', title: 'Datenbank & Sitzungen' },
];
@@ -136,6 +137,9 @@ function showAdminSection(key){
// aendert sich auch ausserhalb dieses Browsers, und die uebrigen Masken
// rechnen mit ihr (Wochenstunden -> Auslastung).
if(key === 'benutzer' && typeof renderAdminUsers === 'function') renderAdminUsers();
// Der Papierkorb ändert sich durch jedes Löschen irgendwo im Büro — ebenso
// frisch holen.
if(key === 'papierkorb') loadAdminPapierkorb();
try {
history.replaceState(null, '', location.pathname + location.search + '#' + key);
@@ -931,3 +935,73 @@ function loadHolidaysIntoState(){
})
.catch(err => console.error('Feiertage neu laden fehlgeschlagen:', err));
}
// -----------------------------
// Papierkorb gelöschter Projekte
// -----------------------------
// Ein aus der Projektliste gelöschtes Projekt legt der Server beim Speichern
// der Sitzung als Schnappschuss in deleted_projects ab (server.js,
// /save-week). Hier lässt es sich 30 Tage lang in die aktuelle Sitzung
// zurückholen oder vorzeitig endgültig entfernen.
// loadAdminPapierkorb: holt die Liste (GET /admin/deleted-projects) und
// rendert sie in #adminPapierkorbList.
async function loadAdminPapierkorb(){
const el = document.getElementById('adminPapierkorbList');
if(!el) return;
try {
const r = await fetch(baseUrl() + '/admin/deleted-projects');
const data = await r.json().catch(() => ({}));
if(!r.ok || !data.ok) throw new Error(data.error || ('HTTP ' + r.status));
if(!data.projekte.length){
el.innerHTML = '<p class="hint">Der Papierkorb ist leer.</p>';
return;
}
const tagMs = 24 * 60 * 60 * 1000;
el.innerHTML = data.projekte.map(p => {
const bez = ((p.nr ? p.nr + ' ' : '') + (p.name || '')).trim() || '(ohne Bezeichnung)';
const am = new Date(p.deletedAt).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' });
const rest = Math.max(0, Math.ceil((Date.parse(p.endgueltigAm) - Date.now()) / tagMs));
return `<div class="template-admin-row">
<span class="template-admin-title">${esc(bez)}
<span class="hint" style="margin:0"> · gelöscht ${esc(am)}${p.deletedBy ? ' von ' + esc(p.deletedBy) : ''} · aus ${esc(p.weekLabel)}</span>
</span>
<span class="sitzung-chip">noch ${rest} ${rest === 1 ? 'Tag' : 'Tage'}</span>
<button class="secondary small" onclick="adminProjektWiederherstellen('${esc(p.id)}')">Wiederherstellen</button>
<button class="danger small" onclick="adminProjektEndgueltigLoeschen('${esc(p.id)}')" title="Sofort endgültig löschen">×</button>
</div>`;
}).join('');
} catch(err){
el.innerHTML = '<p class="hint">Papierkorb konnte nicht geladen werden: ' + esc(err.message) + '</p>';
}
}
// adminProjektWiederherstellen: holt ein Projekt in die aktuelle Sitzung
// zurück. Danach wird neu geladen, damit die Projektliste das Projekt zeigt
// und die lokale Revision der Sitzung zum Server passt (sonst meldete das
// nächste Speichern einen Konflikt).
async function adminProjektWiederherstellen(id){
try {
const r = await fetch(baseUrl() + '/admin/deleted-projects/' + encodeURIComponent(id) + '/restore', { method: 'POST' });
const data = await r.json().catch(() => ({}));
if(!r.ok || !data.ok) throw new Error(data.error || ('HTTP ' + r.status));
load();
loadAdminPapierkorb();
alert('Projekt wiederhergestellt in „' + data.weekLabel + '".');
} catch(err){
alert('Wiederherstellen fehlgeschlagen: ' + err.message);
}
}
// adminProjektEndgueltigLoeschen: entfernt einen Papierkorb-Eintrag sofort.
async function adminProjektEndgueltigLoeschen(id){
if(!confirm('Projekt endgültig löschen? Das lässt sich nicht rückgängig machen.')) return;
try {
const r = await fetch(baseUrl() + '/admin/deleted-projects/' + encodeURIComponent(id), { method: 'DELETE' });
const data = await r.json().catch(() => ({}));
if(!r.ok || !data.ok) throw new Error(data.error || ('HTTP ' + r.status));
loadAdminPapierkorb();
} catch(err){
alert('Löschen fehlgeschlagen: ' + err.message);
}
}