Files
itflow/js/client_switcher.js

138 lines
4.5 KiB
JavaScript

/*
* ITFlow - client switcher.
*
* The client card at the top of the client side nav opens a dropdown listing
* the agent's clients, most recently opened first, with a box to filter them.
* The list comes from ajax.php?get_active_clients the first time it opens and
* is filtered here as you type. Picking a client goes to the same page for
* that client - the side nav works out which page that is (data-switch-url).
*
* Keys: type to filter, Down to move into the list, Enter to open the first
* match, Esc to close.
*/
(function () {
'use strict';
const root = document.querySelector('[data-client-switcher]');
if (!root) {
return;
}
const input = root.querySelector('[data-client-switcher-search]');
const list = root.querySelector('[data-client-switcher-list]');
const currentId = parseInt(root.dataset.clientId, 10);
const switchUrl = root.dataset.switchUrl;
const tones = ['primary', 'success', 'info', 'warning', 'secondary'];
const limit = 50;
let clients = null;
let loading = null;
function message(text) {
list.textContent = '';
const div = document.createElement('div');
div.className = 'itflow-client-switcher-empty';
div.textContent = text;
list.appendChild(div);
}
function item(c) {
const id = parseInt(c.client_id, 10);
const link = document.createElement('a');
link.className = 'dropdown-item itflow-client-switcher-item';
link.href = switchUrl + id;
if (id === currentId) {
link.classList.add('active');
link.setAttribute('aria-current', 'true');
}
const tile = document.createElement('span');
const tone = tones.indexOf(c.client_tone) !== -1 ? c.client_tone : 'secondary';
tile.className = 'itflow-client-avatar itflow-client-avatar-xs text-bg-' + tone;
tile.setAttribute('aria-hidden', 'true');
tile.textContent = c.client_initials;
const name = document.createElement('span');
name.className = 'text-truncate';
name.textContent = c.client_name;
link.append(tile, name);
return link;
}
function render() {
if (clients === null) {
message('Loading clients...');
return;
}
const typed = input.value.trim();
const term = typed.toLowerCase();
const matches = clients.filter(function (c) {
return !term || c.client_name.toLowerCase().indexOf(term) !== -1 || c.client_initials.toLowerCase() === term;
});
if (!matches.length) {
message(term ? 'No clients match "' + typed + '"' : 'No clients');
return;
}
list.textContent = '';
matches.slice(0, limit).forEach(function (c) {
list.appendChild(item(c));
});
if (matches.length > limit) {
const more = document.createElement('div');
more.className = 'itflow-client-switcher-empty';
more.textContent = 'Keep typing to narrow down ' + (matches.length - limit) + ' more';
list.appendChild(more);
}
}
function load() {
if (clients !== null || loading) {
return;
}
loading = fetch('/agent/ajax.php?get_active_clients', { credentials: 'same-origin', headers: { Accept: 'application/json' } })
.then(function (response) {
if (!response.ok) {
throw new Error(response.status);
}
return response.json();
})
.then(function (data) {
clients = (data && Array.isArray(data.clients)) ? data.clients : [];
render();
})
.catch(function () {
loading = null;
message('Could not load clients - close and try again');
});
}
root.addEventListener('show.bs.dropdown', function () {
input.value = '';
render();
load();
});
root.addEventListener('shown.bs.dropdown', function () {
input.focus();
});
input.addEventListener('input', render);
input.addEventListener('keydown', function (event) {
const first = list.querySelector('.itflow-client-switcher-item');
if (event.key === 'Enter') {
event.preventDefault();
if (first) {
window.location.href = first.href;
}
} else if (event.key === 'ArrowDown' && first) {
event.preventDefault();
first.focus();
}
});
})();