Statuskarten via Drag&Drop verschiebbar

Unteraufgaben lassen sich verschieben und bearbeiten
Dieser Commit ist enthalten in:
HG
2025-12-30 19:55:39 +00:00
committet von Server Deploy
Ursprung 15627cce99
Commit 9bf298c26b
20 geänderte Dateien mit 1609 neuen und 39 gelöschten Zeilen

Datei anzeigen

@ -179,12 +179,14 @@ class BoardManager {
const columnEl = createElement('div', {
className: 'column',
dataset: { columnId: column.id },
draggable: 'true'
dataset: { columnId: column.id }
});
// Header with column color
const header = createElement('div', { className: 'column-header' });
// Header with column color - draggable
const header = createElement('div', {
className: 'column-header',
draggable: 'true'
});
// Apply column color to header background
const columnColor = column.color || '#6B7280';
@ -536,6 +538,7 @@ class BoardManager {
handleDragStart(e) {
const taskCard = e.target.closest('.task-card');
const header = e.target.closest('.column-header');
const column = e.target.closest('.column');
if (taskCard) {
@ -548,7 +551,8 @@ class BoardManager {
type: 'task',
taskId: parseInt(taskCard.dataset.taskId)
});
} else if (column && e.target.closest('.column-header')) {
} else if (header && column) {
// Header wird gezogen -> ganze Spalte verschieben
this.draggedColumn = column;
column.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
@ -575,6 +579,10 @@ class BoardManager {
// Remove all drop indicators
$$('.column-body.drag-over').forEach(el => el.classList.remove('drag-over'));
$$('.drop-indicator').forEach(el => el.remove());
// Remove column drag indicators
$$('.column.drag-over-left, .column.drag-over-right').forEach(el => {
el.classList.remove('drag-over-left', 'drag-over-right');
});
store.setDragState(null);
}
@ -597,32 +605,44 @@ class BoardManager {
}
} else if (dragState.type === 'column') {
const column = e.target.closest('.column');
// Entferne alle vorherigen Drag-Over-Klassen
$$('.column.drag-over-left, .column.drag-over-right').forEach(col => {
if (col !== column) {
col.classList.remove('drag-over-left', 'drag-over-right');
}
});
if (column && column !== this.draggedColumn) {
const rect = column.getBoundingClientRect();
const midpoint = rect.left + rect.width / 2;
if (e.clientX < midpoint) {
column.style.borderLeft = '3px solid var(--accent)';
column.style.borderRight = '';
column.classList.add('drag-over-left');
column.classList.remove('drag-over-right');
} else {
column.style.borderRight = '3px solid var(--accent)';
column.style.borderLeft = '';
column.classList.add('drag-over-right');
column.classList.remove('drag-over-left');
}
}
}
}
handleDragLeave(e) {
const columnBody = e.target.closest('.column-body');
if (columnBody && !columnBody.contains(e.relatedTarget)) {
columnBody.classList.remove('drag-over');
$$('.drop-indicator', columnBody).forEach(el => el.remove());
}
const dragState = store.get('dragState');
const column = e.target.closest('.column');
if (column) {
column.style.borderLeft = '';
column.style.borderRight = '';
if (dragState?.type === 'task') {
const columnBody = e.target.closest('.column-body');
if (columnBody && !columnBody.contains(e.relatedTarget)) {
columnBody.classList.remove('drag-over');
$$('.drop-indicator', columnBody).forEach(el => el.remove());
}
} else if (dragState?.type === 'column') {
const column = e.target.closest('.column');
// Nur entfernen wenn wirklich die Spalte verlassen wird
if (column && !column.contains(e.relatedTarget)) {
column.classList.remove('drag-over-left', 'drag-over-right');
}
}
}
@ -664,18 +684,45 @@ class BoardManager {
this.moveTask(taskId, columnId, position);
} else if (dragState.type === 'column') {
const targetColumn = e.target.closest('.column');
if (!targetColumn || targetColumn === this.draggedColumn) return;
if (!targetColumn || targetColumn === this.draggedColumn) {
// Cleanup
$$('.column.drag-over-left, .column.drag-over-right').forEach(el => {
el.classList.remove('drag-over-left', 'drag-over-right');
});
return;
}
const columns = store.get('columns');
const fromIndex = columns.findIndex(c => c.id === dragState.columnId);
const toIndex = columns.findIndex(c => c.id === parseInt(targetColumn.dataset.columnId));
let toIndex = columns.findIndex(c => c.id === parseInt(targetColumn.dataset.columnId));
if (fromIndex !== -1 && toIndex !== -1) {
// Berechne Position basierend auf Maus-Position (links oder rechts der Ziel-Spalte)
const rect = targetColumn.getBoundingClientRect();
const midpoint = rect.left + rect.width / 2;
const dropOnRight = e.clientX > midpoint;
// Wenn rechts gedroppt und von links kommend, nach rechts verschieben
if (dropOnRight && fromIndex < toIndex) {
// toIndex bleibt gleich (Position nach der Ziel-Spalte)
} else if (dropOnRight && fromIndex > toIndex) {
// Von rechts kommend, rechts droppend -> nach der Ziel-Spalte
toIndex = toIndex + 1;
} else if (!dropOnRight && fromIndex > toIndex) {
// toIndex bleibt gleich (Position vor der Ziel-Spalte)
} else if (!dropOnRight && fromIndex < toIndex) {
// Von links kommend, links droppend -> vor der Ziel-Spalte
toIndex = toIndex - 1;
}
if (fromIndex !== -1 && toIndex !== -1 && fromIndex !== toIndex) {
this.reorderColumns(fromIndex, toIndex);
}
targetColumn.style.borderLeft = '';
targetColumn.style.borderRight = '';
// Cleanup
$$('.column.drag-over-left, .column.drag-over-right').forEach(el => {
el.classList.remove('drag-over-left', 'drag-over-right');
});
}
}
@ -948,7 +995,8 @@ class BoardManager {
store.reorderColumns(columnIds);
try {
await api.reorderColumns(projectId, columnIds);
// API erwartet: columnId und newPosition
await api.reorderColumns(projectId, moved.id, toIndex);
syncManager.notifyColumnsReordered(columnIds);
} catch (error) {
console.error('Failed to reorder columns:', error);