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:
1 parent
f4fb3c550b
commit
b7e0bd0d92
56 files changed
+12265
-2974
No files matched your search
+109
-5
@@ -150,6 +150,9 @@ function buildGanttHtml(projects, start, end, options = {}){
|
||||
|
||||
// Kopfzeile: Monate und Tage
|
||||
let headerMonths = '<div class="gantt-row gantt-header">';
|
||||
// Den Griff zum Ändern der Spaltenbreite setzt ganttGriffeEinsetzen() nach
|
||||
// dem Rendern in JEDE Zeile (auch die nachträglich eingefügten Aufgaben-
|
||||
// und Meilensteinzeilen aus js/plan/plan-build-gantt.js).
|
||||
headerMonths += '<div class="gantt-label">Projekt</div>';
|
||||
let monthsHtml = '';
|
||||
let current = new Date(start.getTime());
|
||||
@@ -280,10 +283,11 @@ function buildGanttHtml(projects, start, end, options = {}){
|
||||
}
|
||||
}
|
||||
|
||||
// Meilensteine: nur die nicht positionierten (keine position) in der Projektzeile anzeigen.
|
||||
// Positionierte Meilensteine werden vom Aufrufer (z.B. Projektplan) als separate Zeilen eingefügt.
|
||||
// Meilensteine in der Projektzeile nur ohne showTodos. Im Projektplan
|
||||
// bekommt jeder Meilenstein eine eigene Zeile (plan-build-gantt.js) —
|
||||
// sonst lagen mehrere ohne Aufgabenbezug übereinander in einer Zeile.
|
||||
milestones.forEach((m, mIdx) => {
|
||||
if(options.showTodos && m.position) return;
|
||||
if(options.showTodos) return;
|
||||
const d = parseDateISO(m.date);
|
||||
if(!d || d < start || d > end) return;
|
||||
const offset = daysBetween(start, d);
|
||||
@@ -298,7 +302,7 @@ function buildGanttHtml(projects, start, end, options = {}){
|
||||
// Zoom breiter als der Container wird und horizontal gescrollt werden kann.
|
||||
let chartStyle = '';
|
||||
if(pxPerDay){
|
||||
chartStyle = ` style="min-width:${(220 + Math.ceil(totalDays * pxPerDay)).toFixed(0)}px"`;
|
||||
chartStyle = ` style="min-width:${(ganttSpaltenBreite() + Math.ceil(totalDays * pxPerDay)).toFixed(0)}px"`;
|
||||
}
|
||||
return `<div class="gantt-chart"${chartStyle}>${body}</div>`;
|
||||
}
|
||||
@@ -345,4 +349,104 @@ function springZuEinstellungen(pid){
|
||||
function closePlanPage(){
|
||||
const back = (typeof previousTab === 'string' && previousTab && previousTab !== 'plan') ? previousTab : 'board';
|
||||
showTab(back);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Breite der Spalte "Projekt" (Eriks Wunsch: "wie in Excel") ----------
|
||||
//
|
||||
// Die Breite steht in der CSS-Variable --gantt-label-w (styles.css,
|
||||
// .gantt-label), damit alle Zeilen beim Ziehen gleichzeitig mitgehen, ohne
|
||||
// das Diagramm neu aufzubauen. Gemerkt wird sie je Browser in localStorage —
|
||||
// reiner Darstellungszustand wie die eingeklappten Dashboard-Projekte.
|
||||
const GANTT_SPALTE_KEY = 'projuebersicht-gantt-spalte';
|
||||
const GANTT_SPALTE_MIN = 120, GANTT_SPALTE_MAX = 640, GANTT_SPALTE_STANDARD = 220;
|
||||
|
||||
// ganttSpaltenBreite: aktuelle Breite in Pixeln (gespeichert oder Standard).
|
||||
function ganttSpaltenBreite(){
|
||||
let w = GANTT_SPALTE_STANDARD;
|
||||
try { w = Number(localStorage.getItem(GANTT_SPALTE_KEY)) || GANTT_SPALTE_STANDARD; } catch(e){ /* Standard */ }
|
||||
return Math.max(GANTT_SPALTE_MIN, Math.min(GANTT_SPALTE_MAX, w));
|
||||
}
|
||||
// ganttSpalteSetzen: Breite anwenden; speichern=true merkt sie dauerhaft.
|
||||
function ganttSpalteSetzen(w, speichern){
|
||||
w = Math.round(Math.max(GANTT_SPALTE_MIN, Math.min(GANTT_SPALTE_MAX, w)));
|
||||
document.documentElement.style.setProperty('--gantt-label-w', w + 'px');
|
||||
if(speichern){ try { localStorage.setItem(GANTT_SPALTE_KEY, String(w)); } catch(e){ /* nur Komfort */ } }
|
||||
}
|
||||
ganttSpalteSetzen(ganttSpaltenBreite(), false);
|
||||
|
||||
// ganttGriffeEinsetzen: hängt an den rechten Rand jeder Namenszelle einen
|
||||
// Griff — die Spaltengrenze lässt sich so in jeder Zeile fassen, nicht nur in
|
||||
// der Kopfzeile (Eriks Wunsch). click/dblclick werden abgefangen, damit ein
|
||||
// Klick auf die Grenze nicht zusätzlich die Aufgabe der Zeile öffnet.
|
||||
// Parameter: root (Element mit dem Diagramm). Keine Rückgabe.
|
||||
function ganttGriffeEinsetzen(root){
|
||||
if(!root) return;
|
||||
root.querySelectorAll('.gantt-chart .gantt-label').forEach(label => {
|
||||
if(label.querySelector(':scope > .gantt-spalten-griff')) return;
|
||||
const g = document.createElement('span');
|
||||
g.className = 'gantt-spalten-griff';
|
||||
g.setAttribute('role', 'separator');
|
||||
g.setAttribute('aria-orientation', 'vertical');
|
||||
g.title = 'Spaltenbreite ziehen, Doppelklick passt sie an';
|
||||
g.addEventListener('pointerdown', ganttSpalteZiehen);
|
||||
g.addEventListener('mousedown', e => e.stopPropagation());
|
||||
g.addEventListener('click', e => { e.stopPropagation(); e.preventDefault(); });
|
||||
g.addEventListener('dblclick', e => { e.stopPropagation(); e.preventDefault(); ganttSpalteAnpassen(); });
|
||||
label.appendChild(g);
|
||||
});
|
||||
}
|
||||
|
||||
// ganttPfeileNachziehen: zeichnet die Abhängigkeitspfeile neu, höchstens
|
||||
// einmal je Bild. Die Pfeile sind ein SVG mit festen Pixelkoordinaten; ohne
|
||||
// das blieben sie beim Ziehen der Spalte an der alten Stelle stehen, während
|
||||
// die Balken mitwandern.
|
||||
let ganttPfeileGeplant = false;
|
||||
function ganttPfeileNachziehen(){
|
||||
if(ganttPfeileGeplant || typeof renderGanttDependencies !== 'function') return;
|
||||
ganttPfeileGeplant = true;
|
||||
requestAnimationFrame(() => { ganttPfeileGeplant = false; renderGanttDependencies(); });
|
||||
}
|
||||
|
||||
// ganttNeuZeichnen: nach dem Ändern einmal neu aufbauen — die Mindestbreite
|
||||
// des Diagramms bei hohem Zoom hängt von der Spaltenbreite ab.
|
||||
function ganttNeuZeichnen(){
|
||||
if(typeof renderPlanGantt === 'function' && typeof currentTab !== 'undefined' && currentTab === 'plan') renderPlanGantt();
|
||||
}
|
||||
|
||||
// ganttSpalteZiehen: Ziehen am Griff (Maus, Stift, Touch). Parameter: e
|
||||
// (pointerdown-Ereignis). Keine Rückgabe.
|
||||
function ganttSpalteZiehen(e){
|
||||
if(e.button !== undefined && e.button !== 0) return;
|
||||
e.preventDefault(); e.stopPropagation();
|
||||
const griff = e.currentTarget;
|
||||
const startX = e.clientX, startW = ganttSpaltenBreite();
|
||||
griff.setPointerCapture && griff.setPointerCapture(e.pointerId);
|
||||
document.body.classList.add('gantt-spalte-zieht');
|
||||
let w = startW;
|
||||
const bewegen = ev => { w = startW + (ev.clientX - startX); ganttSpalteSetzen(w, false); ganttPfeileNachziehen(); };
|
||||
const ende = () => {
|
||||
griff.removeEventListener('pointermove', bewegen);
|
||||
griff.removeEventListener('pointerup', ende);
|
||||
griff.removeEventListener('pointercancel', ende);
|
||||
document.body.classList.remove('gantt-spalte-zieht');
|
||||
ganttSpalteSetzen(w, true);
|
||||
ganttNeuZeichnen();
|
||||
};
|
||||
griff.addEventListener('pointermove', bewegen);
|
||||
griff.addEventListener('pointerup', ende);
|
||||
griff.addEventListener('pointercancel', ende);
|
||||
}
|
||||
|
||||
// ganttSpalteAnpassen: Doppelklick auf den Griff — Breite an den längsten
|
||||
// Eintrag anpassen (wie der Doppelklick auf die Spaltengrenze in Excel).
|
||||
function ganttSpalteAnpassen(){
|
||||
let max = 0;
|
||||
document.querySelectorAll('.gantt-chart .gantt-label').forEach(el => {
|
||||
// scrollWidth = volle Inhaltsbreite samt Innenabstand, auch wenn der
|
||||
// Text gerade mit "…" abgeschnitten ist.
|
||||
max = Math.max(max, el.scrollWidth);
|
||||
});
|
||||
if(!max) return;
|
||||
ganttSpalteSetzen(max + 6, true);
|
||||
ganttNeuZeichnen();
|
||||
}
|
||||
Reference in new issue
Block a user