Termin aus Mail, Rollen & Rechte, Abwesenheiten, Glas-Darstellung und Pi-Betrieb

- Termin aus Mail: Mailtext oder .eml per Drag & Drop/Strg+V auf Terminplan
  oder Aufgaben, KI schlägt Aufgaben vor, Prüfung im Aufgaben-Panel; neues
  Notizfeld je Aufgabe (plan_todos.notiz)
- Terminplan: Meilensteine ohne Aufgabenbezug je in eigener Zeile,
  "+ Neu"-Menü klappt neben schmaler Seitenleiste nach rechts, Leistungsphasen
- Rollen & Rechte, Abwesenheiten-Seite, Benachrichtigungen, Rundgang
- Glas-Darstellung, Logo als Favicon
- Pi-Betrieb: PDF-Arbeit im Kindprozess, Wächter, Einrichtungs- und
  Prüfskript inkl. WLAN-Wächter

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
erikandClaude Opus 5.5 committed 2026-10-29 14:16:52 +01:00
1 parent f4fb3c550b
commit b7e0bd0d92
56 files changed
+12265 -2974

No files matched your search

+52 -4
View File
@@ -73,13 +73,44 @@
return Array.from(document.querySelectorAll('#' + containerId + ' input[type="checkbox"]:checked')).map(cb => cb.value);
}
// systemRollen: die Rollen aus „Rollen & Rechte“ (GET /rollen) für die
// Auswahl „Rolle“ und die Anzeige in der Tabelle.
let systemRollen = [];
function ladeSystemRollen(){
fetch(baseUrl() + '/rollen')
.then(r => r.json())
.then(j => {
if(!j || !j.ok) return;
systemRollen = j.rollen || [];
['newUserRole', 'editUserRole'].forEach(id => {
const sel = document.getElementById(id);
if(!sel) return;
const wert = sel.value;
sel.innerHTML = systemRollen.map(r => `<option value="${esc(r.key)}">${esc(r.name)}${nurWandtafelRolle(r.key) ? ' (nur Wandtafel)' : ''}</option>`).join('');
if(wert && systemRollen.some(r => r.key === wert)) sel.value = wert;
});
loadAndRenderUsers();
})
.catch(err => console.error('Rollen laden fehlgeschlagen:', err));
}
// nurWandtafelRolle: Rolle ohne „Anwendung nutzen“ — sieht nur die
// Wandtafel und trägt keine Fachrolle (früher das Kiosk-Konto).
function nurWandtafelRolle(key){
const r = systemRollen.find(x => x.key === key);
return !!r && !r.fest && !(r.rechte || []).includes('app');
}
function rollenName(key){
const r = systemRollen.find(x => x.key === key);
return r ? r.name : systemRolleText(key);
}
// onUserRoleChange: wird beim Wechsel der Systemrolle in einem der beiden
// Formulare aufgerufen (which = 'new'|'edit'). Ein Kiosk-Konto kann keine
// Fachrolle haben (server.js weist das sonst mit 400 zurück) — die
// Checkboxen werden deshalb schon hier abgehakt entfernt und gesperrt,
// statt den Fehler erst nach dem Absenden zu zeigen.
window.onUserRoleChange = function(which){
const istKiosk = document.getElementById(which + 'UserRole').value === 'kiosk';
const istKiosk = nurWandtafelRolle(document.getElementById(which + 'UserRole').value);
const hint = document.getElementById(which + 'UserRoleHint');
if(hint) hint.style.display = istKiosk ? '' : 'none';
document.querySelectorAll('#' + which + 'UserRoleCheckboxes input[type="checkbox"]').forEach(cb => {
@@ -100,6 +131,7 @@
// erreichbar (showTab() leitet sie um) und die Endpunkte sind serverseitig
// abgesichert. Keine Parameter, keine Rueckgabe.
window.renderAdminUsers = function(){
ladeSystemRollen();
if(!formulareVorbereitet){
renderDepartmentOptions('newUserDepartment');
renderDepartmentOptions('editUserDepartment');
@@ -151,7 +183,7 @@
<td>${esc(u.username)}</td>
<td>${esc(u.displayName)}</td>
<td>${esc(u.email || '')}</td>
<td>${esc(systemRolleText(u.role))}</td>
<td>${esc(rollenName(u.role))}</td>
<td>${deptLabel}</td>
<td>${esc(u.initials || '')}</td>
<td>${u.capacityHours ? esc(fmtCapacity(u.capacityHours)) : '<span class="hint" style="margin:0">voll</span>'}</td>
@@ -202,10 +234,14 @@
document.getElementById('newUserFirstName').value = '';
document.getElementById('newUserLastName').value = '';
document.getElementById('newUserEmail').value = '';
document.getElementById('newUserRole').value = 'member';
document.getElementById('newUserRole').value = systemRollen.some(r => r.key === 'member') ? 'member' : ((systemRollen.find(r => !r.fest) || {}).key || '');
onUserRoleChange('new');
document.getElementById('newUserDepartment').value = DEFAULT_DEPARTMENT;
document.getElementById('newUserInitials').value = '';
document.getElementById('newUserPersonId').value = '';
// Sonst übernahm das nächste Konto unbemerkt die Wochenstunden des
// zuvor abgebrochenen Dialogs (Teilzeit ohne Absicht).
document.getElementById('newUserCapacity').value = '';
document.querySelectorAll('#newUserRoleCheckboxes input[type="checkbox"]').forEach(cb => { cb.checked = false; });
document.getElementById('newUserPassword').value = '';
document.getElementById('newUserPassword').style.display = 'none';
@@ -312,7 +348,7 @@
// im PDF-Export, ohne dass es dafür irgendeinen Hinweis gäbe. Für ein
// Kiosk-Konto gilt das nicht — es soll gerade NICHT in Boards/Auslastung
// auftauchen, server.js weist eine gesetzte Fachrolle dort sogar zurück.
if(role !== 'kiosk' && (!roles || roles.length === 0)){
if(!nurWandtafelRolle(role) && (!roles || roles.length === 0)){
errEl.textContent = 'Bitte mindestens eine Rolle wählen (Planung, Projektleitung oder Bauüberwachung) — sonst erscheint der Benutzer in keinem Projektboard.';
return;
}
@@ -341,6 +377,10 @@
}
closeNewUserModal();
loadAndRenderUsers();
// Der Server hat das Konto in den Teilnehmerkreis der aktuellen
// Sitzung eingetragen — neu laden, sonst fehlt die Person hier bis
// zum nächsten Seitenaufruf in Projekten und im Assistenten.
if(res.data.sitzungGeaendert && typeof load === 'function') load(true);
if(res.data.generatedPassword){
showGeneratedPassword(username, res.data.generatedPassword);
}
@@ -415,6 +455,10 @@
}
closeEditUserModal();
loadAndRenderUsers();
// Der Server hat das Konto in den Teilnehmerkreis der aktuellen
// Sitzung eingetragen — neu laden, sonst fehlt die Person hier bis
// zum nächsten Seitenaufruf in Projekten und im Assistenten.
if(res.data.sitzungGeaendert && typeof load === 'function') load(true);
}).catch(err => { errEl.textContent = 'Verbindung zum Server fehlgeschlagen: ' + err.message; });
};
@@ -521,6 +565,10 @@
return;
}
loadAndRenderUsers();
// Der Server hat das Konto in den Teilnehmerkreis der aktuellen
// Sitzung eingetragen — neu laden, sonst fehlt die Person hier bis
// zum nächsten Seitenaufruf in Projekten und im Assistenten.
if(res.data.sitzungGeaendert && typeof load === 'function') load(true);
}).catch(err => alert('Verbindung zum Server fehlgeschlagen: ' + err.message));
};