Files
projektuebersicht/public/js/plan/plan-info.js
T
erikandClaude Opus 5 93505e0e48 Beteiligte in die Projekteinstellungen, TODOs aus Protokoll, Bewegung
Beteiligten-Zuordnung verlagert: p.assign ist jetzt ein Stammdatum und
nur noch in den Projekteinstellungen editierbar (planCycleAssign()).
Wochenbericht und Projektboard zeigen sie nur noch an, mit Verweis
dorthin (springZuEinstellungen()). cycleAssign() ist damit entfallen.

Neu "TODOs aus Protokoll": ein externes Besprechungsprotokoll als PDF
wird per KI eingelesen und daraus in einem zweiten KI-Schritt Aufgaben
abgeleitet. Eigener Bereich in der Aufgaben-/GANTT-Leiste, bewusst
unabhaengig von den gespeicherten Protokollen - nichts davon wird
abgelegt. Drei Schritte in einem Dialog mit fester Fusszeile; die
Vorschlagsliste als kompakte, aufklappbare Zeilen, weil ein echter
Import leicht 20 Vorschlaege liefert.

Sichtbarkeit je Aufgabe im GANTT: neue Spalte plan_todos.show_in_gantt
(Vorgabe 1, idempotente Migration) und eine Checkbox im Aufgaben-Dialog.
Eine terminierte Aufgabe kann damit gezielt aus dem Terminplan
herausgehalten werden, bleibt sonst aber eine vollwertige Aufgabe.
plan-build-gantt.js und plan-drag.js pruefen dieselbe Bedingung, damit
Zeilenzahl und Umsortierung nicht auseinanderlaufen.

KI-Timeout dritte Stufe: ein abweichender, aber entfernter Endpunkt
(Drittanbieter-Proxy) bekommt 2 Minuten statt der 45 Sekunden der
echten Anbieter-API. Gemessen: die Punkte-Erkennung eines realen
Protokolls brauchte dort 39 Sekunden, lag also stets knapp unter dem
alten Limit und brach bei jeder Schwankung ab.

Bewegung: Hover- und Klick-Rueckmeldung als Token-Satz in :root,
angewandt auf die anklickbaren Zeilen, Schalter und Dialoge. Die
Akzentleiste ist ein inset-Schatten statt eines positionierten
Elements, damit Drag-and-Drop unberuehrt bleibt; prefers-reduced-motion
war bisher gar nicht vorhanden und kippt jetzt einfach die Tokens.

Plan Checker: optionaler Projektabgleich des Plankopfs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-17 15:14:26 +02:00

205 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// -----------------------------
// 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 || '<span class="hint" style="margin:0">Für diese Sitzung ist noch kein Kommentar erfasst.</span>';
}
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 = '<option value="">Keine</option>' + PLAN_ACTIVE_LPS.map(l => `<option value="${l}" ${l===aktLp?'selected':''}>${l}</option>`).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 `<div class="board-lph-row is-ansicht${aktiv ? ' lp-aktiv' : ''}"${aktiv ? ' title="Amtierende Leistungsphase"' : ''}>
<span class="board-lph-label">${esc(l)}</span>
<div class="bar"><div style="width:${pct}%;background:${getProgressColor(pct)}"></div></div>
<span class="board-lph-wert">${v === '' ? '–' : esc(v) + ' %'}</span>
</div>`;
}).join('');
}
setzeBerichtVerweis('planLphVerweis', locked);
// Beteiligte: siehe js/plan/plan-settings.js (renderPlanSettings()) —
// Stammdatum, nicht mehr hier.
};
// 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 = '<span class="hint" style="margin:0">Die Sitzung ist gesperrt.</span>';
return;
}
const uid = esc(app.project && app.project.projectUid ? app.project.projectUid : '');
el.innerHTML = `<button type="button" class="plan-verweis" onclick="springZumWochenbericht('${uid}')">
Im Wochenbericht bearbeiten →
</button>`;
}
// 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 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()/saveBoardComment() in js/board/board.js) sind davon
// unberührt — dort hängt die Erfassung des Wochenberichts dran. Die
// Zuordnung der Beteiligten (ehemals setPlanAssign()) läuft seit ihrer
// Verlagerung in die Projekteinstellungen über planCycleAssign()
// (js/plan/plan-settings.js) — ebenfalls kein Fall für dieses Modul.
})();