workign kaban board

This commit is contained in:
Mark Gregory
2026-06-17 14:03:54 +01:00
parent 47a67e83dc
commit bfea992994
8 changed files with 140 additions and 47 deletions
@@ -263,16 +263,18 @@
<div class="flex items-start justify-between gap-3">
<div>
<h2 class="text-sm font-bold uppercase tracking-wide text-zinc-900 dark:text-zinc-100">{{ $status->name }}</h2>
<p class="mt-1 text-xs text-zinc-500">{{ $tickets->count() }} tickets</p>
<p class="mt-1 text-xs text-zinc-500"><span data-helpdesk-kanban-count data-status-id="{{ $status->id }}">{{ $tickets->count() }}</span> tickets</p>
</div>
<span class="inline-flex h-4 w-4 rounded-full ring-4 ring-zinc-100 dark:ring-zinc-800" style="background-color: {{ \Modules\Helpdesk\Support\Color::css($status->color) }}"></span>
</div>
</header>
<div
id="helpdesk-kanban-column-{{ $status->id }}"
class="helpdesk-kanban-column min-h-[560px] flex-1 space-y-3 p-3 transition data-[drag-over=true]:bg-blue-50 dark:data-[drag-over=true]:bg-blue-950/30"
data-helpdesk-kanban-column
data-status-id="{{ $status->id }}"
wire:key="kanban-dropzone-{{ $board?->id ?? 0 }}-{{ $status->id }}-{{ $refreshNonce }}"
>
@forelse ($tickets as $ticket)
<article
@@ -365,6 +367,39 @@
<script>
const initialiseHelpdeskKanban = function () {
let draggedTicketId = null;
let draggedCard = null;
const updateColumnCounts = function () {
document.querySelectorAll('[data-helpdesk-kanban-count]').forEach(function (counter) {
const statusId = counter.dataset.statusId;
const column = document.querySelector('[data-helpdesk-kanban-column][data-status-id="' + statusId + '"]');
if (!column) {
return;
}
counter.textContent = column.querySelectorAll('[data-helpdesk-kanban-ticket]').length.toString();
});
};
const forceLivewireRefresh = function () {
if (typeof $wire !== 'undefined' && typeof $wire.$refresh === 'function') {
return Promise.resolve($wire.$refresh());
}
if (typeof Livewire !== 'undefined' && typeof Livewire.find === 'function') {
const root = document.querySelector('[wire\\:id]');
const component = root ? Livewire.find(root.getAttribute('wire:id')) : null;
if (component && typeof component.$refresh === 'function') {
return Promise.resolve(component.$refresh());
}
}
window.location.reload();
return Promise.resolve();
};
document.querySelectorAll('[data-helpdesk-kanban-ticket]').forEach(function (card) {
if (card.dataset.nativeDragReady === '1') return;
@@ -372,8 +407,10 @@
card.addEventListener('dragstart', function (event) {
draggedTicketId = card.dataset.ticketId;
draggedCard = card;
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', draggedTicketId);
event.dataTransfer.setData('application/x-helpdesk-ticket-id', draggedTicketId);
card.classList.add('opacity-60');
});
@@ -381,6 +418,7 @@
card.classList.remove('opacity-60');
document.querySelectorAll('[data-helpdesk-kanban-column]').forEach(function (column) {
column.dataset.dragOver = 'false';
column.dataset.refreshing = 'false';
});
});
});
@@ -405,26 +443,43 @@
event.preventDefault();
column.dataset.dragOver = 'false';
const ticketId = event.dataTransfer.getData('text/plain') || draggedTicketId;
const ticketId = event.dataTransfer.getData('application/x-helpdesk-ticket-id') || event.dataTransfer.getData('text/plain') || draggedTicketId;
const statusId = column.dataset.statusId;
const card = draggedCard || document.querySelector('[data-helpdesk-kanban-ticket][data-ticket-id="' + ticketId + '"]');
if (!ticketId || !statusId) return;
if (!ticketId || !statusId || !card) return;
Promise.resolve($wire.moveTicket(ticketId, statusId)).then(function () {
setTimeout(function () {
if (typeof $wire.$refresh === 'function') {
$wire.$refresh();
}
initialiseHelpdeskKanban();
}, 100);
});
column.dataset.refreshing = 'true';
column.appendChild(card);
updateColumnCounts();
Promise.resolve($wire.moveTicket(ticketId, statusId))
.then(forceLivewireRefresh)
.finally(function () {
column.dataset.refreshing = 'false';
draggedTicketId = null;
draggedCard = null;
updateColumnCounts();
setTimeout(initialiseHelpdeskKanban, 50);
});
});
});
updateColumnCounts();
};
initialiseHelpdeskKanban();
document.addEventListener('livewire:navigated', initialiseHelpdeskKanban);
document.addEventListener('livewire:updated', initialiseHelpdeskKanban);
document.addEventListener('helpdesk-kanban-moved', function () {
setTimeout(function () {
initialiseHelpdeskKanban();
if (typeof $wire !== 'undefined' && typeof $wire.$refresh === 'function') {
$wire.$refresh();
}
}, 50);
});
</script>
@endscript
</div>
@@ -233,10 +233,10 @@
align-items: center !important;
justify-content: center !important;
gap: 12px !important;
width: 190px !important;
min-width: 190px !important;
height: 56px !important;
min-height: 56px !important;
width: 210px !important;
min-width: 210px !important;
height: 58px !important;
min-height: 58px !important;
padding: 0 22px !important;
border: 1px solid rgba(134, 239, 172, 0.65) !important;
border-radius: 18px !important;
@@ -723,15 +723,16 @@
const fileInput = event.target.closest('[data-helpdesk-file-input]');
if (fileInput) {
const panel = fileInput.closest('details') || fileInput.closest('div');
const panel = fileInput.closest('[data-helpdesk-ticket-attachment-form], [data-helpdesk-comment-form], details, form');
const label = panel?.querySelector('[data-helpdesk-file-label]');
const count = fileInput.files?.length || 0;
const text = count === 0 ? 'No files selected' : `${count} file${count === 1 ? '' : 's'} selected`;
const names = Array.from(fileInput.files || []).map((file) => file.name);
if (label) {
label.textContent = text;
label.textContent = count === 0
? 'No files selected'
: `${count} file${count === 1 ? '' : 's'} selected${names.length ? ': ' + names.join(', ') : ''}`;
}
}
});