code auf einzelne Datein aufgeteilt

This commit is contained in:
erik committed 2026-08-13 16:07:29 +02:00
1 parent 7451e0bf78
commit 8c94fc91f6
30 files changed
+5765 -27

No files matched your search

+173
View File
@@ -0,0 +1,173 @@
// -----------------------------
// Projektübersicht — Projekte (Listen-Ansicht)
// -----------------------------
// Diese Datei rendert den Projekte-Tab (Tab "Projekte" in `Test.html`) als
// sortierbare Listenansicht: Projekte werden nach Gruppe ("Aktive Projekte",
// "Kleine Projekte" usw.) gruppiert, inline bearbeitbar und per
// Drag-and-Drop umsortiert. Sie wird von `Test.html` nach `js/people.js`
// (vor `js/load.js`) geladen und stellt globale Funktionen wie
// `renderProjects()`, `editProjectField()`, `editProjectActiveLP()` sowie die
// Drag-and-Drop-Handler und die Sortier-Hilfe `getDragAfterElement()` bereit.
// Letztere wird auch von `js/people.js` (Personen-Sortierung) genutzt;
// `renderBoard()` aus `js/board.js` wird nach Änderungen aktualisiert.
// delProjectFromList: löscht ein Projekt (per Bestätigungsdialog) in der
// Listen-Ansicht. Parameter `id`: Projekt-ID. Keine Rückgabe; bei gesperrter
// Sitzung abgebrochen.
function delProjectFromList(id){
if(guardWeekLocked()) return;
if(!confirm("Projekt aus dieser Sitzung löschen?")) 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).
// Keine Rückgabe.
function editProjectField(id,field,val){
if(guardWeekLocked()) return;
const p = currentWeek().projects.find(x=>x.id===id);
if(p){ 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();
}
// 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(){
const w = currentWeek();
if(!w) return;
let html="";
const groups = {};
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 += `<div class="section-title">${esc(g)}</div>`;
html += `<div class="projects-sortable" data-group="${g}">`;
groups[g].forEach(p=>{
html += `<div class="person-list-item" draggable="true" data-project-id="${p.id}" ondragstart="handleDragStart(event)" ondragover="handleDragOver(event)" ondrop="handleDrop(event)" ondragend="handleDragEnd(event)">
<span class="drag-handle" style="cursor:grab;color:#999;margin-right:8px">⋮⋮</span>
<input type="text" value="${esc(p.nr)}" onchange="editProjectField('${p.id}','nr',this.value)" style="width:100px" placeholder="Proj-Nr">
<input type="text" value="${esc(p.name)}" onchange="editProjectField('${p.id}','name',this.value)" style="width:200px" placeholder="Bezeichnung">
<select onchange="editProjectField('${p.id}','group',this.value)">
<option ${p.group==='Aktive Projekte'?'selected':''}>Aktive Projekte</option>
<option ${p.group==='Kleine Projekte'?'selected':''}>Kleine Projekte</option>
<option ${p.group==='In Anbahnung'?'selected':''}>In Anbahnung</option>
<option ${p.group==='Allgemein'?'selected':''}>Allgemein</option>
</select>
<select onchange="editProjectActiveLP('${p.id}',this.value)" title="Aktive Leistungsphase wählen">
<option value="" ${lpActiveList(p).length===0?'selected':''}>Aktive LP: keine</option>
${AUFWAND_LPS.map(l => `<option value="${l}" ${lpActiveList(p)[0]===l?'selected':''}>${l}</option>`).join('')}
</select>
<button class="secondary small" onclick="openLpModal('${p.id}')">✎ LP-Aufwand</button>
<button class="danger small" onclick="delProjectFromList('${p.id}')">Löschen</button>
</div>`;
});
html += `</div>`;
if(groups[g].length===0) html+=`<div class="hint">Keine Projekte.</div>`;
});
document.getElementById('projectsList').innerHTML=html;
}
// Drag-and-Drop Handling für Projekte
// draggedProject: die während eines Drag-and-Drop-Vorgangs gezogene
// Projekt-Zeile (DOM-Element) bzw. null nach dem Loslassen.
let draggedProject = null;
// handleDragStart: startet das Ziehen einer Projekt-Zeile, merkt sich das
// Element in `draggedProject` und verringert seine Deckkraft.
function handleDragStart(event){
draggedProject = event.currentTarget;
event.dataTransfer.effectAllowed = 'move';
event.currentTarget.style.opacity = '0.5';
}
// handleDragOver: erlaubt das Ablegen während des Überfahrens eines Ziels
// (verhindert Standardverhalten, setzt `dropEffect='move'`).
function handleDragOver(event){
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}
// handleDrop: legt die gezogene Projekt-Zeile an der Position ab, die
// `getDragAfterElement` relativ zur Mausposition ermittelt, und speichert
// die neue Reihenfolge über `reorderProjects()`.
function handleDrop(event){
event.preventDefault();
if(!draggedProject || draggedProject === event.currentTarget) return;
const sortable = event.currentTarget.parentElement;
const afterElement = getDragAfterElement(sortable, event.clientY);
if(afterElement == null){
sortable.appendChild(draggedProject);
} else {
sortable.insertBefore(draggedProject, afterElement);
}
reorderProjects();
}
// handleDragEnd: stellt die Deckkraft der gezogenen Zeile wieder her und
// setzt `draggedProject` zurück (Ende des Drag-Vorgangs).
function handleDragEnd(event){
event.currentTarget.style.opacity = '1';
draggedProject = null;
}
// getDragAfterElement: ermittelt das Element in `container`, vor dem die
// gezogene Zeile bei Mausposition `y` abgelegt werden soll (Suche nach dem
// Element mit der kleinsten negativen Distanz zur Mitte). Rückgabe:
// Ziel-Element oder undefined (→ ans Ende anfügen). Wird auch von
// `js/people.js` für die Personen-Sortierung verwendet.
function getDragAfterElement(container, y){
const draggableElements = [...container.querySelectorAll('.person-list-item:not([style*="opacity"])')];
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if(offset < 0 && offset > closest.offset){
return { offset: offset, element: child };
} else {
return closest;
}
}, { offset: Number.NEGATIVE_INFINITY }).element;
}
// reorderProjects: übernimmt die im DOM vorgefundene Reihenfolge der
// Projekt-Zeilen in die Daten (je Gruppe `order`-Werte) und aktualisiert
// Gruppe/Zuordnung pro Projekt. Keine Parameter, keine Rückgabe; bei
// gesperrter Sitzung abgebrochen.
function reorderProjects(){
if(guardWeekLocked()) return;
const w = currentWeek();
const sortables = document.querySelectorAll('.projects-sortable');
sortables.forEach(sortable => {
const group = sortable.dataset.group;
const items = sortable.querySelectorAll('[data-project-id]');
items.forEach((item, index) => {
const projectId = item.dataset.projectId;
const project = w.projects.find(p => p.id === projectId);
if(project){
project.order = index;
project.group = group;
}
});
});
save();
renderProjects();
renderBoard();
}