Fix bulk assign user search: replace broken combobox with typeahead
The combobox modal used lt-combobox-list but lt.combobox looks for lt-combobox-dropdown — it returned immediately, wiring nothing. Replaced with lt.typeahead which is correct for single-select search: - Filters users client-side as you type (minChars:1, debounced 150ms) - Shows display_name (username) with highlight on match - onSelect stores user ID and shows "✓ Name" confirmation below input - Input auto-focuses when modal opens - Enter key now selects first result even without arrow-key navigation (same fix applied to lt.combobox Enter handler) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -2065,7 +2065,7 @@
|
|||||||
if (!dropdown.classList.contains('is-open')) return;
|
if (!dropdown.classList.contains('is-open')) return;
|
||||||
if (e.key === 'ArrowDown') { e.preventDefault(); _moveFocus(1); }
|
if (e.key === 'ArrowDown') { e.preventDefault(); _moveFocus(1); }
|
||||||
if (e.key === 'ArrowUp') { e.preventDefault(); _moveFocus(-1); }
|
if (e.key === 'ArrowUp') { e.preventDefault(); _moveFocus(-1); }
|
||||||
if (e.key === 'Enter') { e.preventDefault(); if (_focusedIdx >= 0 && _items[_focusedIdx]) _select(_items[_focusedIdx]); }
|
if (e.key === 'Enter') { e.preventDefault(); const idx = _focusedIdx >= 0 ? _focusedIdx : 0; if (_items[idx]) _select(_items[idx]); }
|
||||||
if (e.key === 'Escape') { dropdown.classList.remove('is-open'); }
|
if (e.key === 'Escape') { dropdown.classList.remove('is-open'); }
|
||||||
if (e.key === 'Tab') { dropdown.classList.remove('is-open'); }
|
if (e.key === 'Tab') { dropdown.classList.remove('is-open'); }
|
||||||
});
|
});
|
||||||
|
|||||||
+21
-17
@@ -552,7 +552,7 @@ function performBulkCloseAction(ticketIds) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var _bulkAssignUserId = null; // set by combobox onSelect
|
var _bulkAssignUserId = null;
|
||||||
|
|
||||||
function showBulkAssignModal() {
|
function showBulkAssignModal() {
|
||||||
const ticketIds = getSelectedTicketIds();
|
const ticketIds = getSelectedTicketIds();
|
||||||
@@ -566,21 +566,20 @@ function showBulkAssignModal() {
|
|||||||
|
|
||||||
const modalHtml = `
|
const modalHtml = `
|
||||||
<div class="lt-modal-overlay" id="bulkAssignModal" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="bulkAssignModalTitle">
|
<div class="lt-modal-overlay" id="bulkAssignModal" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="bulkAssignModalTitle">
|
||||||
<div class="lt-modal">
|
<div class="lt-modal lt-modal-sm">
|
||||||
<div class="lt-modal-header">
|
<div class="lt-modal-header">
|
||||||
<span class="lt-modal-title" id="bulkAssignModalTitle">Assign ${ticketIds.length} Ticket(s)</span>
|
<span class="lt-modal-title" id="bulkAssignModalTitle">[ @ ] Assign ${ticketIds.length} Ticket(s)</span>
|
||||||
<button class="lt-modal-close" data-modal-close aria-label="Close">✕</button>
|
<button class="lt-modal-close" data-modal-close aria-label="Close">✕</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="lt-modal-body">
|
<div class="lt-modal-body">
|
||||||
<label class="lt-label">Assign to:</label>
|
<label class="lt-label" for="bulkAssignUserInput">Assign to</label>
|
||||||
<div class="lt-combobox" id="bulkAssignCombobox">
|
<div class="lt-typeahead" id="bulkAssignTypeahead" style="position:relative">
|
||||||
<div class="lt-combobox-input-wrap">
|
<input type="text" class="lt-input lt-w-full" id="bulkAssignUserInput"
|
||||||
<input type="text" class="lt-combobox-input" id="bulkAssignUserInput"
|
placeholder="Type a name…" autocomplete="off" spellcheck="false"
|
||||||
placeholder="Search users…" autocomplete="off" aria-label="Search users">
|
aria-label="Search users" aria-autocomplete="list">
|
||||||
|
<div class="lt-typeahead-dropdown" id="bulkAssignDropdown"></div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="lt-combobox-list" role="listbox" aria-hidden="true"></ul>
|
<div id="bulkAssignSelected" style="margin-top:0.4rem;font-size:0.75rem;color:var(--terminal-cyan);min-height:1.2em"></div>
|
||||||
</div>
|
|
||||||
<span class="lt-field-hint lt-text-muted">Type to search — supports large user lists</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="lt-modal-footer">
|
<div class="lt-modal-footer">
|
||||||
<button data-action="perform-bulk-assign" class="lt-btn lt-btn-primary">ASSIGN</button>
|
<button data-action="perform-bulk-assign" class="lt-btn lt-btn-primary">ASSIGN</button>
|
||||||
@@ -592,21 +591,26 @@ function showBulkAssignModal() {
|
|||||||
|
|
||||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||||
lt.modal.open('bulkAssignModal');
|
lt.modal.open('bulkAssignModal');
|
||||||
|
setTimeout(() => { const inp = document.getElementById('bulkAssignUserInput'); if (inp) inp.focus(); }, 120);
|
||||||
|
|
||||||
lt.api.get('/api/get_users.php')
|
lt.api.get('/api/get_users.php')
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success && data.users) {
|
if (!data.success || !data.users) return;
|
||||||
const input = document.getElementById('bulkAssignUserInput');
|
const input = document.getElementById('bulkAssignUserInput');
|
||||||
if (!input) return;
|
if (!input) return;
|
||||||
const items = data.users.map(u => ({
|
const items = data.users.map(u => ({
|
||||||
value: String(u.user_id),
|
value: String(u.user_id),
|
||||||
label: u.display_name || u.username
|
label: u.display_name ? u.display_name + ' (' + u.username + ')' : u.username
|
||||||
}));
|
}));
|
||||||
lt.combobox.init(input, items, {
|
lt.typeahead.init(input, items, {
|
||||||
max: 1,
|
minChars: 1,
|
||||||
onChange: function(selected) { _bulkAssignUserId = selected[0] || null; }
|
maxResults: 8,
|
||||||
});
|
onSelect: function(item) {
|
||||||
|
_bulkAssignUserId = item.value;
|
||||||
|
const sel = document.getElementById('bulkAssignSelected');
|
||||||
|
if (sel) sel.textContent = '✓ ' + item.label;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(() => lt.toast.error('Error loading users'));
|
.catch(() => lt.toast.error('Error loading users'));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user