Fix duplicate users in bulk/quick assign modals; add combobox search
Root cause: DashboardView.php and dashboard.js both had a global
document.addEventListener('click') handler handling the same bulk-assign
and quick-assign actions. Every click fired both handlers, creating two
modals and two API fetches that both appended to the same select element.
Fix: Remove duplicate cases (bulk-*, navigate, view-ticket, quick-*,
set-view-mode, toggle-*, clear-selection) from DashboardView.php's inline
handler. dashboard.js already handles all of these correctly.
Also replace <select> with lt.combobox in both bulk-assign and
quick-assign modals so large user lists are searchable instead of a
long scrolling dropdown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+54
-31
@@ -545,6 +545,8 @@ function performBulkCloseAction(ticketIds) {
|
||||
});
|
||||
}
|
||||
|
||||
var _bulkAssignUserId = null; // set by combobox onSelect
|
||||
|
||||
function showBulkAssignModal() {
|
||||
const ticketIds = getSelectedTicketIds();
|
||||
|
||||
@@ -553,7 +555,8 @@ function showBulkAssignModal() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create modal HTML
|
||||
_bulkAssignUserId = null;
|
||||
|
||||
const modalHtml = `
|
||||
<div class="lt-modal-overlay" id="bulkAssignModal" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="bulkAssignModalTitle">
|
||||
<div class="lt-modal">
|
||||
@@ -562,10 +565,15 @@ function showBulkAssignModal() {
|
||||
<button class="lt-modal-close" data-modal-close aria-label="Close">✕</button>
|
||||
</div>
|
||||
<div class="lt-modal-body">
|
||||
<label for="bulkAssignUser">Assign to:</label>
|
||||
<select id="bulkAssignUser" class="lt-select">
|
||||
<option value="">Select User...</option>
|
||||
</select>
|
||||
<label class="lt-label">Assign to:</label>
|
||||
<div class="lt-combobox" id="bulkAssignCombobox">
|
||||
<div class="lt-combobox-input-wrap">
|
||||
<input type="text" class="lt-combobox-input" id="bulkAssignUserInput"
|
||||
placeholder="Search users…" autocomplete="off" aria-label="Search users">
|
||||
</div>
|
||||
<ul class="lt-combobox-list" role="listbox" aria-hidden="true"></ul>
|
||||
</div>
|
||||
<span class="lt-field-hint lt-text-muted">Type to search — supports large user lists</span>
|
||||
</div>
|
||||
<div class="lt-modal-footer">
|
||||
<button data-action="perform-bulk-assign" class="lt-btn lt-btn-primary">ASSIGN</button>
|
||||
@@ -578,19 +586,18 @@ function showBulkAssignModal() {
|
||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||
lt.modal.open('bulkAssignModal');
|
||||
|
||||
// Fetch users for the dropdown
|
||||
lt.api.get('/api/get_users.php')
|
||||
.then(data => {
|
||||
if (data.success && data.users) {
|
||||
const select = document.getElementById('bulkAssignUser');
|
||||
if (select) {
|
||||
data.users.forEach(user => {
|
||||
const option = document.createElement('option');
|
||||
option.value = user.user_id;
|
||||
option.textContent = user.display_name || user.username;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
const input = document.getElementById('bulkAssignUserInput');
|
||||
if (!input) return;
|
||||
const items = data.users.map(u => ({
|
||||
value: String(u.user_id),
|
||||
label: u.display_name || u.username
|
||||
}));
|
||||
lt.combobox.init(input, items, {
|
||||
onSelect: function(item) { _bulkAssignUserId = item.value; }
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => lt.toast.error('Error loading users'));
|
||||
@@ -603,11 +610,11 @@ function closeBulkAssignModal() {
|
||||
}
|
||||
|
||||
function performBulkAssign() {
|
||||
const userId = document.getElementById('bulkAssignUser').value;
|
||||
const userId = _bulkAssignUserId;
|
||||
const ticketIds = getSelectedTicketIds();
|
||||
|
||||
if (!userId) {
|
||||
lt.toast.warning('Please select a user', 2000);
|
||||
lt.toast.warning('Please select a user from the list', 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -997,10 +1004,14 @@ function performQuickStatusChange(ticketId) {
|
||||
});
|
||||
}
|
||||
|
||||
var _quickAssignUserId = undefined; // undefined = no change; null = unassign; string = user_id
|
||||
|
||||
/**
|
||||
* Quick assign from dashboard
|
||||
*/
|
||||
function quickAssign(ticketId) {
|
||||
_quickAssignUserId = undefined;
|
||||
|
||||
const modalHtml = `
|
||||
<div class="lt-modal-overlay" id="quickAssignModal" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="quickAssignModalTitle">
|
||||
<div class="lt-modal lt-modal-xs">
|
||||
@@ -1009,14 +1020,18 @@ function quickAssign(ticketId) {
|
||||
<button class="lt-modal-close" data-modal-close aria-label="Close">✕</button>
|
||||
</div>
|
||||
<div class="lt-modal-body">
|
||||
<p class="lt-mb-xs">Ticket #${lt.escHtml(ticketId)}</p>
|
||||
<label for="quickAssignSelect">Assign to:</label>
|
||||
<select id="quickAssignSelect" class="lt-select">
|
||||
<option value="">Unassigned</option>
|
||||
</select>
|
||||
<p class="lt-mb-xs lt-text-muted lt-text-xs">Ticket #${lt.escHtml(String(ticketId))}</p>
|
||||
<label class="lt-label">Assign to:</label>
|
||||
<div class="lt-combobox" id="quickAssignCombobox">
|
||||
<div class="lt-combobox-input-wrap">
|
||||
<input type="text" class="lt-combobox-input" id="quickAssignInput"
|
||||
placeholder="Search users…" autocomplete="off" aria-label="Search users">
|
||||
</div>
|
||||
<ul class="lt-combobox-list" role="listbox" aria-hidden="true"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lt-modal-footer">
|
||||
<button data-action="perform-quick-assign" data-ticket-id="${ticketId}" class="lt-btn lt-btn-primary">ASSIGN</button>
|
||||
<button data-action="perform-quick-assign" data-ticket-id="${lt.escHtml(String(ticketId))}" class="lt-btn lt-btn-primary">ASSIGN</button>
|
||||
<button data-action="close-quick-assign-modal" class="lt-btn lt-btn-ghost">CANCEL</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1026,16 +1041,20 @@ function quickAssign(ticketId) {
|
||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||
lt.modal.open('quickAssignModal');
|
||||
|
||||
// Load users
|
||||
lt.api.get('/api/get_users.php')
|
||||
.then(data => {
|
||||
if (data.success && data.users) {
|
||||
const select = document.getElementById('quickAssignSelect');
|
||||
data.users.forEach(user => {
|
||||
const option = document.createElement('option');
|
||||
option.value = user.user_id;
|
||||
option.textContent = user.display_name || user.username;
|
||||
select.appendChild(option);
|
||||
const input = document.getElementById('quickAssignInput');
|
||||
if (!input) return;
|
||||
const items = [
|
||||
{ value: '', label: 'Unassigned' },
|
||||
...data.users.map(u => ({
|
||||
value: String(u.user_id),
|
||||
label: u.display_name || u.username
|
||||
}))
|
||||
];
|
||||
lt.combobox.init(input, items, {
|
||||
onSelect: function(item) { _quickAssignUserId = item.value || null; }
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -1049,7 +1068,11 @@ function closeQuickAssignModal() {
|
||||
}
|
||||
|
||||
function performQuickAssign(ticketId) {
|
||||
const assignedTo = document.getElementById('quickAssignSelect').value || null;
|
||||
if (_quickAssignUserId === undefined) {
|
||||
lt.toast.warning('Please select a user from the list', 2000);
|
||||
return;
|
||||
}
|
||||
const assignedTo = _quickAssignUserId;
|
||||
|
||||
lt.api.post('/api/assign_ticket.php', { ticket_id: ticketId, assigned_to: assignedTo })
|
||||
.then(data => {
|
||||
|
||||
Reference in New Issue
Block a user