// ----------------------------- // Projektübersicht — Board (Listenansicht) // ----------------------------- // Diese Datei rendert das Projektboard (Tab "Projektboard" in `App.html`) als // schlanke, aufklappbare Liste statt der früheren breiten Matrix (eine Spalte // je Person/Leistungsphase): jede Zeile zeigt auf einen Blick Kürzel-Avatare // der zugeordneten Personen, die aktuelle Leistungsphase samt Prozentwert und // eine einzeilige Status-Vorschau; ein Klick auf die Zeile klappt darunter das // Detailpanel auf (voller Status-Editor, vollständige Rollen-Zuordnung, alle // LPH-Balken). Sie wird von `App.html` nach `js/weeks.js` (vor // `js/people/people.js`) geladen und stellt globale Funktionen wie // `renderBoard()`, `toggleBoardRow()`, // und `editLph()` bereit. Sie nutzt `currentWeek()`/ // `isWeekLocked()` aus `js/weeks.js`, `lpActiveList()` aus `js/load/load.js`, // `assignKey()`, `statusFocus()`, `sanitizeStatusHtml()` und // `getProgressColor()` aus `js/utils.js` sowie `openProjectPlan()` aus // `js/gantt.js` — navigiert bewusst im selben Tab (kein window.open() mehr), // ein Klick auf den Rest der Zeile klappt stattdessen nur das Detailpanel // auf/zu (toggleBoardRow()). editLph() ruft zusätzlich // renderWochenbericht() (js/wochenbericht/wochenbericht.js) auf, da dessen // Karten dieselben renderBoardDrawer()-Bedienelemente nutzen, aber in einem // eigenen DOM-Container (#wochenberichtContent) liegen. Die Zuordnung der // Beteiligten (p.assign) ist hier und im Wochenbericht nur noch Anzeige; // editierbar ist sie allein über planCycleAssign() in // js/plan/plan-settings.js — siehe renderBoardDrawer()s Kopfkommentar. // boardRowOpen: UI-Zustand, welche Projekt-Zeile ihr Detailpanel aufgeklappt // darstellt (Key = Projekt-ID, Wert = boolean). Reine Anzeige-Steuerung, lebt // außerhalb von renderBoard() und übersteht daher dessen Neuaufbauten (z. B. // nach einer Zuordnungs- oder Prozent-Änderung im offenen Panel). Nicht // persistiert — jede Sitzung startet mit eingeklappten Zeilen. let boardRowOpen = {}; // berichtDisclosureState: merkt sich, ob die Anwesenheit im Wochenbericht // (renderAttendance(), js/wochenbericht/wochenbericht.js) gerade auf- oder // zugeklappt ist. Ursprünglich auch für Leistungsphasen/Beteiligte gedacht // (damals beide ebenfalls Aufklapper, Fokus-Modus "C"); seit Eriks // Rückmeldung zum Entwurfsvergleich ("Aktenregal") sind beide wieder // dauerhaft sichtbar (renderBoardDrawer()s dichte Fassung weiter unten) und // brauchen dieses Gedächtnis nicht mehr — nur die Anwesenheit bleibt ein // Aufklapper. Ohne dieses Gedächtnis klappte sie bei jedem Neu-Rendern wieder // auf ihren Vorgabewert zurück — und JEDE Änderung im Bericht (eine // Zuordnung, ein LPH-Wert, ein Schritt im Vorwoche-Scrollrad) baut den ganzen // Bericht neu auf. 'toggle' bubbelt nicht, ein Lauscher in der Capture-Phase // sieht das Ereignis trotzdem auf dem Weg nach unten (gleiches Muster wie der // scroll-Lauscher der Wandtafel). const berichtDisclosureState = { anwesenheit: false }; document.addEventListener('toggle', function(e){ const key = e.target && e.target.dataset && e.target.dataset.berichtAbschnitt; if(key) berichtDisclosureState[key] = e.target.open; }, true); // toggleBoardRow: klappt das Detailpanel einer Projekt-Zeile auf bzw. zu. // Parameter: pid (Projekt-ID). Keine Rückgabe. function toggleBoardRow(pid){ boardRowOpen[pid] = !boardRowOpen[pid]; renderBoard(); // Der Wochenbericht und die Wandtafel rendern dieselben Projektzeilen in // einem eigenen Container. Ohne diesen Aufruf merkten sie sich den Klick // zwar, zeigten die Zeile aber erst nach einem Tab-Wechsel auf- bzw. // zugeklappt. if(typeof renderWochenbericht === 'function') renderWochenbericht(); if(typeof renderWandboard === 'function') renderWandboard(); } // statusPreviewText: liefert eine einzeilige Klartext-Vorschau eines // Status-HTML-Werts (Tags entfernt, überflüssiger Leerraum/Zeilenumbrüche zu // einem Leerzeichen zusammengefasst) für die eingeklappte Zeile — die volle // Formatierung bleibt dem aufgeklappten Editor vorbehalten. Parameter: html // (String). Rückgabe: String. function statusPreviewText(html){ if(!html) return ''; const tmp = document.createElement('div'); // Bereinigen, BEVOR der Wert ins DOM geht: Auch ein nicht eingehängtes // Element lädt Ressourcen und führt deren Fehler-Handler aus, ungefiltertes // innerHTML wäre hier also ein Einfallstor. tmp.innerHTML = sanitizeStatusHtml(html); // Zeilen- und Absatzgrenzen zuerst in Leerzeichen überführen: textContent // ignoriert sie, sonst klebte das letzte Wort einer Zeile am ersten der // nächsten ("… KW 40.Freigabe steht aus"). tmp.querySelectorAll('br').forEach(el => el.replaceWith(document.createTextNode(' '))); tmp.querySelectorAll('p, div').forEach(el => el.append(document.createTextNode(' '))); return (tmp.textContent || tmp.innerText || '').replace(/\s+/g, ' ').trim(); } // HINWEIS: statusBlur() ist entfallen. Seit der Kommentar-Editor ein //