mirror of https://github.com/itflow-org/itflow
Change the button handlebar class
This commit is contained in:
parent
65e107d154
commit
846947ff49
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -105,7 +105,7 @@ $kanban = array_values($statuses);
|
|||
<span class='badge badge-secondary'>
|
||||
<?php echo $item['category_name']; ?>
|
||||
</span>
|
||||
<div class='btn btn-secondary drag-handle-class' style="display: none;">
|
||||
<div class='btn btn-light drag-handle-class' style="display: none;">
|
||||
<i class="drag-handle-class fas fa-bars"></i>
|
||||
</div>
|
||||
<br>
|
||||
|
|
|
|||
Loading…
Reference in New Issue