Termin aus Mail, Rollen & Rechte, Abwesenheiten, Glas-Darstellung und Pi-Betrieb

- Termin aus Mail: Mailtext oder .eml per Drag & Drop/Strg+V auf Terminplan
  oder Aufgaben, KI schlägt Aufgaben vor, Prüfung im Aufgaben-Panel; neues
  Notizfeld je Aufgabe (plan_todos.notiz)
- Terminplan: Meilensteine ohne Aufgabenbezug je in eigener Zeile,
  "+ Neu"-Menü klappt neben schmaler Seitenleiste nach rechts, Leistungsphasen
- Rollen & Rechte, Abwesenheiten-Seite, Benachrichtigungen, Rundgang
- Glas-Darstellung, Logo als Favicon
- Pi-Betrieb: PDF-Arbeit im Kindprozess, Wächter, Einrichtungs- und
  Prüfskript inkl. WLAN-Wächter

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
erikandClaude Opus 5.5 committed 2026-10-29 14:16:52 +01:00
1 parent f4fb3c550b
commit b7e0bd0d92
56 files changed
+12265 -2974

No files matched your search

+36 -2
View File
@@ -75,6 +75,18 @@
return e < today;
}
// Phasenfilter der Aufgabenliste: '' = alle, 'LPH 5', oder '-' = ohne
// Phase. Gilt nur für das Projekt, für das er gesetzt wurde.
let filter = { projekt: null, phase: '' };
const filterPhase = () => filter.projekt === app.project ? filter.phase : '';
// setzeAufgabenFilter: von außen (Bereich Leistungsphasen) oder über die
// Filterknöpfe. Parameter: phase ('' | 'LPH x' | '-').
window.setzeAufgabenFilter = function(phase){
filter = { projekt: app.project, phase: phase || '' };
window.renderPlanTasks();
};
// renderPlanTasks: baut die Aufgaben- und die Meilensteinliste des Bereichs
// "Aufgaben" neu auf und schreibt die Zähler in die Kartenüberschriften.
// Keine Parameter, keine Rückgabe.
@@ -85,7 +97,25 @@
// ---- Aufgaben ----
const todoEl = document.getElementById('planTodoList');
const todos = Array.isArray(plan.todos) ? plan.todos : [];
const alleTodos = Array.isArray(plan.todos) ? plan.todos : [];
// Phasenbalken stehen im eigenen Bereich "Leistungsphasen"
// (plan-phasen.js), nicht in der Aufgabenliste. idx bleibt der Index in
// plan.todos, den die Dialoge erwarten.
const eintraege = alleTodos.map((t, idx) => ({ t, idx, phase: aufgabenPhase(t, alleTodos) }))
.filter(e => !balkenPhase(e.t));
const todos = eintraege.map(e => e.t);
const fp = filterPhase();
const sichtbar = eintraege.filter(e => !fp || (fp === '-' ? !e.phase : e.phase === fp));
const filterEl = document.getElementById('planTodoFilter');
if(filterEl){
const phasen = LPHS.filter(l => eintraege.some(e => e.phase === l));
const knopf = (wert, text) => `<button type="button" class="plan-task-filter-btn${fp === wert ? ' is-active' : ''}" onclick="setzeAufgabenFilter('${esc(wert)}')">${esc(text)}</button>`;
filterEl.innerHTML = phasen.length
? '<span>Phase:</span>' + knopf('', 'Alle') + phasen.map(l => knopf(l, l)).join('')
+ (eintraege.some(e => !e.phase) ? knopf('-', 'ohne Phase') : '')
: '';
}
if(todoEl){
// In gesperrten Sitzungen sind die Anlege-Schaltflächen ausgeblendet
// (renderPlanPage(), plan-state.js) — dann darf der Leertext auch nicht
@@ -95,7 +125,9 @@
: 'Noch keine Aufgaben. Über „+ Aufgabe“, „+ Vorlage“ oder „+ Terminplan“ anlegen.';
todoEl.innerHTML = todos.length === 0
? '<div class="plan-task-empty">' + leerTextTodos + '</div>'
: todos.map((t, idx) => {
: sichtbar.length === 0
? '<div class="plan-task-empty">Keine Aufgaben in dieser Auswahl.</div>'
: sichtbar.map(({ t, idx, phase }) => {
const status = todoStatus(t);
const range = dateRangeText(t);
const overdue = isOverdue(t);
@@ -109,6 +141,8 @@
${check}
<span class="plan-task-dot ${status}"></span>
<span class="plan-task-title">${esc(t.title || 'Aufgabe')}</span>
${t.notiz ? `<span class="plan-task-notiz" title="${esc(String(t.notiz).slice(0, 300))}">${icon('fileText')}</span>` : ''}
${phase ? `<span class="plan-task-phase" title="Liegt im Zeitraum dieser Leistungsphase">${esc(phase)}</span>` : ''}
${assigneeAvatars(t.assignees)}
${progress}
<span class="plan-task-meta${overdue ? ' overdue' : ''}" title="${range ? '' : 'Ohne Termin — erscheint nicht im Terminplan'}">${range ? esc(range) : '<span class="plan-task-no-schedule">ohne Termin</span>'}</span>