// ----------------------------- // Projektübersicht — allgemeine Helferfunktionen // ----------------------------- // Diese Datei bündelt kleine, allgemein verwendbare Hilfsfunktionen (Escape-, // ID-, Sortier- und Datumsfunktionen) und wird nach js/globals.js von beiden // HTML-Einstiegspunkten geladen. Sie stellt ausschließlich globale Funktionen // ohne eigenen Zustand bereit; genutzt wird sie praktisch von allen Modulen, // u. a. js/csv.js, js/board.js, js/load.js sowie den Plan-Modulen unter js/plan/* // (z. B. für ISO-Datumsformate und Werktage) und von js/gantt.js. // departmentParam: gibt ein Query-String-Fragment fuer die aktuelle Abteilung zurueck. // Parameter: keine. Rückgabe: String wie '?department=ELT'. function departmentParam(){ return '?department=' + encodeURIComponent(currentDepartment); } // esc: einfache Escape-Funktion für Ausgabe in HTML // Parameter: s (String). Rückgabe: HTML-sicherer String (&, <, >, " ersetzt). function esc(s){ return (s||"").replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } // uid: generiert eine einfache, ausreichend eindeutige ID // Parameter: keine. Rückgabe: String aus Zeitstempel und Zufallsteil. function uid(){ return Date.now().toString(36)+Math.random().toString(36).slice(2,7); } // assignKey: Schlüssel für Projektzuordnungen (Person + Rolle getrennt) // Parameter: personId (String), role (String). Rückgabe: kombinierter Schlüssel 'personId|role'. function assignKey(personId, role){ return personId + '|' + role; } // sortGroups: sortiert Gruppen-Namen nach GROUP_ORDER, unbekannte ans Ende // Parameter: arr (Array von Gruppennamen, wird in-place sortiert). Rückgabe: dasselbe Array. function sortGroups(arr){ return arr.sort((a,b)=>{ const ia=GROUP_ORDER.indexOf(a), ib=GROUP_ORDER.indexOf(b); return (ia===-1?999:ia)-(ib===-1?999:ib); }); } // Persistente ID fuer eine Person aus dem Kuerzel, damit Anwesenheit und // Zuordnungen auch nach einem Neuladen stabil bleiben. // Parameter: name (String). Rückgabe: String wie 'p_ab'. function personIdForName(name){ return 'p_' + String(name).trim().toLowerCase().replace(/[^a-z0-9]/g,'_'); } // formatWeekDate: wandelt ein ISO-Datum (YYYY-MM-DD) in deutsches Format DD.MM.YYYY um // Parameter: dateString (String). Rückgabe: formatierter String oder leer. function formatWeekDate(dateString){ if(!dateString) return ''; const parts = dateString.split('-'); if(parts.length!==3) return dateString; return `${parts[2]}.${parts[1]}.${parts[0]}`; } // ---------- Datumshilfsfunktionen (GANTT) ---------- // parseDateISO: parst einen ISO-Datumsstring (YYYY-MM-DD) in ein Date-Objekt (lokal) // Parameter: d (String). Rückgabe: Date-Objekt oder null bei ungültigem Format. function parseDateISO(d){ if(!d) return null; const m = String(d).match(/^(\d{4})-(\d{2})-(\d{2})$/); if(!m) return null; const date = new Date(parseInt(m[1]), parseInt(m[2])-1, parseInt(m[3])); if(isNaN(date.getTime())) return null; return date; } // formatDateISO: formatiert ein Date-Objekt als ISO-Datum (YYYY-MM-DD) // Parameter: d (Date). Rückgabe: String oder leer bei ungültigem Datum. function formatDateISO(d){ if(!d || isNaN(d.getTime())) return ''; const y = d.getFullYear(); const m = String(d.getMonth()+1).padStart(2,'0'); const day = String(d.getDate()).padStart(2,'0'); return `${y}-${m}-${day}`; } // addDays: addiert n Tage zu einem Datum (ohne das Original zu verändern) // Parameter: d (Date), n (Anzahl Tage). Rückgabe: neues Date-Objekt. function addDays(d, n){ const res = new Date(d.getTime()); res.setDate(res.getDate() + n); return res; } // daysBetween: Anzahl der Kalendertage zwischen zwei Daten // Parameter: a, b (Date). Rückgabe: ganzzahlige Tagesdifferenz (b - a). function daysBetween(a, b){ const ms = b.getTime() - a.getTime(); return Math.round(ms / (1000 * 60 * 60 * 24)); } // workdaysBetween: Anzahl der Werktage (Mo–Fr) im Zeitraum inkl. Start- und Endtag. // Wochenendtage (Sa/So) werden nicht mitgezählt – z.B. für die Auslastung. // Parameter: a, b (Date). Rückgabe: Anzahl der Werktage oder 0 bei ungültigem Zeitraum. function workdaysBetween(a, b){ if(!a || !b || b < a) return 0; let count = 0; let current = new Date(a.getTime()); while(current <= b){ const day = current.getDay(); if(day !== 0 && day !== 6) count++; current = addDays(current, 1); } return count; } // formatDateDE: formatiert ein Date-Objekt als deutsches Datum (DD.MM.YYYY) // Parameter: d (Date). Rückgabe: String oder leer bei ungültigem Datum. function formatDateDE(d){ if(!d || isNaN(d.getTime())) return ''; return `${String(d.getDate()).padStart(2,'0')}.${String(d.getMonth()+1).padStart(2,'0')}.${d.getFullYear()}`; } // ISO-Kalenderwoche nach https://en.wikipedia.org/wiki/ISO_week_date // Parameter: d (Date). Rückgabe: Kalenderwochennummer (1-53). function getWeekNumber(d){ const date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate())); const dayNum = date.getUTCDay() || 7; date.setUTCDate(date.getUTCDate() + 4 - dayNum); const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1)); return Math.ceil((((date - yearStart) / 86400000) + 1) / 7); }