// -----------------------------
// Projektübersicht — Personen-Ansicht (global, NUR LESEND)
// -----------------------------
// Diese Datei rendert den Personen-Tab (Tab "Projektbeteiligte" in `App.html`)
// als reine Anzeige. Seit der Ablösung von people/person_roles/person_absences
// (server.js) werden Projektbeteiligte ausschließlich in der zentralen
// Benutzerverwaltung (Bereich „Admin“) gepflegt; Anlegen/Bearbeiten/Löschen/
// Drag-and-Drop-Sortierung/Abwesenheits-Formulare gibt es hier bewusst nicht
// mehr. EINE Ausnahme ist geblieben bzw. wieder dazugekommen: der
// Teilnehmerkreis der aktuellen Sitzung (`currentWeek().people`). Er entsteht
// nur beim Anlegen einer neuen Sitzung und wird beim Duplizieren
// unverändert übernommen — ohne die Schaltflächen hier ("Zur Sitzung
// hinzufügen"/"Aus Sitzung entfernen") wäre ein neu angelegter Benutzer in
// keiner laufenden Sitzung zuordenbar, weder im Board noch in der
// Auslastung noch in der Anwesenheit. Die Personendaten selbst kommen unverändert über `db.globalPeople`
// (befüllt via `GET /load-all`, serverseitig jetzt aus `users`/`user_roles`/
// `user_absences` zusammengesetzt, firmenweit ohne Abteilungstrennung). Sie
// wird von `App.html` nach `js/board/board.js` (vor `js/projects/projects.js`)
// geladen und stellt die globale Funktion `renderPeople()` bereit, die u.a.
// von `js/tabs-auth.js` (`showTab()`) und `js/init.js` aufgerufen wird.
// renderPeople: baut die schreibgeschützte Liste (`.people-readonly`, eine
// Bubble-Card mit den Personen als Listenzeilen) der Personen für den
// Personen-Tab auf (Zeilen mit Kürzel, Vollname, E-Mail, Rollen,
// aufklappbaren Abwesenheiten). Kein Formular, keine Lösch-/
// Bearbeiten-Aktionen, kein Drag-Handle. Zeigt zusätzlich einen Hinweis auf
// die zentrale Benutzerverwaltung (Link nur für Admins sichtbar). Keine
// Parameter, keine Rückgabe.
function renderPeople(){
const people = db.globalPeople || [];
const isAdmin = typeof currentUser !== 'undefined' && !!currentUser && currentUser.role === 'admin';
const w = currentWeek();
const inWeek = new Set((w && Array.isArray(w.people) ? w.people : []).map(p => p.id));
const locked = !w || isWeekLocked(w);
let html = `
Projektbeteiligte werden zentral in der Benutzerverwaltung gepflegt.`
+ (isAdmin ? ` Zur Benutzerverwaltung` : '')
+ `
`;
html += `
Wer in der aktuellen Sitzung`
+ (w ? ` ${esc(w.label)}` : '')
+ ` mitwirkt, erscheint als Spalte im Projektboard, in der Auslastung und in der Anwesenheit.`
+ (locked ? ` Die Sitzung ist gesperrt — der Kreis kann nicht geändert werden.` : '')
+ `
`;
if(people.length === 0){
html += '
Keine Personen vorhanden.
';
document.getElementById('peopleList').innerHTML = html;
return;
}
html += `
`;
people.forEach(p => {
const isOpen = personAbsenceOpen[p.id];
const roles = Array.isArray(p.role) ? p.role : [];
const dabei = inWeek.has(p.id);
// Die Schaltfläche liegt IM Kopfbereich, der das Aufklappen auslöst —
// stopPropagation verhindert, dass ein Klick darauf zusätzlich die
// Abwesenheiten auf- oder zuklappt.
const weekAction = locked ? '' : (dabei
? ``
: ``);
html += `
`;
html += `
${icon('chevronRight')}${esc(p.name)}${esc(p.fullName || '')}${esc(p.email || '')}${roles.length ? roles.map(r => esc(r)).join(', ') : 'Keine Rolle'}${dabei ? 'in dieser Sitzung' : 'nicht in dieser Sitzung'}
${weekAction}
`;
document.getElementById('peopleList').innerHTML = html;
}
// personAbsenceOpen: UI-Zustand, welche Personen-Karte ihre Abwesenheiten
// aufgeklappt darstellt (Key = Personen-ID, Wert = boolean). Reine Anzeige-
// Steuerung, wird nicht persistiert.
let personAbsenceOpen = {};
// togglePersonAbsences: klappt die Abwesenheits-Liste einer Person auf bzw. zu
// (Umschalten des Eintrags in `personAbsenceOpen`) und rendert neu.
function togglePersonAbsences(personId){
personAbsenceOpen[personId] = !personAbsenceOpen[personId];
renderPeople();
}
// addPersonToCurrentWeek: nimmt eine Person aus der globalen Liste in den
// Teilnehmerkreis der aktuellen Sitzung auf. Nötig, weil der Kreis sonst nur
// beim Anlegen einer neuen Sitzung entsteht: Ein später angelegter Benutzer
// tauchte in laufenden (und in allen daraus duplizierten) Sitzungen nirgends
// auf. Geschrieben wird über den normalen save()-Weg — `week.people` ist Teil
// des Wochen-Datensatzes, den /save-week ohnehin schreibt.
// Parameter: personId (String). Keine Rückgabe.
function addPersonToCurrentWeek(personId){
if(guardWeekLocked()) return;
const w = currentWeek();
if(!w) return;
if(!Array.isArray(w.people)) w.people = [];
if(w.people.some(p => p.id === personId)) return;
const person = (db.globalPeople || []).find(p => p.id === personId);
if(!person){ alert('Diese Person ist nicht mehr in der Benutzerverwaltung vorhanden.'); return; }
// Tiefe Kopie: Der Teilnehmerkreis friert Name und Rollen zum Zeitpunkt der
// Sitzung ein (genauso wie newWeek() es tut) und darf sich später nicht
// rückwirkend mit der globalen Liste ändern.
w.people.push(JSON.parse(JSON.stringify(person)));
save();
renderAll();
}
// removePersonFromCurrentWeek: nimmt eine Person wieder aus dem
// Teilnehmerkreis der aktuellen Sitzung heraus. Verweigert wird das, solange
// sie noch einem Projekt dieser Sitzung zugeordnet ist — sonst bliebe eine
// Zuordnung ohne zugehörige Spalte zurück. Parameter: personId (String).
function removePersonFromCurrentWeek(personId){
if(guardWeekLocked()) return;
const w = currentWeek();
if(!w || !Array.isArray(w.people)) return;
const person = w.people.find(p => p.id === personId);
if(!person) return;
const zugeordnet = (w.projects || []).filter(proj => {
const assign = proj.assign || {};
return Object.keys(assign).some(key => key === personId || key.indexOf(personId + '|') === 0);
});
if(zugeordnet.length > 0){
alert('"' + (person.fullName || person.name) + '" ist in dieser Sitzung noch ' + zugeordnet.length
+ ' Projekt(en) zugeordnet:\n\n' + zugeordnet.map(p => '· ' + (p.name || p.nr)).join('\n')
+ '\n\nBitte zuerst die Zuordnungen im Projektboard entfernen.');
return;
}
w.people = w.people.filter(p => p.id !== personId);
if(w.attendance) delete w.attendance[personId];
save();
renderAll();
}