Compare commits
1
Commits
e291ca8438
..
v1.1.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ac3d8d53d |
No files matched your search
@@ -4871,6 +4871,31 @@ Kommentare nach dreimaligem Bereinigen unverändert). `entitiesReparieren()`
|
|||||||
`project_kommentare.status` und `protokoll_punkte.text` aller Sitzungen über
|
`project_kommentare.status` und `protokoll_punkte.text` aller Sitzungen über
|
||||||
`entitiesEntstapeln()` zurück.
|
`entitiesEntstapeln()` zurück.
|
||||||
|
|
||||||
|
## Behobener Fehler: Kommentare abgewählter Gewerke (11/2026)
|
||||||
|
|
||||||
|
Eriks Meldung zu 21-4030: Im Projekt-Dashboard stand ein HLS-Kommentar,
|
||||||
|
obwohl das Projekt seit KW 31 nur noch ELT hat. Ursache: Die einmalige
|
||||||
|
Umstellung `kommentareInAktuelleSitzungUebernehmen()` hatte je Gewerk den
|
||||||
|
jüngsten früheren Kommentar übernommen, ohne die aktuellen Gewerke des
|
||||||
|
Projekts zu prüfen; der Sitzungswechsel trug ihn weiter. Jetzt:
|
||||||
|
`kommentareDesProjekts()` (js/utils.js) — die Gesamtanzeige
|
||||||
|
(`kommentarVon(p, null)`, `kommentarAnzeigeHtml()`) und der Verlauf im
|
||||||
|
Projekt-Dashboard (`planKommentarDaten()`, je Sitzung mit DEREN Gewerken)
|
||||||
|
zeigen nur Gewerke des Projekts; `ensureCurrentWeekRollover()` übernimmt nur
|
||||||
|
Kommentare dieser Gewerke. Gelöscht wird nichts — die verirrte Kopie fällt
|
||||||
|
beim nächsten Sitzungswechsel aus der aktuellen Sitzung heraus.
|
||||||
|
|
||||||
|
**Darstellung mehrerer Gewerke „B – Überschrift mit Linie“** (Eriks Wahl
|
||||||
|
aus dem Artifact „Kommentare je Gewerk“): `kommentarAnzeigeHtml(p, null,
|
||||||
|
mitMeta)` zeichnet je Gewerk eine Kopfzeile (Gewerk in seiner Farbe
|
||||||
|
`--gewerk-elt/-hls/-arch`, feine Linie, mit `mitMeta` Verfasser und Zeit)
|
||||||
|
und darunter den Text; bei nur einem Gewerk wie bisher nur den Text.
|
||||||
|
Genutzt von der Kachel „Kommentar der Sitzung“ im Projekt-Dashboard (dort
|
||||||
|
entfällt dann die gemeinsame Verfasserzeile, `kommentarGewerkeMitText()`),
|
||||||
|
der Kommentaransicht der Projektliste/Wandtafel (`renderKommentarAnsicht()`)
|
||||||
|
und den Verlauf-Kacheln (ohne Verfasser). Stile `.kommentar-gewerke`,
|
||||||
|
`.kommentar-kopf` in `styles.css`.
|
||||||
|
|
||||||
## Behobene Fehler (Testlauf 10/2026, fünf Test-Agents)
|
## Behobene Fehler (Testlauf 10/2026, fünf Test-Agents)
|
||||||
|
|
||||||
- **Gesperrte Sitzungen sind für Nicht-Admins auch inhaltlich unveränderlich**:
|
- **Gesperrte Sitzungen sind für Nicht-Admins auch inhaltlich unveränderlich**:
|
||||||
|
|||||||
+4
-4
@@ -38,7 +38,7 @@
|
|||||||
<link rel="icon" type="image/png" href="/logo.png">
|
<link rel="icon" type="image/png" href="/logo.png">
|
||||||
<link rel="alternate icon" href="/logo.ico">
|
<link rel="alternate icon" href="/logo.ico">
|
||||||
<link rel="apple-touch-icon" href="/logo.png">
|
<link rel="apple-touch-icon" href="/logo.png">
|
||||||
<link rel="stylesheet" href="/styles.css?v=122">
|
<link rel="stylesheet" href="/styles.css?v=123">
|
||||||
<link rel="stylesheet" href="/plan-styles.css?v=56">
|
<link rel="stylesheet" href="/plan-styles.css?v=56">
|
||||||
<!-- Glas-Darstellung (public/glas.css, als letztes Stylesheet): greift nur mit
|
<!-- Glas-Darstellung (public/glas.css, als letztes Stylesheet): greift nur mit
|
||||||
der Klasse html.glas. Gesetzt wird sie hier, vor dem ersten Zeichnen,
|
der Klasse html.glas. Gesetzt wird sie hier, vor dem ersten Zeichnen,
|
||||||
@@ -1954,7 +1954,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/globals.js?v=10"></script>
|
<script src="/js/globals.js?v=10"></script>
|
||||||
<script src="/js/utils.js?v=31"></script>
|
<script src="/js/utils.js?v=33"></script>
|
||||||
<script src="/js/icons.js?v=2"></script>
|
<script src="/js/icons.js?v=2"></script>
|
||||||
<script src="/js/ui.js?v=9"></script>
|
<script src="/js/ui.js?v=9"></script>
|
||||||
<script src="/js/date-picker.js?v=6"></script>
|
<script src="/js/date-picker.js?v=6"></script>
|
||||||
@@ -1962,7 +1962,7 @@
|
|||||||
<script src="/js/auth.js?v=11"></script>
|
<script src="/js/auth.js?v=11"></script>
|
||||||
<script src="/js/store.js?v=17"></script>
|
<script src="/js/store.js?v=17"></script>
|
||||||
<script src="/js/weeks.js?v=12"></script>
|
<script src="/js/weeks.js?v=12"></script>
|
||||||
<script src="/js/board/board.js?v=41"></script>
|
<script src="/js/board/board.js?v=42"></script>
|
||||||
<script src="/js/wochenbericht/wochenbericht.js?v=29"></script>
|
<script src="/js/wochenbericht/wochenbericht.js?v=29"></script>
|
||||||
<script src="/js/people/people.js?v=8"></script>
|
<script src="/js/people/people.js?v=8"></script>
|
||||||
<script src="/js/planung/planung.js?v=3"></script>
|
<script src="/js/planung/planung.js?v=3"></script>
|
||||||
@@ -2001,7 +2001,7 @@
|
|||||||
umhuellt. -->
|
umhuellt. -->
|
||||||
<script src="/js/plan/plan-state.js?v=25"></script>
|
<script src="/js/plan/plan-state.js?v=25"></script>
|
||||||
<script src="/js/plan/plan-sections.js?v=12"></script>
|
<script src="/js/plan/plan-sections.js?v=12"></script>
|
||||||
<script src="/js/plan/plan-info.js?v=15"></script>
|
<script src="/js/plan/plan-info.js?v=17"></script>
|
||||||
<script src="/js/plan/plan-history.js?v=4"></script>
|
<script src="/js/plan/plan-history.js?v=4"></script>
|
||||||
<script src="/js/plan/plan-build-gantt.js?v=11"></script>
|
<script src="/js/plan/plan-build-gantt.js?v=11"></script>
|
||||||
<script src="/js/plan/plan-gantt.js?v=23"></script>
|
<script src="/js/plan/plan-gantt.js?v=23"></script>
|
||||||
|
|||||||
@@ -748,7 +748,7 @@ function renderBoardDrawer(p, allPeople, locked, weekLabel, opts){
|
|||||||
// renderKommentarAnsicht: der Kommentar der laufenden Sitzung, nur zu lesen.
|
// renderKommentarAnsicht: der Kommentar der laufenden Sitzung, nur zu lesen.
|
||||||
// Parameter: p (Projekt), locked. Rückgabe: HTML-String.
|
// Parameter: p (Projekt), locked. Rückgabe: HTML-String.
|
||||||
function renderKommentarAnsicht(p, locked){
|
function renderKommentarAnsicht(p, locked){
|
||||||
const text = sanitizeStatusHtml(kommentarVon(p, kommentarGewerk()).status || '');
|
const text = kommentarAnzeigeHtml(p, kommentarGewerk(), true);
|
||||||
return `<div class="status-edit is-ansicht locked-cell">${text ||
|
return `<div class="status-edit is-ansicht locked-cell">${text ||
|
||||||
'<span class="hint" style="margin:0">Für diese Sitzung ist noch kein Kommentar erfasst.</span>'}</div>`;
|
'<span class="hint" style="margin:0">Für diese Sitzung ist noch kein Kommentar erfasst.</span>'}</div>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@
|
|||||||
for(let i = versionen.length - 1; i >= 0; i--){
|
for(let i = versionen.length - 1; i >= 0; i--){
|
||||||
const k = kommentarVon(versionen[i].project, null);
|
const k = kommentarVon(versionen[i].project, null);
|
||||||
if(statusPlainText(k.status)){
|
if(statusPlainText(k.status)){
|
||||||
letzter = { week: versionen[i].week, k, aktuell: i === versionen.length - 1 };
|
letzter = { week: versionen[i].week, k, project: versionen[i].project, aktuell: i === versionen.length - 1 };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,8 @@
|
|||||||
gewerke.forEach(g => {
|
gewerke.forEach(g => {
|
||||||
let vorher = '';
|
let vorher = '';
|
||||||
versionen.forEach(v => {
|
versionen.forEach(v => {
|
||||||
const k = kommentarVon(v.project, g);
|
// Nur, wenn das Projekt in JENER Sitzung das Gewerk hatte.
|
||||||
|
const k = (g in kommentareDesProjekts(v.project)) ? kommentarVon(v.project, g) : { status: '' };
|
||||||
const text = statusPlainText(k.status);
|
const text = statusPlainText(k.status);
|
||||||
if(text && text !== vorher && !(letzter && v.week === letzter.week)){
|
if(text && text !== vorher && !(letzter && v.week === letzter.week)){
|
||||||
verlauf.push({ week: v.week, gewerk: g, html: sanitizeStatusHtml(k.status), autor: k.statusAuthor || '', at: k.statusAt || '' });
|
verlauf.push({ week: v.week, gewerk: g, html: sanitizeStatusHtml(k.status), autor: k.statusAuthor || '', at: k.statusAt || '' });
|
||||||
@@ -184,8 +185,9 @@
|
|||||||
if(editEl){
|
if(editEl){
|
||||||
editEl.className = 'status-edit is-ansicht';
|
editEl.className = 'status-edit is-ansicht';
|
||||||
editEl.contentEditable = 'false';
|
editEl.contentEditable = 'false';
|
||||||
// Alle Gewerke zusammen, je mit Gewerk davor (kommentarVon(), js/utils.js).
|
// Alle Gewerke zusammen; bei mehreren je mit Überschrift, Linie und
|
||||||
const text = kom.letzter ? sanitizeStatusHtml(kom.letzter.k.status) : '';
|
// Verfasser (kommentarAnzeigeHtml(), js/utils.js, Eriks Wahl „B“).
|
||||||
|
const text = kom.letzter ? kommentarAnzeigeHtml(kom.letzter.project, null, true) : '';
|
||||||
editEl.innerHTML = text || '<span class="hint" style="margin:0">Zu diesem Projekt ist noch kein Kommentar erfasst.</span>';
|
editEl.innerHTML = text || '<span class="hint" style="margin:0">Zu diesem Projekt ist noch kein Kommentar erfasst.</span>';
|
||||||
}
|
}
|
||||||
const hinweisEl = document.getElementById('planStatusHinweis');
|
const hinweisEl = document.getElementById('planStatusHinweis');
|
||||||
@@ -200,7 +202,9 @@
|
|||||||
const autorEl = document.getElementById('planStatusAutor');
|
const autorEl = document.getElementById('planStatusAutor');
|
||||||
if(autorEl){
|
if(autorEl){
|
||||||
const k = kom.letzter ? kom.letzter.k : {};
|
const k = kom.letzter ? kom.letzter.k : {};
|
||||||
autorEl.innerHTML = k.status && k.statusAuthor
|
// Bei mehreren Gewerken steht der Verfasser schon je Gewerk im Text.
|
||||||
|
const mehrere = kom.letzter && kommentarGewerkeMitText(kom.letzter.project) > 1;
|
||||||
|
autorEl.innerHTML = !mehrere && k.status && k.statusAuthor
|
||||||
? `<span class="plan-autor-ava">${esc(initialsForName(k.statusAuthor))}</span>${esc(k.statusAuthor)}`
|
? `<span class="plan-autor-ava">${esc(initialsForName(k.statusAuthor))}</span>${esc(k.statusAuthor)}`
|
||||||
+ (k.statusAt && typeof formatCommentStamp === 'function' ? ', ' + esc(formatCommentStamp(k.statusAt)) : '')
|
+ (k.statusAt && typeof formatCommentStamp === 'function' ? ', ' + esc(formatCommentStamp(k.statusAt)) : '')
|
||||||
: '';
|
: '';
|
||||||
|
|||||||
+39
-8
@@ -385,8 +385,21 @@ function kommentarGewerk(){
|
|||||||
// Verfasser/Zeit des jüngsten, besprochen nur, wenn ALLE Gewerke des
|
// Verfasser/Zeit des jüngsten, besprochen nur, wenn ALLE Gewerke des
|
||||||
// Projekts es abgehakt haben. Parameter: p (Projekt), g (Gewerk oder
|
// Projekts es abgehakt haben. Parameter: p (Projekt), g (Gewerk oder
|
||||||
// null/undefined). Rückgabe: { status, statusAuthor, statusAt, besprochen }.
|
// null/undefined). Rückgabe: { status, statusAuthor, statusAt, besprochen }.
|
||||||
function kommentarVon(p, g){
|
// kommentareDesProjekts: die Kommentare nur der Gewerke, die das Projekt hat.
|
||||||
|
// Ein Kommentar eines Gewerks, das dem Projekt nicht (mehr) gehört — etwa aus
|
||||||
|
// einer Zeit, als es noch HLS hatte —, gehört nicht in die Gesamtanzeige
|
||||||
|
// (Eriks Meldung zu 21-4030). Ohne gepflegte Gewerke: alle. Rückgabe: Objekt.
|
||||||
|
function kommentareDesProjekts(p){
|
||||||
const alle = (p && p.kommentare) || {};
|
const alle = (p && p.kommentare) || {};
|
||||||
|
const gewerke = Array.isArray(p && p.gewerke) ? p.gewerke : [];
|
||||||
|
if(!gewerke.length) return alle;
|
||||||
|
const aus = {};
|
||||||
|
Object.keys(alle).forEach(k => { if(gewerke.includes(k)) aus[k] = alle[k]; });
|
||||||
|
return aus;
|
||||||
|
}
|
||||||
|
|
||||||
|
function kommentarVon(p, g){
|
||||||
|
const alle = g ? ((p && p.kommentare) || {}) : kommentareDesProjekts(p);
|
||||||
if(g){
|
if(g){
|
||||||
const e = alle[g] || {};
|
const e = alle[g] || {};
|
||||||
return { status: e.status || '', statusAuthor: e.statusAuthor || '', statusAt: e.statusAt || '', besprochen: !!e.besprochen };
|
return { status: e.status || '', statusAuthor: e.statusAuthor || '', statusAt: e.statusAt || '', besprochen: !!e.besprochen };
|
||||||
@@ -408,19 +421,37 @@ function kommentarVon(p, g){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// kommentarAnzeigeHtml: der Kommentar zur ANZEIGE, bereinigt. Mit Gewerk g
|
// kommentarAnzeigeHtml: der Kommentar zur ANZEIGE, bereinigt. Mit Gewerk g
|
||||||
// nur dessen Text; ohne Gewerk die Texte aller Gewerke, bei mehreren je mit
|
// nur dessen Text; ohne Gewerk die Texte aller Gewerke des Projekts. Bei
|
||||||
// dem Gewerk als eigener, dezenter Beschriftung (.kommentar-gewerk) —
|
// mehreren Gewerken Darstellung „B – Überschrift mit Linie“ (Eriks Wahl):
|
||||||
// Systemtext, nicht Teil des Kommentars (anders als die fette "ELT:"-Zeile,
|
// je Gewerk eine Kopfzeile mit dem Gewerk in seiner Farbe, einer feinen Linie
|
||||||
// die kommentarVon() für Klartext-Vorschauen einfügt). Rückgabe: HTML ('' ohne Text).
|
// und (mitMeta) Verfasser und Zeit, darunter der Text — Systemtext, nicht
|
||||||
function kommentarAnzeigeHtml(p, g){
|
// Teil des Kommentars (anders als die fette "ELT:"-Zeile, die kommentarVon()
|
||||||
|
// für Klartext-Vorschauen einfügt). Parameter: p, g, mitMeta.
|
||||||
|
// Rückgabe: HTML ('' ohne Text).
|
||||||
|
function kommentarAnzeigeHtml(p, g, mitMeta){
|
||||||
if(g) return sanitizeStatusHtml(kommentarVon(p, g).status || '');
|
if(g) return sanitizeStatusHtml(kommentarVon(p, g).status || '');
|
||||||
const alle = (p && p.kommentare) || {};
|
const alle = kommentareDesProjekts(p);
|
||||||
const reihenfolge = (typeof DEPARTMENTS === 'object' && DEPARTMENTS) ? Object.keys(DEPARTMENTS) : ['ELT','HLS','ARCH'];
|
const reihenfolge = (typeof DEPARTMENTS === 'object' && DEPARTMENTS) ? Object.keys(DEPARTMENTS) : ['ELT','HLS','ARCH'];
|
||||||
Object.keys(alle).forEach(k => { if(!reihenfolge.includes(k)) reihenfolge.push(k); });
|
Object.keys(alle).forEach(k => { if(!reihenfolge.includes(k)) reihenfolge.push(k); });
|
||||||
const mitText = reihenfolge.filter(k => alle[k] && statusPlainText(alle[k].status) !== '');
|
const mitText = reihenfolge.filter(k => alle[k] && statusPlainText(alle[k].status) !== '');
|
||||||
if(mitText.length === 0) return '';
|
if(mitText.length === 0) return '';
|
||||||
if(mitText.length === 1) return sanitizeStatusHtml(alle[mitText[0]].status);
|
if(mitText.length === 1) return sanitizeStatusHtml(alle[mitText[0]].status);
|
||||||
return mitText.map(k => `<div class="kommentar-gewerk">${esc(k)}</div><div>${sanitizeStatusHtml(alle[k].status)}</div>`).join('');
|
return '<div class="kommentar-gewerke">' + mitText.map(k => {
|
||||||
|
const e = alle[k];
|
||||||
|
const zeit = e.statusAt && typeof formatCommentStamp === 'function' ? formatCommentStamp(e.statusAt) : '';
|
||||||
|
const meta = mitMeta ? [e.statusAuthor, zeit].filter(Boolean).join(' · ') : '';
|
||||||
|
return '<div class="kommentar-block">'
|
||||||
|
+ '<div class="kommentar-kopf"><span class="kommentar-gewerk gewerk-' + esc(k.toLowerCase()) + '">' + esc(k) + '</span>'
|
||||||
|
+ '<span class="kommentar-linie"></span>' + (meta ? '<span class="kommentar-meta">' + esc(meta) + '</span>' : '') + '</div>'
|
||||||
|
+ '<div class="kommentar-text">' + sanitizeStatusHtml(e.status) + '</div></div>';
|
||||||
|
}).join('') + '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// kommentarGewerkeMitText: wie viele Gewerke des Projekts einen Kommentar
|
||||||
|
// haben (für die Frage, ob eine gemeinsame Verfasserzeile Sinn ergibt).
|
||||||
|
function kommentarGewerkeMitText(p){
|
||||||
|
const alle = kommentareDesProjekts(p);
|
||||||
|
return Object.keys(alle).filter(k => alle[k] && statusPlainText(alle[k].status) !== '').length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// kommentarSetzen: ändert den Kommentar-Eintrag eines Gewerks (nur die in
|
// kommentarSetzen: ändert den Kommentar-Eintrag eines Gewerks (nur die in
|
||||||
|
|||||||
+13
-6
@@ -4188,12 +4188,19 @@ tr.is-menu-open { background: var(--surface-alt); }
|
|||||||
min-height: 3em;
|
min-height: 3em;
|
||||||
}
|
}
|
||||||
.timeline-text p { margin: 0; }
|
.timeline-text p { margin: 0; }
|
||||||
/* Gewerk über dem Kommentar: Systemtext, nicht Teil des Kommentars. */
|
/* Kommentare mehrerer Gewerke, Darstellung „B – Überschrift mit Linie“
|
||||||
.kommentar-gewerk {
|
(Eriks Wahl): Gewerk in seiner Farbe, feine Linie, Verfasser und Zeit,
|
||||||
font-size: 11px; font-weight: 600; letter-spacing: 0;
|
darunter der Text. Systemtext, nicht Teil des Kommentars. */
|
||||||
color: var(--text-subtle); margin-top: 4px;
|
.kommentar-gewerke { display: flex; flex-direction: column; gap: 16px; }
|
||||||
}
|
.kommentar-block { display: flex; flex-direction: column; gap: 4px; }
|
||||||
.kommentar-gewerk:first-child { margin-top: 0; }
|
.kommentar-kopf { display: flex; align-items: center; gap: 12px; }
|
||||||
|
.kommentar-gewerk { flex: none; font-size: 12px; font-weight: 500; letter-spacing: 0; color: var(--text-muted); }
|
||||||
|
.kommentar-gewerk.gewerk-elt { color: var(--gewerk-elt); }
|
||||||
|
.kommentar-gewerk.gewerk-hls { color: var(--gewerk-hls); }
|
||||||
|
.kommentar-gewerk.gewerk-arch { color: var(--gewerk-arch); }
|
||||||
|
.kommentar-linie { flex: 1; height: 1px; background: var(--line-soft); min-width: 16px; }
|
||||||
|
.kommentar-meta { flex: none; font-size: 12px; font-weight: 400; color: var(--text-subtle); white-space: nowrap; }
|
||||||
|
:root { --gewerk-elt: var(--accent); --gewerk-hls: var(--green); --gewerk-arch: var(--amber); }
|
||||||
.timeline-leer { color: var(--text-muted); font-style: normal; }
|
.timeline-leer { color: var(--text-muted); font-style: normal; }
|
||||||
|
|
||||||
.timeline-lph-list { display: flex; flex-direction: column; gap: 1px; }
|
.timeline-lph-list { display: flex; flex-direction: column; gap: 1px; }
|
||||||
|
|||||||
@@ -2956,8 +2956,12 @@ function ensureCurrentWeekRollover(){
|
|||||||
// zur Sitzung und beginnt leer.
|
// zur Sitzung und beginnt leer.
|
||||||
const vorher = p.kommentare || {};
|
const vorher = p.kommentare || {};
|
||||||
p.kommentare = {};
|
p.kommentare = {};
|
||||||
|
// Nur Gewerke, die das Projekt hat — sonst lebte ein Kommentar eines
|
||||||
|
// längst abgewählten Gewerks Woche für Woche weiter (21-4030).
|
||||||
|
const gewerke = Array.isArray(p.gewerke) ? p.gewerke : [];
|
||||||
Object.keys(vorher).forEach(g => {
|
Object.keys(vorher).forEach(g => {
|
||||||
const k = vorher[g] || {};
|
const k = vorher[g] || {};
|
||||||
|
if(gewerke.length && !gewerke.includes(g)) return;
|
||||||
if(!String(k.status || '').trim()) return;
|
if(!String(k.status || '').trim()) return;
|
||||||
p.kommentare[g] = { status: k.status, statusAuthor: k.statusAuthor || '',
|
p.kommentare[g] = { status: k.status, statusAuthor: k.statusAuthor || '',
|
||||||
statusAt: k.statusAt || '', besprochen: false };
|
statusAt: k.statusAt || '', besprochen: false };
|
||||||
|
|||||||
Reference in new issue
Block a user