Plan Checker, Wochenbericht-Fokus-Modus und Wandtafel-Dashboard-Feinschliff

Plan Checker (neues Studenten-Werkzeug, Seitenleiste + Admin → Plan-Checker-
Merkmale): lädt einen Bauplan als PDF hoch und lässt ChatGPT oder Claude ihn
gegen eine gepflegte Merkmalsliste prüfen. PDF-Rendering (pdf.js +
@napi-rs/canvas) berücksichtigt jetzt Ebenen (Optional Content Groups) und
WASM-Decoder korrekt, sendet eine Übersicht plus mehrere hochaufgelöste
Kachel-Ausschnitte statt eines einzelnen, zu groben Bilds, und beide Anbieter
bekommen dasselbe Bildmaterial. Jedes Prüfergebnis kann auf eines der
gesendeten Bilder als Beleg verweisen (bildIndex) — als Screenshot im
Ergebnis anklickbar, öffnet vergrößert in einem Modal statt eines von Chrome
blockierten target=_blank-Links auf eine data:-URL. KI-Modellname ist jetzt
pro Konto überschreibbar (Einstellungen → Mein Konto) für Drittanbieter mit
abweichendem Modell-Katalog.

Wochenbericht: dichte Kartenansicht bekommt einen Fokus-Modus — nur der
Kommentar liegt offen, Leistungsphasen und Beteiligte stecken in
aufklappbaren Abschnitten statt dauerhaft nebeneinander zu stehen.

Projektbereich: Terminplan-Auslastung rechnet jetzt exakt wie der
Auslastungs-Tab/die Wandtafel (statt einer unabhängigen, tageweisen
Schätzung); die LPH-Fortschrittsliste im Dashboard zeigt nur noch die
beauftragten Leistungsphasen.

Wandtafel: Dashboard-Kopf mit Kennzahlen-Reihe und Panel-Reihe neu
strukturiert, in den Blautönen des Firmenlogos; dieselbe Palette wurde
anschließend auf den gesamten App-weiten Dark Mode übernommen.

Dateibereinigung: nicht mehr benötigte Test-Uploads/Backups entfernt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
erikandClaude Sonnet 5 committed 2026-10-05 14:01:41 +02:00
1 parent db63a29365
commit 6ba93b2a92
308 files changed
+64441 -743

No files matched your search

