diff --git a/css/tickets_kanban.css b/css/tickets_kanban.css index 140c0f9e..a03d6773 100644 --- a/css/tickets_kanban.css +++ b/css/tickets_kanban.css @@ -1,83 +1,41 @@ -/* General Popover Styling */ .popover { max-width: 600px; } - -/* Kanban Board Container */ #kanban-board { display: flex; - overflow-x: auto; box-sizing: border-box; + overflow-x: auto; min-width: 400px; height: calc(100vh - 210px); } -/* Kanban Column */ .kanban-column { - flex: 1; + flex: 1; /* Allows columns to grow equally */ + margin: 0 10px; /* Space between columns */ min-width: 300px; max-width: 300px; - margin: 0 10px; background: #f4f4f4; - border: 1px solid #ccc; - border-radius: 4px; padding: 10px; + border: 1px solid #ccc; min-height: calc(100vh - 230px); max-height: calc(100vh - 230px); box-sizing: border-box; - display: flex; - flex-direction: column; } -/* Column Inner Scrollable Task Area */ -.kanban-status { - flex: 1; - overflow-y: auto; - min-height: 60px; - position: relative; - padding: 5px; - background-color: #f9f9f9; - border-radius: 4px; +.kanban-column div { + max-height: calc(100vh - 280px); /* Set your desired max height */ + overflow-y: auto; /* Adds a scrollbar when content exceeds max height */ } -/* Individual Task Cards */ .task { background: #fff; margin: 5px 0; padding: 10px; border: 1px solid #ddd; - border-radius: 4px; - cursor: grab; - user-select: none; + user-select: none; /* Prevent text selection */ } -/* Grabbing Cursor State */ -.task:active { - cursor: grabbing; -} - -/* Drag Handle (shown on mobile or with class targeting) */ .drag-handle-class { - float: right; touch-action: none; - cursor: grab; -} - -/* Placeholder shown in empty columns */ -.empty-placeholder { - border: 2px dashed #ccc; - background-color: #fcfcfc; - color: #999; - font-style: italic; - padding: 12px; - margin: 10px 0; - text-align: center; - border-radius: 4px; - pointer-events: none; -} - -/* Sortable drop zone feedback (optional visual cue) */ -.kanban-status.sortable-over { - background-color: #eaf6ff; - transition: background-color 0.2s ease; + float: right; } diff --git a/js/tickets_kanban.js b/js/tickets_kanban.js index c614d057..419a0ee3 100644 --- a/js/tickets_kanban.js +++ b/js/tickets_kanban.js @@ -1,126 +1,146 @@ -$(document).ready(function () { - console.log('CONFIG_TICKET_MOVING_COLUMNS:', CONFIG_TICKET_MOVING_COLUMNS); - console.log('CONFIG_TICKET_ORDERING:', CONFIG_TICKET_ORDERING); +$(document).ready(function() { + console.log('CONFIG_TICKET_MOVING_COLUMNS: ' + CONFIG_TICKET_MOVING_COLUMNS); + console.log('CONFIG_TICKET_ORDERING: ' + CONFIG_TICKET_ORDERING); - // ------------------------------- - // Drag: Kanban Columns (Statuses) - // ------------------------------- - new Sortable(document.querySelector('#kanban-board'), { - animation: 150, - handle: '.panel-title', - draggable: '.kanban-column', - onEnd: function () { - const columnPositions = Array.from(document.querySelectorAll('#kanban-board .kanban-column')).map((col, index) => ({ - status_id: $(col).data('status-id'), + // Function to detect touch devices + function isTouchDevice() { + return 'ontouchstart' in window || navigator.maxTouchPoints; + } + + // Initialize Dragula for the Kanban board + let boardDrake = dragula([ + document.querySelector('#kanban-board') + ], { + moves: function(el, container, handle) { + return handle.classList.contains('panel-title'); + }, + accepts: function(el, target, source, sibling) { + return CONFIG_TICKET_MOVING_COLUMNS === 1; + } + }); + + // Log the event of moving the column panel-title + boardDrake.on('drag', function(el) { + //console.log('Dragging column:', el.querySelector('.panel-title').innerText); + }); + + boardDrake.on('drop', function(el, target, source, sibling) { + //console.log('Dropped column:', el.querySelector('.panel-title').innerText); + + // Get all columns and their positions + let columns = document.querySelectorAll('#kanban-board .kanban-column'); + let columnPositions = []; + + columns.forEach(function(column, index) { + let statusId = $(column).data('status-id'); // Assuming you have a data attribute for status ID + columnPositions.push({ + status_id: statusId, status_kanban: index - })); + }); + }); - if (CONFIG_TICKET_MOVING_COLUMNS === 1) { - $.post('ajax.php', { - update_kanban_status_position: true, - positions: columnPositions - }).done(() => { - console.log('Ticket status kanban orders updated.'); - }).fail((xhr) => { - console.error('Error updating status order:', xhr.responseText); - }); + // Send AJAX request to update all column positions + $.ajax({ + url: 'ajax.php', + type: 'POST', + data: { + update_kanban_status_position: true, + positions: columnPositions + }, + success: function(response) { + console.log('Ticket status kanban orders updated successfully.'); + // Optionally, you can refresh the page or update the UI here + }, + error: function(xhr, status, error) { + console.error('Error updating ticket status kanban orders:', error); + } + }); + }); + + // Initialize Dragula for the Kanban Cards + let drake = dragula([ + ...document.querySelectorAll('#status') + ], { + moves: function(el, container, handle) { + if (isTouchDevice()) { + return handle.classList.contains('drag-handle-class'); + } else { + return true; // Allow dragging on the entire task element for desktop } } }); - // ------------------------------- - // Drag: Tasks within Columns - // ------------------------------- - document.querySelectorAll('.kanban-status').forEach(statusCol => { - new Sortable(statusCol, { - group: 'tickets', - animation: 150, - handle: isTouchDevice() ? '.drag-handle-class' : undefined, - onStart: () => hidePlaceholders(), - onEnd: function (evt) { - const target = evt.to; - const movedEl = evt.item; + if (isTouchDevice()) { + const moveList = document.querySelectorAll('.task'); + moveList.forEach(task => { + task.querySelector('.drag-handle-class').style.display = 'inline'; + }); + } - // Disallow reordering in same column if config says so - if (CONFIG_TICKET_ORDERING === 0 && evt.from === evt.to) { - evt.from.insertBefore(movedEl, evt.from.children[evt.oldIndex]); - showPlaceholders(); - return; - } + drake.on('drag', function(el) { + el.style.cursor = 'grabbing'; + }); + + drake.on('dragend', function(el) { + el.style.cursor = 'grab'; + }); + // Add event listener for the drop event + drake.on('drop', function (el, target, source, sibling) { + // Log the target ID to the console + //console.log('Dropped into:', target.getAttribute('data-column-name')); - const columnId = $(target).data('status-id'); + if (CONFIG_TICKET_ORDERING === 0 && source == target) { + drake.cancel(true); // Move the card back to its original position + return; + } + + // Get all cards in the target column and their positions + let cards = $(target).children('.task'); + let positions = []; - const positions = Array.from(target.querySelectorAll('.task')).map((card, index) => { - const ticketId = $(card).data('ticket-id'); - const oldStatus = ticketId === $(movedEl).data('ticket-id') - ? $(movedEl).data('ticket-status-id') - : false; + //id of current status / column + let columnId = $(target).data('status-id'); - $(card).data('ticket-status-id', columnId); // update DOM + let movedTicketId = $(el).data('ticket-id'); + let movedTicketStatusId = $(el).data('ticket-status-id'); - return { - ticket_id: ticketId, - ticket_order: index, - ticket_oldStatus: oldStatus, - ticket_status: columnId - }; - }); + cards.each(function(index, card) { + let ticketId = $(card).data('ticket-id'); + let statusId = $(card).data('ticket-status-id'); + + let oldStatus = false; + if (ticketId == movedTicketId) { + oldStatus = movedTicketStatusId; + } - $.post('ajax.php', { - update_kanban_ticket: true, - positions: positions - }).done(() => { - console.log('Updated kanban ticket positions.'); - }).fail((xhr) => { - console.error('Error updating ticket positions:', xhr.responseText); - }); + //update the status id of the card if needed + if (statusId != columnId) { + $(card).data('ticket-status-id', columnId); + statusId = columnId; + } + positions.push({ + ticket_id: ticketId, + ticket_order: index, + ticket_oldStatus: oldStatus, + ticket_status: statusId ?? null// Get the new status ID from the target column + }); + }); - // Refresh placeholders after update - showPlaceholders(); + //console.log(positions); + // Send AJAX request to update all ticket kanban orders and statuses + $.ajax({ + url: 'ajax.php', + type: 'POST', + data: { + update_kanban_ticket: true, + positions: positions + }, + success: function(response) { + //console.log('Ticket kanban orders and statuses updated successfully.'); + }, + error: function(xhr, status, error) { + console.error('Error updating ticket kanban orders and statuses:', error); } }); }); - - // ------------------------------- - // 📱 Touch Support: Show drag handle on mobile - // ------------------------------- - if (isTouchDevice()) { - $('.drag-handle-class').css('display', 'inline'); - } - - // ------------------------------- - // Placeholder Management - // ------------------------------- - function showPlaceholders() { - document.querySelectorAll('.kanban-status').forEach(status => { - const placeholderClass = 'empty-placeholder'; - - // Remove existing placeholder - const existing = status.querySelector(`.${placeholderClass}`); - if (existing) existing.remove(); - - // Only show if there are no tasks - if (status.querySelectorAll('.task').length === 0) { - const placeholder = document.createElement('div'); - placeholder.className = `${placeholderClass} text-muted text-center p-2`; - placeholder.innerText = 'Drop ticket here'; - placeholder.style.pointerEvents = 'none'; - status.appendChild(placeholder); - } - }); - } - - function hidePlaceholders() { - document.querySelectorAll('.empty-placeholder').forEach(el => el.remove()); - } - - // Run once on load - showPlaceholders(); - - // ------------------------------- - // Utility: Detect touch device - // ------------------------------- - function isTouchDevice() { - return 'ontouchstart' in window || navigator.maxTouchPoints > 0; - } -}); +}); \ No newline at end of file diff --git a/tickets_kanban.php b/tickets_kanban.php index bb4e3ab0..e8b4dd3d 100644 --- a/tickets_kanban.php +++ b/tickets_kanban.php @@ -105,7 +105,7 @@ $kanban = array_values($statuses); -