@@ -333,6 +348,83 @@ async function submitManualItem() {
if (data.success) {
location.reload();
}
+}
+
+// --- Drag & Drop van Roadmap Items naar ander Kwartaal (Q1, Q2, Q3, Q4) ---
+let draggedRoadmapItemId = null;
+
+function handleRoadmapItemDragStart(e, itemId) {
+ draggedRoadmapItemId = itemId;
+ try {
+ e.dataTransfer.effectAllowed = 'move';
+ e.dataTransfer.setData('text/plain', String(itemId));
+ } catch (err) {}
+
+ setTimeout(() => {
+ const el = document.getElementById(`roadmap-item-${itemId}`);
+ if (el) el.classList.add('opacity-40', 'scale-[0.99]');
+ }, 0);
+}
+
+function handleRoadmapItemDragEnd(e) {
+ if (draggedRoadmapItemId) {
+ const el = document.getElementById(`roadmap-item-${draggedRoadmapItemId}`);
+ if (el) el.classList.remove('opacity-40', 'scale-[0.99]');
+ draggedRoadmapItemId = null;
+ }
+
+ document.querySelectorAll('.roadmap-quarter-tab').forEach(tab => {
+ tab.classList.remove('ring-2', 'ring-indigo-400', 'scale-105', 'bg-indigo-100', 'dark:bg-indigo-900/60');
+ });
+}
+
+function handleRoadmapQuarterDragOver(e) {
+ if (!draggedRoadmapItemId) return;
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'move';
+ const tab = e.currentTarget;
+ if (tab && !tab.classList.contains('ring-2')) {
+ tab.classList.add('ring-2', 'ring-indigo-400', 'scale-105', 'bg-indigo-100', 'dark:bg-indigo-900/60');
+ }
+}
+
+function handleRoadmapQuarterDragLeave(e) {
+ const tab = e.currentTarget;
+ if (tab && !tab.contains(e.relatedTarget)) {
+ tab.classList.remove('ring-2', 'ring-indigo-400', 'scale-105', 'bg-indigo-100', 'dark:bg-indigo-900/60');
+ }
+}
+
+async function handleRoadmapQuarterDrop(e, targetQuarter) {
+ e.preventDefault();
+ const tab = e.currentTarget;
+ if (tab) {
+ tab.classList.remove('ring-2', 'ring-indigo-400', 'scale-105', 'bg-indigo-100', 'dark:bg-indigo-900/60');
+ }
+
+ const itemId = draggedRoadmapItemId || parseInt(e.dataTransfer.getData('text/plain'), 10);
+ draggedRoadmapItemId = null;
+
+ if (!itemId || !targetQuarter) return;
+
+ try {
+ const res = await fetch('/api/roadmap.php?action=update_item', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({ id: itemId, quarter: targetQuarter })
+ });
+ const data = await res.json();
+ if (data.success) {
+ location.reload();
+ } else {
+ alert(data.error || 'Kon item niet verplaatsen');
+ }
+ } catch (err) {
+ console.error('Fout bij verplaatsen roadmap item:', err);
+ alert('Netwerkfout bij verplaatsen');
+ }
+}
+
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const modal = document.getElementById('newItemModal');
diff --git a/timeline.php b/timeline.php
index 31ac026..0f6dd6c 100644
--- a/timeline.php
+++ b/timeline.php
@@ -164,13 +164,18 @@ html.dark #timelineGridWrapper::-webkit-scrollbar-thumb {
-
+
Werk
0
-
Status & Datum
+
Sleep taak naar lane / datum
+
+
+
+
+
@@ -528,6 +533,53 @@ function populateLaneFilter() {
sel.appendChild(opt);
});
}
+ renderTimelineLaneDropBar();
+}
+
+// Render drop targets voor snelle statuswijziging in het werkpaneel
+function renderTimelineLaneDropBar() {
+ const bar = document.getElementById('timelineLaneDropBar');
+ if (!bar) return;
+ bar.innerHTML = '';
+
+ const lanes = (currentBoardData && currentBoardData.lanes) ? currentBoardData.lanes : [];
+ if (lanes.length === 0) {
+ bar.classList.add('hidden');
+ return;
+ }
+ bar.classList.remove('hidden');
+
+ lanes.forEach(lane => {
+ const dotColor = LANE_COLORS[lane.color] || 'bg-slate-500';
+ const dropBadge = document.createElement('div');
+ dropBadge.className = 'timeline-lane-target px-2.5 py-1 rounded-lg border border-slate-200/80 dark:border-slate-800 bg-white/80 dark:bg-slate-800/70 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 text-slate-700 dark:text-slate-200 font-semibold text-[11px] flex items-center space-x-1.5 cursor-pointer transition flex-shrink-0 select-none shadow-2xs';
+ dropBadge.setAttribute('data-lane-id', lane.id);
+ dropBadge.title = `Sleep een taak hierheen om te verplaatsen naar: ${lane.title}`;
+ dropBadge.innerHTML = `
+
+
${escapeHtml(lane.title)}
+ `;
+
+ dropBadge.ondragover = (e) => {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'move';
+ dropBadge.classList.add('ring-2', 'ring-indigo-500', 'bg-indigo-100', 'dark:bg-indigo-900/60');
+ };
+ dropBadge.ondragleave = () => {
+ dropBadge.classList.remove('ring-2', 'ring-indigo-500', 'bg-indigo-100', 'dark:bg-indigo-900/60');
+ };
+ dropBadge.ondrop = async (e) => {
+ e.preventDefault();
+ dropBadge.classList.remove('ring-2', 'ring-indigo-500', 'bg-indigo-100', 'dark:bg-indigo-900/60');
+ const cardIdStr = e.dataTransfer.getData('text/plain');
+ const cardId = parseInt(cardIdStr, 10);
+ if (cardId && lane.id) {
+ await moveTimelineCardToLane(cardId, lane.id);
+ }
+ };
+
+ bar.appendChild(dropBadge);
+ });
}
function populateModalDropdowns() {
@@ -668,10 +720,26 @@ function renderTimeline() {
const laneDotColor = LANE_COLORS[card.lane_color] || 'bg-slate-500';
const hasSchedule = (card.start_date && card.end_date);
- // 1. Linkerpaneel Item
+ // 1. Linkerpaneel Item (draggable naar lane drop zones)
const leftItem = document.createElement('div');
- leftItem.className = 'timeline-row h-12 px-3.5 flex items-center justify-between transition cursor-pointer hover:bg-slate-100/70 dark:hover:bg-slate-800/50';
- leftItem.onclick = () => openCardModal(card.id);
+ leftItem.className = 'timeline-row h-12 px-3.5 flex items-center justify-between transition cursor-grab active:cursor-grabbing hover:bg-slate-100/70 dark:hover:bg-slate-800/50 group select-none';
+ leftItem.setAttribute('draggable', 'true');
+ leftItem.setAttribute('data-card-id', card.id);
+
+ leftItem.ondragstart = (e) => {
+ e.dataTransfer.setData('text/plain', String(card.id));
+ e.dataTransfer.effectAllowed = 'move';
+ leftItem.classList.add('opacity-50', 'bg-indigo-50/50', 'dark:bg-indigo-950/30');
+ };
+ leftItem.ondragend = () => {
+ leftItem.classList.remove('opacity-50', 'bg-indigo-50/50', 'dark:bg-indigo-950/30');
+ };
+
+ leftItem.onclick = (e) => {
+ // Voorkom trigger als er net gedragged werd
+ if (e.defaultPrevented) return;
+ openCardModal(card.id);
+ };
// Date badge
let dateBadge = '
Niet gepland';
@@ -858,6 +926,29 @@ async function updateCardDates(cardId, startDate, endDate) {
}
}
+// API call om een taak direct naar een andere lane / statuscategorie te verplaatsen
+async function moveTimelineCardToLane(cardId, targetLaneId) {
+ try {
+ const res = await fetch('/api/board.php?action=move_card', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({
+ card_id: cardId,
+ target_lane_id: targetLaneId,
+ target_position: 999
+ })
+ });
+ const data = await res.json();
+ if (data.success) {
+ await loadBoardData(currentBoardId);
+ } else {
+ alert(data.error || 'Kon taak niet verplaatsen');
+ }
+ } catch (e) {
+ console.error('Fout bij verplaatsen naar lane:', e);
+ }
+}
+
// Verslepen van tijdlijnbalken
function enableBarDragging(barEl, cardId, originalStart, originalEnd) {
let isDragging = false;