feat(js): add app-http contracts and migrate helpdesk runtime layer
This commit is contained in:
@@ -3,9 +3,34 @@
|
||||
* Current tab: per-agent widget with section title + ticket table.
|
||||
* Performance tab: compact agent rows with resolved counts.
|
||||
*/
|
||||
import { getJson } from '/js/core/app-http.js';
|
||||
import { resolveHost } from '/js/core/app-dom.js';
|
||||
|
||||
const EMPTY_API = Object.freeze({ destroy: () => {} });
|
||||
|
||||
const resolveContainer = (root) => {
|
||||
const host = resolveHost(root);
|
||||
if (host instanceof HTMLElement && host.matches('.app-details-container[data-app-component="helpdesk-team"]')) {
|
||||
return host;
|
||||
}
|
||||
return host.querySelector('.app-details-container[data-app-component="helpdesk-team"]');
|
||||
};
|
||||
|
||||
export function initHelpdeskTeam(root = document) {
|
||||
const container = resolveContainer(root);
|
||||
if (!container) {
|
||||
return EMPTY_API;
|
||||
}
|
||||
|
||||
const listenerController = new AbortController();
|
||||
const requestController = new AbortController();
|
||||
const on = (target, type, handler, options = {}) => {
|
||||
if (!target || typeof target.addEventListener !== 'function') {
|
||||
return;
|
||||
}
|
||||
target.addEventListener(type, handler, { ...options, signal: listenerController.signal });
|
||||
};
|
||||
|
||||
const container = document.querySelector('.app-details-container[data-team-workload-url]');
|
||||
if (container) {
|
||||
const dataUrl = container.dataset.teamWorkloadUrl;
|
||||
const labelQueue = container.dataset.labelQueue || 'Queue';
|
||||
const labelTickets = container.dataset.labelTickets || 'Tickets';
|
||||
@@ -202,7 +227,7 @@ if (container) {
|
||||
for (const item of items) {
|
||||
const li = h('li', 'helpdesk-team-perf-ranked-clickable');
|
||||
|
||||
li.addEventListener('click', () => {
|
||||
on(li, 'click', () => {
|
||||
if (activeItem === li) {
|
||||
activeItem.classList.remove('active');
|
||||
activeItem = null;
|
||||
@@ -363,8 +388,9 @@ if (container) {
|
||||
if (refresh) params.set('refresh', '1');
|
||||
|
||||
try {
|
||||
const res = await fetch(dataUrl + '?' + params.toString(), { credentials: 'same-origin' });
|
||||
const json = await res.json();
|
||||
const json = await getJson(`${dataUrl}?${params.toString()}`, {
|
||||
signal: requestController.signal,
|
||||
});
|
||||
|
||||
if (!json.ok) { showState('error'); return; }
|
||||
if (!json.members || json.members.length === 0) { showState('empty'); return; }
|
||||
@@ -372,25 +398,31 @@ if (container) {
|
||||
renderCurrentTab(json.members);
|
||||
renderPerformanceTab(json.members);
|
||||
showState('success');
|
||||
} catch {
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
return;
|
||||
}
|
||||
showState('error');
|
||||
}
|
||||
}
|
||||
|
||||
if (periodSelector) {
|
||||
periodSelector.addEventListener('change', (e) => {
|
||||
const radio = e.target.closest('input[name="team-period"]');
|
||||
if (!radio) return;
|
||||
const period = parseInt(radio.value, 10);
|
||||
if (!period || period === currentPeriod) return;
|
||||
currentPeriod = period;
|
||||
fetchData();
|
||||
});
|
||||
}
|
||||
on(periodSelector, 'change', (e) => {
|
||||
const radio = e.target.closest('input[name="team-period"]');
|
||||
if (!radio) return;
|
||||
const period = parseInt(radio.value, 10);
|
||||
if (!period || period === currentPeriod) return;
|
||||
currentPeriod = period;
|
||||
void fetchData();
|
||||
});
|
||||
|
||||
if (elRefresh) {
|
||||
elRefresh.addEventListener('click', () => fetchData(true));
|
||||
}
|
||||
on(elRefresh, 'click', () => void fetchData(true));
|
||||
|
||||
fetchData();
|
||||
void fetchData();
|
||||
|
||||
return {
|
||||
destroy: () => {
|
||||
listenerController.abort();
|
||||
requestController.abort();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user