documents.js 5.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. (() => {
  2. const initialized = new WeakSet(), scopes = new Map(), requests = new WeakMap();
  3. let previewController, previewURL, returnToList = false;
  4. const modal = id => coreui.Modal.getOrCreateInstance(document.getElementById(id));
  5. async function load(host) {
  6. requests.get(host)?.abort();
  7. const controller = new AbortController(); requests.set(host, controller);
  8. const scope = host.dataset.documentDetail ? (scopes.get(host.dataset.documentKey) || 'invoice') : 'invoice';
  9. host.dataset.scope = scope; host.setAttribute('aria-busy', 'true');
  10. host.textContent = 'Dokumente werden geladen …';
  11. try {
  12. const url = `/app/forderungen/documents/${encodeURIComponent(host.dataset.documentKey)}?scope=${scope}&detail=${host.dataset.documentDetail === 'true'}`;
  13. const response = await fetch(url, {signal: controller.signal});
  14. if (!response.ok) throw new Error();
  15. const html = await response.text();
  16. if (!controller.signal.aborted && host.isConnected) host.innerHTML = html;
  17. } catch (error) {
  18. if (error.name !== 'AbortError' && host.isConnected) {
  19. host.textContent = 'Die Dokumentliste konnte nicht geladen werden. ';
  20. const retry = document.createElement('button'); retry.type = 'button'; retry.textContent = 'Erneut versuchen';
  21. retry.addEventListener('click', () => load(host)); host.append(retry);
  22. }
  23. } finally { if (requests.get(host) === controller) host.removeAttribute('aria-busy'); }
  24. }
  25. function init(root = document) {
  26. const hosts = [...(root.matches?.('[data-document-list]') ? [root] : []), ...root.querySelectorAll('[data-document-list]')];
  27. for (const host of hosts) if (!initialized.has(host)) { initialized.add(host); load(host); }
  28. }
  29. document.addEventListener('click', async event => {
  30. const open = event.target.closest('[data-document-open]');
  31. if (open) {
  32. event.preventDefault();
  33. const body = document.getElementById('documents-modal-body');
  34. body.querySelectorAll('[data-document-list]').forEach(host => requests.get(host)?.abort());
  35. const host = document.createElement('div'); host.dataset.documentList = ''; host.dataset.documentKey = open.dataset.documentOpen;
  36. body.replaceChildren(host); document.getElementById('documents-modal-title').textContent = `Dokumente zu Beleg ${open.dataset.documentOpen.split('_').slice(1).join('_')}`;
  37. modal('documents-modal').show(); init(host); return;
  38. }
  39. const filter = event.target.closest('[data-document-scope]');
  40. if (filter) { const host = filter.closest('[data-document-list]'); scopes.set(host.dataset.documentKey, filter.dataset.documentScope); load(host); return; }
  41. const forward = event.target.closest('[data-document-recipients]');
  42. if (forward) {
  43. const host = forward.closest('[data-document-list]'), target = host.querySelector('[data-document-mail]');
  44. requests.get(target)?.abort(); const controller = new AbortController(); requests.set(target, controller);
  45. target.textContent = 'Empfänger werden geladen …';
  46. try { const response = await fetch(forward.dataset.documentRecipients, {signal: controller.signal}); if (!response.ok) throw new Error(); const html = await response.text(); if (!controller.signal.aborted && target.isConnected) target.innerHTML = html; }
  47. catch (error) { if (error.name !== 'AbortError') target.textContent = 'Empfänger konnten nicht geladen werden. Bitte erneut versuchen.'; }
  48. return;
  49. }
  50. const link = event.target.closest('[data-document-preview]');
  51. if (link) {
  52. event.preventDefault(); previewController?.abort(); previewController = new AbortController();
  53. const controller = previewController, frame = document.getElementById('document-pdf-frame'), status = document.querySelector('[data-preview-status]');
  54. frame.removeAttribute('src'); if (previewURL) URL.revokeObjectURL(previewURL);
  55. returnToList = document.getElementById('documents-modal').classList.contains('show');
  56. if (returnToList) {
  57. await new Promise(resolve => {
  58. document.getElementById('documents-modal').addEventListener('hidden.coreui.modal', resolve, {once: true});
  59. modal('documents-modal').hide();
  60. });
  61. }
  62. if (controller.signal.aborted) return;
  63. modal('document-preview-modal').show(); status.textContent = 'Dokument wird geladen …';
  64. try { const response = await fetch(link.href, {signal: controller.signal}); if (!response.ok) throw new Error(); const blob = await response.blob(); if (controller.signal.aborted) return; previewURL = URL.createObjectURL(blob); frame.src = previewURL; status.textContent = ''; }
  65. catch (error) { if (error.name !== 'AbortError') status.textContent = 'Dokument konnte nicht geladen werden. Bitte zur Liste zurückkehren und erneut versuchen.'; }
  66. }
  67. if (event.target.closest('[data-document-back]')) modal('document-preview-modal').hide();
  68. });
  69. document.addEventListener('hidden.coreui.modal', event => {
  70. if (event.target.id === 'document-preview-modal') { previewController?.abort(); document.getElementById('document-pdf-frame').removeAttribute('src'); if (previewURL) URL.revokeObjectURL(previewURL); if (returnToList) modal('documents-modal').show(); }
  71. });
  72. document.addEventListener('DOMContentLoaded', () => init());
  73. document.addEventListener('htmx:load', event => init(event.detail.elt));
  74. document.addEventListener('htmx:afterSettle', () => init());
  75. window.addEventListener('pageshow', () => init());
  76. })();