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
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user