// ----------------------------- // Projektübersicht — Projektliste (Ablage-Logik) // ----------------------------- // Der eigene Tab "Projekte" ist entfallen. Er listete dieselben Projekte ein // zweites Mal auf und ließ dieselben Angaben — Nummer, Bezeichnung, Gruppe, // amtierende Leistungsphase, Schlagwörter, Aufwand — ein zweites Mal // bearbeiten. Diese Angaben haben ihren einen Ort jetzt im Projektbereich // (js/plan/plan-settings.js, js/plan/plan-info.js); die Liste selbst ist im // Projektboard aufgegangen (renderBoard(), js/board/board.js). // // Was hier bleibt, betrifft die LISTE, nicht die einzelnen Angaben: // // * delProjectFromList() — Projekt aus der Sitzung löschen // * die Drag-and-Drop-Handler + reorderProjects() — Reihenfolge innerhalb // einer Gruppe und Wechsel zwischen Gruppen. Das Board spannt dieselbe // Ablagefläche auf (.projects-sortable mit data-group), damit diese // Handler unverändert weiterarbeiten. // * getDragAfterElement() — Sortier-Hilfe, auch von js/people/people.js // genutzt // // editProjectField()/editProjectActiveLP()/addProjectTag()/removeProjectTag() // stehen weiterhin bereit: Der Beauftragungs-Dialog und die Auslastungs-Maske // rufen sie auf. renderProjects() rendert nur noch, wenn ein Element // #projectsList vorhanden ist — sonst übernimmt renderBoard(). // delProjectFromList: löscht ein Projekt (per Bestätigungsdialog) in der // Listen-Ansicht. Parameter `id`: Projekt-ID. Keine Rückgabe; bei gesperrter // Sitzung abgebrochen. Nur für Projektleiter und Administratoren // (darfProjektlisteAendern(), js/auth.js; serverseitig in /save-week // durchgesetzt). Der Server legt das Projekt beim Speichern in den // Papierkorb, aus dem es ein Administrator 30 Tage lang zurückholen kann // (Admin → Papierkorb, js/admin/admin.js). function delProjectFromList(id){ if(guardWeekLocked()) return; if(!darfProjektlisteAendern()){ alert('Projekte löschen dürfen nur Projektleiter und Administratoren.'); return; } const p = currentWeek().projects.find(x => x.id === id); const bez = p ? ((p.nr ? p.nr + ' ' : '') + (p.name || '')).trim() : ''; if(!confirm('Projekt ' + (bez ? '„' + bez + '" ' : '') + 'löschen?\n\nEin Administrator kann es 30 Tage lang wiederherstellen.')) return; const w = currentWeek(); w.projects = w.projects.filter(p=>p.id!==id); save(); renderProjects(); renderBoard(); } // editProjectField: ändert ein Feld eines Projekts (z.B. Name oder Gruppe). // Parameter: `id` (Projekt-ID), `field` (Feldname), `val` (neuer Wert). // Beim Wechsel der Gruppe wird das Projekt ans Ende der Zielgruppe angehängt // (neue `order` = höchste Order der Zielgruppe + 1), damit es nicht anhand // seiner alten `order` in der Liste landet. Keine Rückgabe. function editProjectField(id,field,val){ if(guardWeekLocked()) return; const w = currentWeek(); const p = w.projects.find(x=>x.id===id); if(!p) return; if(field === 'group' && val && val !== p.group){ p.group = val; const targetProjects = w.projects.filter(x => x.id !== id && x.group === val); const maxOrder = targetProjects.reduce((m,x)=>Math.max(m, x.order || 0), -1); p.order = maxOrder + 1; } else { p[field] = val; } save(); renderProjects(); renderBoard(); } // editProjectActiveLP: setzt bzw. hebt die amtierende Leistungsphase eines // Projekts auf (`p.aktuelleLP`). Parameter: `id` (Projekt-ID), `value` // (LPH-Name oder leer). Aktualisiert Projekte, Board und Auslastung. function editProjectActiveLP(id, value){ if(guardWeekLocked()) return; const p = currentWeek().projects.find(x=>x.id===id); if(!p) return; if(value) p.aktuelleLP = [value]; else delete p.aktuelleLP; save(); renderProjects(); renderBoard(); renderLoad(); } // addProjectTag: fügt dem Projekt einen neuen Tag hinzu (getrimmt, nicht // leer, keine exakten Duplikate). Initialisiert `p.tags`, falls es fehlt // oder kein Array ist. Parameter: `projectId`, `tag` (roher Eingabetext). // Keine Rückgabe; bei gesperrter Sitzung abgebrochen. function addProjectTag(projectId, tag){ if(guardWeekLocked()) return; const val = String(tag || '').trim(); if(!val) return; const w = currentWeek(); const p = w.projects.find(x=>x.id===projectId); if(!p) return; if(!Array.isArray(p.tags)) p.tags = []; if(p.tags.includes(val)) return; p.tags.push(val); save(); renderProjects(); renderBoard(); } // removeProjectTag: entfernt einen Tag aus dem Projekt (exakter Textvergleich). // Parameter: `projectId`, `tag`. Keine Rückgabe; bei gesperrter Sitzung // abgebrochen. function removeProjectTag(projectId, tag){ if(guardWeekLocked()) return; const w = currentWeek(); const p = w.projects.find(x=>x.id===projectId); if(!p) return; p.tags = (Array.isArray(p.tags) ? p.tags : []).filter(t => t !== tag); save(); renderProjects(); renderBoard(); } // handleProjectTagKeydown: fügt bei Enter oder Komma im Tag-Eingabefeld den // aktuellen Feldinhalt als neuen Tag hinzu (das Eingabefeld wird ohnehin // beim anschließenden renderProjects() frisch/leer neu aufgebaut). Parameter: // `event` (KeyboardEvent), `projectId`. Keine Rückgabe. function handleProjectTagKeydown(event, projectId){ if(event.key !== 'Enter' && event.key !== ','){ return; } event.preventDefault(); addProjectTag(projectId, event.target.value); } // renderProjects: baut die HTML-Liste der Projekte auf, gruppiert nach `group` // und sortiert je Gruppe nach `order`. Jedes Projekt erhält Felder für Nr., // Name, Gruppe, aktive LP sowie Drag-and-Drop-Handler. Keine Parameter, keine // Rückgabe. function renderProjects(){ // Ohne #projectsList gibt es den eigenen Projekte-Bereich nicht mehr — die // Liste steht im Projektboard. Der Aufruf bleibt dennoch überall stehen // (renderAll(), editProjectField(), …), damit kein Pfad ins Leere greift. const listEl = document.getElementById('projectsList'); if(!listEl) return; const w = currentWeek(); if(!w) return; const locked = isWeekLocked(w); let html=""; const groups = {}; // Alle Standardgruppen vorbelegen: Ohne das entsteht für eine leer geräumte // Gruppe weder Überschrift noch Ablagefläche — ein Projekt ließ sich dann // nur noch über das Auswahlfeld zurückverschieben, und der Hinweis // "Keine Projekte." unten war toter Code. ['Aktive Projekte','Kleine Projekte','In Anbahnung','Allgemein'].forEach(g => { groups[g] = []; }); w.projects.forEach(p=>{ (groups[p.group]||(groups[p.group]=[])).push(p); }); Object.keys(groups).forEach(g=>{ groups[g].sort((a,b)=>(a.order||0)-(b.order||0)); }); sortGroups(Object.keys(groups)).forEach(g=>{ html += `