Files
breadcrumb-the-shire/modules/helpdesk/web/js/helpdesk-ticket.js

128 lines
3.8 KiB
JavaScript

/**
* Helpdesk ticket detail page — async communication aside.
*/
import { getJson } from '/js/core/app-http.js';
import { resolveHost } from '/js/core/app-dom.js';
import {
renderCommunicationError,
renderCommunicationFeed,
renderCommunicationHint,
} from './helpdesk-communication.js';
const EMPTY_API = Object.freeze({ destroy: () => {} });
const resolveContainer = (root) => {
const host = resolveHost(root);
if (host instanceof HTMLElement && host.matches('.app-details-container[data-ticket-no]')) {
return host;
}
return host.querySelector('.app-details-container[data-ticket-no]');
};
export function initHelpdeskTicket(root = document) {
const container = resolveContainer(root);
if (!container) {
return EMPTY_API;
}
const ticketNo = container.dataset.ticketNo || '';
const communicationUrl = container.dataset.ticketCommunicationUrl || '';
const fileDownloadUrl = container.dataset.fileDownloadUrl || '';
if (!ticketNo || !communicationUrl) {
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 i18nEl = document.getElementById('helpdesk-ticket-i18n');
const i18n = i18nEl ? JSON.parse(i18nEl.textContent || '{}') : {};
const t = (key) => i18n[key] || key;
const toggleHidden = (id, hidden) => {
const el = document.getElementById(id);
if (el) {
el.hidden = hidden;
}
};
const loadCommunication = async (refresh = false) => {
const feedEl = document.getElementById('ticket-communication-feed');
const loadingEl = document.getElementById('ticket-communication-loading');
const contentEl = document.getElementById('ticket-communication-content');
if (!feedEl || !loadingEl || !contentEl) {
return;
}
contentEl.hidden = true;
loadingEl.hidden = false;
try {
const params = new URLSearchParams({ ticketNo });
if (refresh) {
params.set('refresh', '1');
}
const data = await getJson(`${communicationUrl}?${params.toString()}`, {
signal: requestController.signal,
});
toggleHidden('ticket-communication-loading', true);
toggleHidden('ticket-communication-content', false);
if (!data || data.ok !== true) {
feedEl.innerHTML = renderCommunicationError(t('Could not load communication history.'));
return;
}
let html = '';
if (
data.meta
&& data.meta.soap_used === false
&& data.meta.fallback_used === true
&& data.meta.soap_error
) {
html += renderCommunicationHint(t('Live communication unavailable, showing activity fallback.'));
}
html += renderCommunicationFeed(data.entries || [], {
t,
emptyMessage: 'No communication found.',
showTicketNo: false,
fileDownloadUrl,
});
feedEl.innerHTML = html;
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
return;
}
toggleHidden('ticket-communication-loading', true);
toggleHidden('ticket-communication-content', false);
const feedEl = document.getElementById('ticket-communication-feed');
if (feedEl) {
feedEl.innerHTML = renderCommunicationError(t('Could not load communication history.'));
}
}
};
const refreshButton = container.querySelector('button[name="ticket-refresh"]');
on(refreshButton, 'click', () => {
void loadCommunication(true);
});
void loadCommunication();
return {
destroy: () => {
listenerController.abort();
requestController.abort();
},
};
}