+52 -21
View File
@@ -28,6 +28,22 @@
// persistiert — jede Sitzung startet mit eingeklappten Zeilen.
let boardRowOpen = {};
// berichtDisclosureState: merkt sich je Abschnitt (Leistungsphasen, Beteiligte,
// Anwesenheit), ob er im Wochenbericht (Fokus-Modus — Eriks Wahl "C" aus dem
// Darstellungs-Vergleich: renderBoardDrawer()s dichte Fassung weiter unten +
// renderAttendance() in js/wochenbericht/wochenbericht.js) gerade auf- oder
// zugeklappt ist. Ohne dieses Gedächtnis klappte jeder Abschnitt bei jedem
// Neu-Rendern wieder auf seinen Vorgabewert zurück — und JEDE Änderung in der
// Schublade (eine Zuordnung, ein LPH-Wert) 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 = { lph: true, beteiligte: false, 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){
@@ -500,34 +516,49 @@ function renderBoardDrawer(p, allPeople, locked, weekLabel, opts){
return `<div class="board-role-row"><span class="board-role-label ${roleClass(r)}">${esc(r)}</span><div class="board-role-chips">${chips}</div></div>`;
}).join('');
// Dichte Fassung: zwei Spalten — links der Kommentar, rechts die
// Leistungsphasen-Leiste und darunter die Beteiligten.
//
// Zwischenzeitlich waren es drei Spalten, weil die gestapelten Beteiligten
// die Karte höher machten und damals 29 Karten untereinander standen. Seit
// der Wochenbericht eine Tagesordnung ist, ist immer nur EIN Projekt
// geöffnet — die Höhe der Karte kostet nichts mehr, während die Breite
// knapp ist: Neben der Projektliste blieben für drei Spalten nur noch
// 137 px für die Beteiligten, zu wenig für Rollenname und Kürzel.
// Dichte Fassung (Wochenbericht): erst zwei, dann drei Spalten nebeneinander
// — bei 29 Projekten mit allem gleichzeitig sichtbar kam von Erik die
// Rückmeldung "zu überladen". Jetzt (Fokus-Modus, Eriks Wahl "C" aus dem
// Darstellungs-Vergleich) liegt nur der Kommentar offen, Leistungsphasen
// und Beteiligte stecken in aufklappbaren Abschnitten untereinander.
//
// Die Beteiligten behalten ihre Zeile je Rolle — anders als im Entwurf, wo
// sie zu einer flachen Reihe zusammengefasst waren: Ein Kürzel kann in
// mehreren Rollen vorkommen, und cycleAssign() braucht die Rolle. Ohne sie
// wäre beim Klicken nicht erkennbar, welche Zuordnung man durchschaltet.
if(opts && opts.dicht){
// Fokus-Modus (Eriks Wahl "C" aus dem Darstellungs-Vergleich): nur der
// Kommentar liegt offen, Leistungsphasen und Beteiligte stecken in
// aufklappbaren Abschnitten (<details>, Zustand in berichtDisclosureState)
// statt dauerhaft nebeneinander zu stehen. Die Leistungsphasen stehen hier
// als Balkenzeilen (bericht-lph-row) statt als Kästchen-Leiste
// (board-lph-leiste bleibt die Darstellung des Projektboards, unverändert).
const lphBarRows = sichtbareLphs.map(l=>{
const v = (p.lph[l]!==undefined && p.lph[l]!="") ? p.lph[l] : "";
const aktiv = l === aktLp;
const inputAttr = starr ? 'readonly tabindex="-1"' : `onchange="editLph('${p.id}','${l}',this.value)"`;
const pct = v !== "" ? Math.min(100, parseFloat(v)||0) : 0;
return `<div class="bericht-lph-row${aktiv ? ' is-aktiv' : ''}">
<span class="bericht-lph-label">${esc(l.replace('LPH ', 'LP '))}</span>
<span class="bericht-lph-track"><i style="width:${pct}%"></i></span>
<span class="bericht-lph-pct"><input type="text" class="bericht-lph-input${locked ? ' locked-cell' : ''}" value="${esc(v)}" ${inputAttr} placeholder="-">%</span>
</div>`;
}).join('');
const lphInfo = aktLp
? esc(aktLp.replace('LPH ', 'LP ')) + ' aktiv' + (p.lph[aktLp] !== undefined && p.lph[aktLp] !== '' ? ' · ' + esc(p.lph[aktLp]) + '%' : '')
: 'Keine aktive Leistungsphase';
const zugeordnetAnzahl = allPeople.filter(e => (p.assign[assignKey(e.person.id, e.role)] || 0) > 0).length;
return `<div class="board-drawer board-drawer-dicht">
<div class="board-drawer-grid">
<div class="drawer-col drawer-col-comment">
<h4>Kommentar${weekLabel ? ' zur Sitzung ' + esc(weekLabel) : ''}</h4>
${renderCommentEditor(p, weekLabel, locked, true)}
</div>
<div class="drawer-col drawer-col-kompakt">
<h4>${nurBeauftragte ? 'Beauftragte Leistungsphasen' : 'Leistungsphasen'}</h4>
<div class="board-lph-leiste">${lphLeiste}</div>
<h4 class="drawer-h4-zweit">Beteiligte</h4>
<div class="board-role-mini">${roleRows}</div>
</div>
</div>
${renderCommentEditor(p, weekLabel, locked, true)}
<details class="bericht-disclosure" data-bericht-abschnitt="lph" ${berichtDisclosureState.lph ? 'open' : ''}>
<summary><span>${nurBeauftragte ? 'Beauftragte Leistungsphasen' : 'Leistungsphasen'}</span><span class="count">${lphInfo}</span><span class="chev">${icon('chevronRight')}</span></summary>
<div class="bericht-disclosure-body">${lphBarRows}</div>
</details>
<details class="bericht-disclosure" data-bericht-abschnitt="beteiligte" ${berichtDisclosureState.beteiligte ? 'open' : ''}>
<summary><span>Beteiligte</span><span class="count">${zugeordnetAnzahl} zugeordnet</span><span class="chev">${icon('chevronRight')}</span></summary>
<div class="bericht-disclosure-body"><div class="board-role-mini">${roleRows}</div></div>
</details>
${typeof renderProjektVerlauf === 'function' ? renderProjektVerlauf(p, weekLabel, locked) : ''}
</div>`;
}