// ----------------------------- // Projektbereich — Projektangaben (Anzeige und Stammdaten) // ----------------------------- // Dieses Modul füllt die Projektangaben des Projektbereichs. Es unterscheidet // dabei zwei Arten von Angaben, und das ist der Kern der Sache: // // * NUR ANZEIGE — Kommentar der Sitzung (#planStatusEdit) und Fortschritt // je Leistungsphase (#planLphList). Beide entstehen in der wöchentlichen // Besprechung und werden ausschließlich im Wochenbericht erfasst. Neben // jeder von ihnen steht ein Verweis dorthin (setzeBerichtVerweis()), der // hinüberwechselt UND dort dieses Projekt einstellt. // * STAMMDATEN, hier pflegbar — Gruppe (#planGroupSelect) und amtierende // Leistungsphase (#planActiveLpSelect), beide im Bereich "Einstellungen". // // Warum: Dieselben Angaben waren zuvor an DREI Stellen bearbeitbar — // Projektboard, Wochenbericht und hier. Niemand wusste, welche davon die // richtige ist, und zwei Personen konnten dasselbe Feld an verschiedenen // Stellen ändern. Seitdem hat jede Angabe genau einen Ort, an dem sie // entsteht, und überall sonst steht sie zum Lesen samt Weg dorthin. Die // früheren Schreibfunktionen dieses Moduls (planStatusBlur(), setPlanLph(), // setPlanAssign()) sind damit entfallen. // // Die Zuordnung der Beteiligten (p.assign) gehörte früher ebenfalls zu // dieser "nur Anzeige"-Gruppe — auf Eriks ausdrücklichen Wunsch ist sie // seitdem umgekehrt: kein reines Anzeigefeld mehr, sondern ein echtes // Stammdatum, und zwar NUR HIER im Projektbereich editierbar (Bereich // "Einstellungen", gerendert von js/plan/plan-settings.js — // #planAssignMatrix liegt dort im Markup, nicht im Dashboard). Der // Wochenbericht zeigt die Zuordnung seitdem nur noch zum Lesen, mit einem // Verweis hierher. // // BEWUSST NICHT hier: Projektnummer, Bezeichnung, Schlagwörter, Honorar, // beauftragte Leistungsphasen und die Beteiligten-Matrix. Sie sind // Stammdaten und werden von js/plan/plan-settings.js gerendert (Honorar und // beauftragte Phasen geschrieben von setHonorarBetrag()/toggleBeauftragteLp() // aus js/plan/plan-honorar.js, die Beteiligten-Matrix von planCycleAssign()). // // renderPlanInfo() wird von // renderPlanPage() (plan-state.js) nach jedem Laden aufgerufen; Änderungen // speichern direkt über app.savePlan() und rendern danach nur das Infopanel // neu (das GANTT bleibt unberührt). // // Läuft wie alle plan-Module in einem IIFE; Zustand: window.PlanApp // (plan-state.js). (function(){ const app = window.PlanApp; // PLAN_ACTIVE_LPS: Phasen, die als amtierende Leistungsphase wählbar sind — // alle echten Leistungsphasen (inkl. LPH 9), aber ohne die Sammelkategorie // "W+M". Entspricht AUFWAND_LPS in js/load/load.js, hier separat definiert, // weil load.js auf der Projektplan-Seite bewusst nicht geladen wird (siehe // Kopfkommentar in App.html). Bewusst per Filter statt per // Index-Schnitt, damit eine spätere Ergänzung von LPHS nicht stillschweigend // durchs Raster fällt. const PLAN_ACTIVE_LPS = LPHS.filter(l => l !== 'W+M'); // planLpActiveList: liefert die (auf gültige Phasen gefilterte) amtierende // Leistungsphase eines Projekts als Array — Kopie von lpActiveList() in // js/load/load.js, das auf dieser Seite nicht geladen wird. Parameter: p // (Projekt). Rückgabe: Array (leer, wenn keine gesetzt). function planLpActiveList(p){ const a = p && p.aktuelleLP; if(Array.isArray(a)) return a.filter(l => PLAN_ACTIVE_LPS.includes(l)); if(typeof a === 'string' && PLAN_ACTIVE_LPS.includes(a)) return [a]; return []; } // renderPlanInfo: baut das Projektinfo-Panel (Status-Editor, Gruppe, // amtierende Leistungsphase, LPH-Prozentliste) aus app.project auf. Wird // nach jedem Laden (renderPlanPage(), plan-state.js) sowie nach jeder // Änderung hier selbst erneut aufgerufen. Keine Parameter, keine Rückgabe. window.renderPlanInfo = function(){ const p = app.project; const locked = app.isLocked(); // Kommentar, Fortschritt und Zuordnung sind hier NUR ZU SEHEN. // // Sie entstehen in der wöchentlichen Besprechung und werden deshalb im // Wochenbericht gepflegt — an genau einer Stelle. Zuvor waren dieselben // drei Angaben an drei Orten bearbeitbar (Projektboard, Wochenbericht, // Projektbereich); niemand wusste, welcher der richtige ist, und zwei // Personen konnten dasselbe Feld an verschiedenen Stellen pflegen. // Was hier bleibt, sind die Stammdaten des Projekts (Bereich // "Einstellungen", siehe plan-settings.js). const editEl = document.getElementById('planStatusEdit'); if(editEl){ editEl.className = 'status-edit is-ansicht'; editEl.contentEditable = 'false'; const text = sanitizeStatusHtml(p.status || ''); editEl.innerHTML = text || 'Für diese Sitzung ist noch kein Kommentar erfasst.'; } const statusWrap = document.getElementById('planStatusWrap'); if(statusWrap) statusWrap.classList.add('locked-cell'); setzeBerichtVerweis('planStatusVerweis', locked); // Gruppe — Stammdatum, bleibt hier pflegbar const groupSel = document.getElementById('planGroupSelect'); if(groupSel){ groupSel.value = p.group || 'Allgemein'; groupSel.disabled = locked; } // Amtierende Leistungsphase — Stammdatum, bleibt hier pflegbar const aktLp = planLpActiveList(p)[0] || ''; const lpSel = document.getElementById('planActiveLpSelect'); if(lpSel){ lpSel.innerHTML = '' + PLAN_ACTIVE_LPS.map(l => ``).join(''); lpSel.disabled = locked; } // Fortschritt je Leistungsphase — nur Anzeige, ohne Eingabefeld. // Zeigt nur die BEAUFTRAGTEN Leistungsphasen (p.beauftragteLP, siehe // "Einstellungen"), genau wie die Wochenbericht-/Board-Karte // (BOARD_OPTS.nurBeauftragteLph, js/board/board.js) — dieselbe Regel, // hier nur fest statt als opt-in, da das Dashboard keine zweite Fassung // mit allen Phasen kennt. Ist nichts beauftragt (ältere Bestands- // projekte), bleibt es bei der vollständigen Liste, sonst stünde dort // gar nichts. Vorher zeigte diese Liste IMMER alle Leistungsphasen, // unabhängig von den Einstellungen — von Erik gemeldet. const beauftragt = Array.isArray(p.beauftragteLP) ? p.beauftragteLP : []; const sichtbareLphs = beauftragt.length > 0 ? LPHS.filter(l => beauftragt.includes(l)) : LPHS; const lphList = document.getElementById('planLphList'); if(lphList){ if(!p.lph) p.lph = {}; lphList.innerHTML = sichtbareLphs.map(l => { const v = (p.lph[l] !== undefined && p.lph[l] !== '') ? p.lph[l] : ''; const aktiv = l === aktLp; const pct = v !== '' ? Math.min(100, parseFloat(v)||0) : 0; return `