118 lines
4.5 KiB
JavaScript
118 lines
4.5 KiB
JavaScript
/**
|
|
* Project Board JavaScript
|
|
* Handles drag and drop reordering for kanban board
|
|
*/
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
// Initialize drag and drop when DOM is ready
|
|
document.addEventListener('DOMContentLoaded', initDragAndDrop);
|
|
|
|
// Re-initialize after HTMX swaps
|
|
document.body.addEventListener('htmx:afterSwap', function(evt) {
|
|
initDragAndDrop();
|
|
});
|
|
|
|
function initDragAndDrop() {
|
|
const taskLists = document.querySelectorAll('[id^="phase-tasks-"]');
|
|
|
|
taskLists.forEach(list => {
|
|
const tasks = list.querySelectorAll('.task-item');
|
|
|
|
tasks.forEach(task => {
|
|
task.draggable = true;
|
|
|
|
task.addEventListener('dragstart', function(e) {
|
|
e.dataTransfer.effectAllowed = 'move';
|
|
e.dataTransfer.setData('text/plain', task.dataset.taskId);
|
|
task.classList.add('dragging');
|
|
});
|
|
|
|
task.addEventListener('dragend', function() {
|
|
task.classList.remove('dragging');
|
|
document.querySelectorAll('.task-item').forEach(t => t.classList.remove('drag-over'));
|
|
});
|
|
|
|
task.addEventListener('dragover', function(e) {
|
|
e.preventDefault();
|
|
e.dataTransfer.dropEffect = 'move';
|
|
|
|
const draggingTask = document.querySelector('.task-item.dragging');
|
|
if (draggingTask && draggingTask !== task) {
|
|
task.classList.add('drag-over');
|
|
}
|
|
});
|
|
|
|
task.addEventListener('dragleave', function() {
|
|
task.classList.remove('drag-over');
|
|
});
|
|
|
|
task.addEventListener('drop', function(e) {
|
|
e.preventDefault();
|
|
task.classList.remove('drag-over');
|
|
|
|
const draggedId = e.dataTransfer.getData('text/plain');
|
|
const draggedTask = document.querySelector(`[data-task-id="${draggedId}"]`);
|
|
|
|
if (draggedTask && draggedTask !== task) {
|
|
const list = task.parentElement;
|
|
const rect = task.getBoundingClientRect();
|
|
const midpoint = rect.top + rect.height / 2;
|
|
|
|
if (e.clientY < midpoint) {
|
|
list.insertBefore(draggedTask, task);
|
|
} else {
|
|
list.insertBefore(draggedTask, task.nextSibling);
|
|
}
|
|
|
|
// Update order via HTMX
|
|
updateTaskOrder(list);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Allow dropping at the end of the list
|
|
list.addEventListener('dragover', function(e) {
|
|
e.preventDefault();
|
|
const draggingTask = document.querySelector('.task-item.dragging');
|
|
if (draggingTask) {
|
|
list.appendChild(draggingTask);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function updateTaskOrder(list) {
|
|
const tasks = list.querySelectorAll('.task-item');
|
|
const taskIds = Array.from(tasks).map(t => t.dataset.taskId);
|
|
|
|
// Get phase info from list ID
|
|
const listId = list.id; // e.g., "phase-tasks-pdca-plan"
|
|
const parts = listId.split('-');
|
|
const phaseType = parts[2];
|
|
const phaseKey = parts[3];
|
|
|
|
// Update each task's order
|
|
taskIds.forEach((taskId, index) => {
|
|
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]')?.value;
|
|
|
|
fetch(`/projects/${getProjectId()}/htmx/tasks/${taskId}/reorder/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'X-CSRFToken': csrfToken || '',
|
|
},
|
|
body: `order=${index}`
|
|
}).catch(err => console.error('Failed to update task order:', err));
|
|
});
|
|
}
|
|
|
|
function getProjectId() {
|
|
// Extract project ID from URL
|
|
const match = window.location.pathname.match(/\/projects\/([a-f0-9-]+)\//);
|
|
return match ? match[1] : '';
|
|
}
|
|
|
|
})();
|