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

+18 -9
View File
@@ -130,14 +130,11 @@
const todoRowIds = rows.filter(r => r.classList.contains('gantt-todo')).map(r => r.dataset.todoId).filter(Boolean);
const milestoneRowIds = rows.filter(r => r.classList.contains('gantt-milestone-row')).map(r => r.dataset.milestoneId).filter(Boolean);
// Sicherheitscheck: Anzahl muss stimmen. Nicht jedes Meilenstein wird als
// eigene .gantt-milestone-row gerendert – nur die mit einer TODO-basierten
// Position (before:/after:) sind eigene Zeilen. Meilensteine ohne Position
// liegen in der Projektzeile und werden hier nicht umsortiert, deshalb nur
// die positionierten zählen.
// Sicherheitscheck: Anzahl muss stimmen. Jeder Meilenstein hat eine
// eigene .gantt-milestone-row (auch ohne Position, dann oben unter
// "Projekt gesamt").
if(todoRowIds.length !== scheduledIds.size) return;
const positionedIds = milestones.filter(m => m.position && /^(before|after):/.test(m.position)).map(m => m.id);
if(milestoneRowIds.length !== positionedIds.length) return;
if(milestoneRowIds.length !== milestones.length) return;
// TODOs neu ordnen: nur die Positionen der terminierten Aufgaben werden
// gemäß der neuen DOM-Reihenfolge neu belegt (todoRowIds); Aufgaben ohne
@@ -153,7 +150,7 @@
// Regeln:
// - Steht der Meilenstein direkt vor einer TODO -> before:todoId
// - Steht er direkt nach einer TODO -> after:todoId
// - Steht er ganz am Anfang (vor erster TODO) oder es gibt keine TODO -> '' (oben in Projektzeile)
// - Steht er ganz am Anfang (vor erster TODO) oder es gibt keine TODO -> '' (eigene Zeile oben)
// - Steht er ganz am Ende (nach letzter TODO) -> after:lastTodoId
rows.forEach((row, idx) => {
if(!row.classList.contains('gantt-milestone-row')) return;
@@ -165,7 +162,11 @@
const prevRow = rows[idx - 1];
const nextRow = rows[idx + 1];
if(prevRow && prevRow.classList.contains('gantt-todo')){
const todoDavor = rows.slice(0, idx).some(r => r.classList.contains('gantt-todo'));
if(!todoDavor){
// Vor der ersten Aufgabe: ohne Position (eigene Zeile oben).
position = '';
} else if(prevRow && prevRow.classList.contains('gantt-todo')){
position = 'after:' + prevRow.dataset.todoId;
} else if(nextRow && nextRow.classList.contains('gantt-todo')){
position = 'before:' + nextRow.dataset.todoId;
@@ -449,6 +450,14 @@
const globalAbsence = globalPerson && Array.isArray(globalPerson.absences)
? globalPerson.absences.find(x => x.id === absenceId) : null;
if(!globalAbsence) return;
// Abwesenheit einer anderen Person: Art und Notiz kennt nur sie selbst
// (loadGlobalPeople() schickt sie verdeckt). Das Verschieben legt neu an
// und bräuchte die Art — sonst wäre der Eintrag nach dem Löschen weg.
if(globalAbsence.verdeckt){
app.showToast('Abwesenheiten anderer Personen lassen sich hier nicht verschieben.');
renderPlanGantt();
return;
}
const type = globalAbsence.type || '';
const note = globalAbsence.note || '';
// Ursprungstermine merken: Scheitert das Speichern (Netz, fehlende