// ----------------------------- // 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), Fortschritt je // Leistungsphase (#planLphList) und die Zuordnung der Beteiligten // (#planAssignMatrix). Diese drei 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 drei 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. // // BEWUSST NICHT hier: Projektnummer, Bezeichnung, Schlagwörter, Honorar und // beauftragte Leistungsphasen. Sie sind ebenfalls Stammdaten, werden aber von // js/plan/plan-settings.js gerendert (Honorar und beauftragte Phasen // geschrieben von setHonorarBetrag()/toggleBeauftragteLp() aus // js/plan/plan-honorar.js). // // 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 Test.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 const lphList = document.getElementById('planLphList'); if(lphList){ if(!p.lph) p.lph = {}; lphList.innerHTML = LPHS.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 `
${esc(l)}
${v === '' ? '–' : esc(v) + ' %'}
`; }).join(''); } setzeBerichtVerweis('planLphVerweis', locked); // Beteiligte — nur Anzeige. Nicht zugeordnete Personen erscheinen // weiterhin blass, damit erkennbar bleibt, wer überhaupt zur Auswahl // stünde; anklickbar sind sie hier aber nicht mehr. const assignMatrix = document.getElementById('planAssignMatrix'); if(assignMatrix){ const grouped = peopleByRole(app.week); if(!p.assign) p.assign = {}; assignMatrix.innerHTML = ROLES.map(r => { const entries = grouped[r] || []; const chips = entries.length ? entries.map(entry => { const state = p.assign[assignKey(entry.person.id, r)] || 0; const title = (entry.person.fullName || entry.person.name) + ' · ' + r; return `${esc(entry.person.name)}`; }).join('') : `Niemand hinterlegt`; return `
${esc(r)}
${chips}
`; }).join(''); } setzeBerichtVerweis('planAssignVerweis', locked); }; // setzeBerichtVerweis: füllt einen Hinweis-Platz mit dem Weg zur // Bearbeitung. Der Verweis ist ein echter Sprung: Er wechselt in den // Wochenbericht UND wählt dort dieses Projekt aus — ohne das müsste man es // in einer Liste von dreißig wiederfinden. // Parameter: elementId, locked. Keine Rückgabe. function setzeBerichtVerweis(elementId, locked){ const el = document.getElementById(elementId); if(!el) return; if(locked){ el.innerHTML = 'Die Sitzung ist gesperrt.'; return; } const uid = esc(app.project && app.project.projectUid ? app.project.projectUid : ''); el.innerHTML = ``; } // springZumWochenbericht: wechselt in den Wochenbericht und stellt dort // dieses Projekt ein. Beides zusammen, weil ein Sprung, der nur den Bereich // wechselt, die eigentliche Arbeit — das Projekt wiederfinden — beim // Nutzer ließe. Parameter: uid (project_uid). Keine Rückgabe. window.springZumWochenbericht = function(uid){ if(typeof showTab !== 'function') return; // Erst die Auswahl setzen, dann umschalten: showTab('wochenbericht') // rendert den Bereich bereits, und der soll gleich das richtige Projekt // zeigen — sonst blitzte kurz das erste Projekt der Liste auf. // berichtAuswahl ist eine globale let-Bindung aus // js/wochenbericht/wochenbericht.js (klassische Skripte, kein Modul). if(uid) berichtAuswahl = uid; showTab('wochenbericht'); }; // setPlanGroup: ändert die Gruppe des Projekts. Parameter: val (String). // Keine Rückgabe; bei gesperrter Woche abgebrochen. window.setPlanGroup = function(val){ if(app.isLocked()) return; app.project.group = val; app.savePlan(); }; // setPlanActiveLp: setzt bzw. hebt die amtierende Leistungsphase des // Projekts auf. Parameter: val (LPH-Name oder leer). Rendert das // Infopanel neu, damit die Markierung in der LPH-Liste sofort mitzieht. window.setPlanActiveLp = function(val){ if(app.isLocked()) return; if(val) app.project.aktuelleLP = [val]; else delete app.project.aktuelleLP; app.savePlan(); window.renderPlanInfo(); }; // Die Schreibfunktionen setPlanLph()/setPlanAssign()/planStatusBlur() sind // entfallen. Fortschritt, Zuordnung und Kommentar werden allein im // Wochenbericht erfasst (siehe den Kopf dieser Datei); hier gab es damit // keinen Aufrufer mehr. Die gleichnamigen Funktionen des Projektboards // (editLph()/cycleAssign()/saveBoardComment() in js/board/board.js) sind // davon unberührt — dort hängt die Erfassung des Wochenberichts dran. })();