From cc3d0df2c2d965b502582bc1f8d76a9476b1f402 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 9 Jun 2026 10:25:37 -0500 Subject: [PATCH] feat(helpdesk): add drag-and-drop reorder to categories (#139) - Drag handle column with icon-menu grip - HTML5 native drag-and-drop on table rows - reorderCategory controller task persists ordering via AJAX --- .../src/Controller/DisplayController.php | 13 ++++++ .../admin/tmpl/categories/default.php | 40 ++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/source/packages/com_mokosuite/admin/src/Controller/DisplayController.php b/source/packages/com_mokosuite/admin/src/Controller/DisplayController.php index ddf464aa..23e401d0 100644 --- a/source/packages/com_mokosuite/admin/src/Controller/DisplayController.php +++ b/source/packages/com_mokosuite/admin/src/Controller/DisplayController.php @@ -526,6 +526,19 @@ class DisplayController extends BaseController $this->jsonResponse(['success' => true, 'message' => 'Category deleted.']); } + public function reorderCategory() + { + Session::checkToken() or die(Text::_('JINVALID_TOKEN')); + if (!$this->checkAcl('mokosuite.tickets')) { $this->jsonForbidden(); } + $order = json_decode(Factory::getApplication()->getInput()->getRaw('order', '[]'), true); + if (!is_array($order)) { $this->jsonResponse(['success' => false, 'message' => 'Invalid order']); return; } + $db = Factory::getDbo(); + foreach ($order as $i => $id) { + $db->setQuery('UPDATE ' . $db->quoteName('#__mokosuite_ticket_categories') . ' SET ordering = ' . (int) $i . ' WHERE id = ' . (int) $id)->execute(); + } + $this->jsonResponse(['success' => true, 'message' => 'Order saved.']); + } + public function saveCanned() { Session::checkToken() or die(Text::_('JINVALID_TOKEN')); diff --git a/source/packages/com_mokosuite/admin/tmpl/categories/default.php b/source/packages/com_mokosuite/admin/tmpl/categories/default.php index 25d7ec1b..cf53ffa4 100644 --- a/source/packages/com_mokosuite/admin/tmpl/categories/default.php +++ b/source/packages/com_mokosuite/admin/tmpl/categories/default.php @@ -9,6 +9,7 @@ $users = $this->users; $token = Session::getFormToken(); $saveUrl = Route::_('index.php?option=com_mokosuite&task=display.saveCategory&format=json'); $deleteUrl = Route::_('index.php?option=com_mokosuite&task=display.deleteCategory&format=json'); +$reorderUrl = Route::_('index.php?option=com_mokosuite&task=display.reorderCategory&format=json'); ?>
@@ -22,10 +23,11 @@ $deleteUrl = Route::_('index.php?option=com_mokosuite&task=display.deleteCatego
- + - + + @@ -122,5 +124,39 @@ document.addEventListener('DOMContentLoaded', function() { }); tr.querySelector('input').focus(); }); + + // Drag-and-drop reorder + var tbody = document.querySelector('#cat-table tbody'); + var dragRow = null; + + tbody.addEventListener('dragstart', function(e) { + dragRow = e.target.closest('tr'); + if (dragRow) dragRow.style.opacity = '0.5'; + }); + tbody.addEventListener('dragend', function() { + if (dragRow) dragRow.style.opacity = ''; + dragRow = null; + }); + tbody.addEventListener('dragover', function(e) { + e.preventDefault(); + var target = e.target.closest('tr'); + if (target && target !== dragRow) { + var rect = target.getBoundingClientRect(); + if ((e.clientY - rect.top) > rect.height / 2) { + target.parentNode.insertBefore(dragRow, target.nextSibling); + } else { + target.parentNode.insertBefore(dragRow, target); + } + } + }); + tbody.addEventListener('drop', function(e) { + e.preventDefault(); + var ids = []; + tbody.querySelectorAll('tr[data-id]').forEach(function(r) { if (r.dataset.id !== '0') ids.push(r.dataset.id); }); + var fd = new FormData(); + fd.append('order', JSON.stringify(ids)); + fd.append(token, '1'); + fetch('', {method:'POST', body:fd, headers:{'X-Requested-With':'XMLHttpRequest'}}); + }); });
TitleSLA ResponseSLA ResolutionAuto-AssignActive
TitleSLA ResponseSLA ResolutionAuto-AssignActive
min min