feat: add SortableJS and wrap kanban initialization in a robustness handler for delayed script loading

This commit is contained in:
Ümit Tunç
2026-07-30 17:37:42 +03:00
parent 483b0518fd
commit cc750fcb1e
+57 -41
View File
@@ -15,6 +15,9 @@
<!-- Tailwind CSS --> <!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<!-- Sortable JS for Drag and Drop -->
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
<script> <script>
tailwind.config = { tailwind.config = {
darkMode: 'class', darkMode: 'class',
@@ -860,52 +863,65 @@ gantt
} }
// Sortable.js Drag and Drop Setup for 4 Kanban Columns // Sortable.js Drag and Drop Setup for 4 Kanban Columns
document.querySelectorAll('.kanban-drop-zone').forEach(zone => { function initKanbanSortable() {
new Sortable(zone, { if (typeof Sortable === 'undefined') {
group: 'kanban-board', setTimeout(initKanbanSortable, 100);
animation: 200, return;
ghostClass: 'sortable-ghost', }
dragClass: 'sortable-drag',
onEnd: function(evt) {
const itemEl = evt.item;
const targetZone = evt.to;
const taskId = itemEl.getAttribute('data-task-id');
const newStatus = targetZone.getAttribute('data-status');
if (!taskId || !newStatus) return; document.querySelectorAll('.kanban-drop-zone').forEach(zone => {
new Sortable(zone, {
group: 'kanban-board',
animation: 200,
ghostClass: 'sortable-ghost',
dragClass: 'sortable-drag',
onEnd: function(evt) {
const itemEl = evt.item;
const targetZone = evt.to;
const taskId = itemEl.getAttribute('data-task-id');
const newStatus = targetZone.getAttribute('data-status');
fetch('{{ route("projects.admin.task-status", $project->slug) }}', { if (!taskId || !newStatus) return;
method: 'POST',
headers: { fetch('{{ route("projects.admin.task-status", $project->slug) }}', {
'Content-Type': 'application/json', method: 'POST',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), headers: {
'Accept': 'application/json', 'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest' 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
}, 'Accept': 'application/json',
body: JSON.stringify({ 'X-Requested-With': 'XMLHttpRequest'
task_id: taskId, },
status: newStatus body: JSON.stringify({
task_id: taskId,
status: newStatus
})
}) })
}) .then(res => res.json())
.then(res => res.json()) .then(data => {
.then(data => { if (data.success) {
if (data.success) { updateProgressUI(data.progress_percent);
updateProgressUI(data.progress_percent); if (data.counts) {
if (data.counts) { updateCountsUI(data.counts);
updateCountsUI(data.counts); }
showToast(data.message, 'success');
} else {
showToast('Görev taşınamadı.', 'error');
} }
showToast(data.message, 'success'); })
} else { .catch(err => {
showToast('Görev taşınamadı.', 'error'); console.error('AJAX Error:', err);
} showToast('Bağlantı hatası.', 'error');
}) });
.catch(err => { }
console.error('AJAX Error:', err); });
showToast('Bağlantı hatası.', 'error');
});
}
}); });
}); }
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initKanbanSortable);
} else {
initKanbanSortable();
}
// AJAX Module Status Toggle Handler // AJAX Module Status Toggle Handler
function setModuleStatusAjax(moduleId, status) { function setModuleStatusAjax(moduleId, status) {