Files

163 lines
6.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// -----------------------------
// 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/board/board.js, js/load/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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
// 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);
});
}
// peopleByRole: gruppiert Personen nach Rolle zurück in ein Objekt.
// Parameter `target`: Woche(n) mit `people`-Array (oder undefined → aktuelle
// Woche). Rückgabe: Objekt `{ Rolle: [{person, role}] }` mit Deduplizierung.
// Liegt im gemeinsamen Modul, weil board/load/pdf auf sie zugreifen.
function peopleByRole(target){
const m = {}; ROLES.forEach(r=>m[r]=[]);
const people = [];
if(Array.isArray(target)){
target.forEach(w => { if(w && Array.isArray(w.people)) people.push(...w.people); });
} else if(target && Array.isArray(target.people)){
people.push(...target.people);
} else if(Array.isArray(currentWeek()?.people)){
people.push(...currentWeek().people);
}
const seen = new Set();
people.forEach(p=>{
const personRoles = Array.isArray(p.role) ? p.role : [p.role];
personRoles.forEach(role=>{
if(!m[role]) m[role]=[];
const key = `${p.id}|${role}`;
if(!seen.has(key)){
seen.add(key);
m[role].push({person:p, role});
}
});
});
return m;
}
// weekSortKey: liefert einen nach Jahr und Kalenderwoche sortierbaren Schlüssel,
// sodass z.B. "KW 9 / 2026" vor "KW 10 / 2026" kommt.
// Parameter: label (String). Rückgabe: String (Jahr-KW) oder kleingeschriebenes Label.
function weekSortKey(label){
const m = String(label || '').match(/KW\s*(\d+)[^\d]+(\d{4})/i);
if(m){
const kw = m[1].padStart(2,'0');
return `${m[2]}-${kw}`;
}
return String(label || '').toLowerCase();
}
// 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);
}