(function () { function escapeHtml(value) { return String(value ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function formatDashboardDate(value) { if (!value) { return ''; } var date = new Date(value); if (Number.isNaN(date.getTime())) { return ''; } return new Intl.DateTimeFormat('en-US', { month: 'short', day: '2-digit', year: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit' }).format(date); } function getClientRowKey(client) { return String(client && client.id ? client.id : client && client.clientId ? client.clientId : '').trim(); } function getClientDisplayName(client) { if (client && client.client_name) { return String(client.client_name).trim(); } var clientId = String(client && client.clientId ? client.clientId : '').trim(); if (clientId) { return clientId; } return ''; } function setButtonVariant(button, classesToRemove, classToAdd) { if (!button) { return; } if (button.classList) { classesToRemove.forEach(function (className) { button.classList.remove(className); }); if (classToAdd) { button.classList.add(classToAdd); } return; } var className = String(button.className || ''); classesToRemove.forEach(function (removeClass) { className = className.replace(new RegExp('(^|\\s)' + removeClass + '(?=\\s|$)', 'g'), ' '); }); if (classToAdd) { className += ' ' + classToAdd; } button.className = className.replace(/\s+/g, ' ').trim(); } function normalizeDisplayIp(value) { var ip = String(value || '').trim(); if (!ip) { return ''; } if (ip.toLowerCase().indexOf('::ffff:') === 0) { return ip.slice(7).trim(); } return ip; } function initConfirmForms() { document.addEventListener('submit', function (event) { var form = event.target; if (!form || !form.getAttribute) { return; } if (form.hasAttribute && form.hasAttribute('data-async-command')) { return; } var message = form.getAttribute('data-confirm-message'); if (message && !window.confirm(message)) { event.preventDefault(); } }); } function markFormDirty(form) { if (!form || !form.hasAttribute || form.hasAttribute('data-clean-on-load')) { return; } form.dataset.dirty = 'true'; } function clearFormDirty(form) { if (!form) { return; } form.dataset.dirty = 'false'; } function isFormDirty(form) { return Boolean(form && form.dataset && form.dataset.dirty === 'true'); } function initDirtyTracking() { document.addEventListener('input', function (event) { var target = event.target; if (!target || !target.form) { return; } markFormDirty(target.form); }, true); document.addEventListener('change', function (event) { var target = event.target; if (!target || !target.form) { return; } markFormDirty(target.form); }, true); } function initCancelConfirm() { document.addEventListener('click', function (event) { var cancelTarget = event.target.closest('[data-confirm-unsaved]'); if (!cancelTarget) { return; } var form = cancelTarget.form || cancelTarget.closest('form') || document.querySelector('form[data-dirty="true"]'); if (!isFormDirty(form)) { return; } var message = cancelTarget.getAttribute('data-confirm-unsaved') || 'You have unsaved changes. Leave this page?'; if (!window.confirm(message)) { event.preventDefault(); event.stopPropagation(); } }, true); } function initAsyncCommandForms() { document.addEventListener('submit', function (event) { var form = event.target; if (!form || !form.hasAttribute || !form.hasAttribute('data-async-command')) { return; } if (form.dataset && form.dataset.busy === 'true') { event.preventDefault(); return; } var message = form.getAttribute('data-confirm-message'); if (message && !window.confirm(message)) { event.preventDefault(); return; } event.preventDefault(); form.dataset.busy = 'true'; var formData = new FormData(form); var body = new URLSearchParams(); formData.forEach(function (value, key) { body.append(key, value); }); fetch(form.action, { method: (form.method || 'POST').toUpperCase(), headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json, text/plain, */*' }, body: body.toString(), credentials: 'same-origin' }).finally(function () { delete form.dataset.busy; }); }, true); } function initAsyncSaveForms() { var refreshSequence = 0; function setSaveActionValue(form, value) { if (!form) { return; } var hiddenInput = form.querySelector('input[type="hidden"][name="save_action"]'); if (!hiddenInput) { hiddenInput = document.createElement('input'); hiddenInput.type = 'hidden'; hiddenInput.name = 'save_action'; form.appendChild(hiddenInput); } hiddenInput.value = String(value || '').trim().toLowerCase(); } function getResponseQueryValue(responseUrl, key) { try { var url = new URL(responseUrl, window.location.href); return String(url.searchParams.get(key) || '').trim(); } catch (_error) { return ''; } } function rebindRefreshTarget(targetElement) { if (!targetElement) { return; } if (typeof window.initJsonTogglePanels === 'function') { window.initJsonTogglePanels(targetElement); } if (typeof window.initLocalDateTimes === 'function') { window.initLocalDateTimes(targetElement); } if (typeof window.initTableSearches === 'function') { window.initTableSearches(targetElement); } if (typeof window.initTablePaginations === 'function') { window.initTablePaginations(targetElement); } } function replaceRefreshTargetFromPage(refreshTargetSelector, sequenceId) { if (sequenceId !== refreshSequence) { return; } fetch(window.location.href, { method: 'GET', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'text/html, application/xhtml+xml' }, credentials: 'same-origin', cache: 'no-store' }).then(function (response) { if (!response.ok) { throw new Error('Unable to refresh saved data.'); } return response.text(); }).then(function (text) { if (sequenceId !== refreshSequence) { return; } var responseDocument = new DOMParser().parseFromString(text || '', 'text/html'); var currentTarget = document.querySelector(refreshTargetSelector); var nextTarget = responseDocument.querySelector(refreshTargetSelector); if (!currentTarget || !nextTarget) { return; } currentTarget.outerHTML = nextTarget.outerHTML; rebindRefreshTarget(document.querySelector(refreshTargetSelector)); }).catch(function (_error) { // Ignore refresh replacement failures and leave the existing content in place. }); } function watchRefreshTask(refreshStateUrl, refreshTaskId, refreshTargetSelector, sequenceId) { var pollDelayMs = 1000; var maxAttempts = 60; function poll(attempt) { if (sequenceId !== refreshSequence) { return; } var stateUrl; try { stateUrl = new URL(refreshStateUrl, window.location.href); } catch (_error) { return; } stateUrl.searchParams.set('refresh_task_id', refreshTaskId); fetch(stateUrl.toString(), { method: 'GET', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json, text/plain, */*' }, credentials: 'same-origin', cache: 'no-store' }).then(function (response) { if (!response.ok) { throw new Error('Unable to check refresh status.'); } return response.json(); }).then(function (payload) { if (sequenceId !== refreshSequence) { return; } var status = String(payload && payload.status || '').trim().toLowerCase(); if (status === 'queued' || status === 'running') { if (attempt < maxAttempts) { window.setTimeout(function () { poll(attempt + 1); }, pollDelayMs); } return; } replaceRefreshTargetFromPage(refreshTargetSelector, sequenceId); }).catch(function () { if (attempt < maxAttempts) { window.setTimeout(function () { poll(attempt + 1); }, pollDelayMs); } }); } poll(0); } document.addEventListener('click', function (event) { var target = event.target; if (!target || !target.closest) { return; } var button = target.closest('button[name="save_action"]'); if (!button) { return; } var form = button.form || button.closest('form'); if (!form || !form.hasAttribute || !form.hasAttribute('data-async-save')) { return; } setSaveActionValue(form, button.value || ''); form.dataset.submitterValue = String(button.value || '').trim().toLowerCase(); }, true); document.addEventListener('submit', function (event) { var form = event.target; if (!form || !form.hasAttribute || !form.hasAttribute('data-async-save')) { return; } if (form.dataset && form.dataset.busy === 'true') { event.preventDefault(); return; } var message = form.getAttribute('data-confirm-message'); if (message && !window.confirm(message)) { event.preventDefault(); return; } event.preventDefault(); form.dataset.busy = 'true'; var hiddenSaveAction = form.querySelector('input[type="hidden"][name="save_action"]'); var formData = new FormData(form); var submitterValue = String((hiddenSaveAction && hiddenSaveAction.value) || form.dataset.submitterValue || '').trim().toLowerCase(); if (event.submitter && event.submitter.name) { submitterValue = String(event.submitter.value || '').trim().toLowerCase(); formData.set(event.submitter.name, event.submitter.value || ''); } var hasFileValue = false; formData.forEach(function (value) { if (value && typeof value === 'object' && typeof value.name === 'string') { hasFileValue = true; } }); var isMultipart = hasFileValue || String(form.enctype || '').toLowerCase() === 'multipart/form-data'; var body = isMultipart ? formData : new URLSearchParams(); if (!isMultipart) { formData.forEach(function (value, key) { body.append(key, value); }); } fetch(form.action, { method: (form.method || 'POST').toUpperCase(), headers: Object.assign({ 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'text/html, application/json, text/plain, */*' }, isMultipart ? {} : { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }), body: isMultipart ? body : body.toString(), credentials: 'same-origin' }).then(function (response) { if (!response.ok) { return response.text().then(function (text) { var error = new Error(text || 'Unable to save changes.'); error.status = response.status; throw error; }); } var actionUrl = ''; try { actionUrl = new URL(form.action, window.location.href).pathname; } catch (_error) { actionUrl = String(form.action || ''); } var refreshTargetSelector = String(form.getAttribute('data-async-save-refresh-target') || '').trim(); var refreshStateUrl = String(form.getAttribute('data-async-save-refresh-state-url') || '').trim(); var refreshTaskId = getResponseQueryValue(response.url || '', 'refresh_task_id'); var shouldFollowRedirect = Boolean(form.hasAttribute('data-async-save-new-url')) && !/\/\d+(?:\/|$)/.test(actionUrl); if (submitterValue === 'close' || submitterValue === 'new') { var redirectUrl = submitterValue === 'close' ? String(form.dataset.asyncSaveCloseUrl || response.url || window.location.href) : String(form.dataset.asyncSaveNewUrl || response.url || window.location.href); window.location.replace(redirectUrl); return; } if (shouldFollowRedirect && response.url) { clearFormDirty(form); window.location.replace(response.url); return; } if (form.hasAttribute('data-async-save-reload-on-success')) { clearFormDirty(form); window.location.replace(response.url || window.location.href); return; } clearFormDirty(form); return response.text().then(function (text) { var savedMessage = ''; var responseDocument = null; try { responseDocument = new DOMParser().parseFromString(text || '', 'text/html'); var toastBody = responseDocument.querySelector('.toast-body'); if (toastBody && toastBody.textContent) { savedMessage = toastBody.textContent.trim(); } } catch (_error) { savedMessage = ''; } if (refreshTargetSelector && refreshStateUrl && refreshTaskId) { refreshSequence += 1; watchRefreshTask(refreshStateUrl, refreshTaskId, refreshTargetSelector, refreshSequence); } else if (refreshTargetSelector && responseDocument) { var currentTarget = document.querySelector(refreshTargetSelector); var nextTarget = responseDocument.querySelector(refreshTargetSelector); if (currentTarget && nextTarget) { currentTarget.outerHTML = nextTarget.outerHTML; rebindRefreshTarget(document.querySelector(refreshTargetSelector)); } } showToast(savedMessage || 'Saved.', 'success'); }); }).catch(function (error) { if (typeof showToast === 'function') { var variant = Number(error && error.status) >= 400 && Number(error && error.status) < 500 ? 'warning' : 'danger'; showToast(error.message || 'Unable to save changes.', variant); return; } window.alert(error.message || 'Unable to save changes.'); }).finally(function () { delete form.dataset.busy; delete form.dataset.submitterValue; if (hiddenSaveAction) { hiddenSaveAction.value = ''; } }); }, true); } function initSubmitOnChange() { var fields = document.querySelectorAll('[data-submit-on-change]'); Array.prototype.forEach.call(fields, function (field) { field.addEventListener('change', function () { var form = field.form || field.closest('form'); if (form) { form.submit(); } }); }); } function initJsonTogglePanels(root) { var scope = root && root.querySelectorAll ? root : document; var panels = scope.querySelectorAll('[data-json-toggle-panel]'); Array.prototype.forEach.call(panels, function (panel) { var output = panel.querySelector('[data-json-toggle-output]'); if (!output) { return; } var card = panel.closest ? panel.closest('.card') : null; var button = card ? card.querySelector('[data-json-toggle]') : null; var label = button ? button.querySelector('[data-json-toggle-label]') : null; var sourceNode = panel.querySelector('[data-json-toggle-source]'); var rawJson = ''; try { rawJson = JSON.parse(String(sourceNode ? sourceNode.textContent : '""')); } catch (_error) { rawJson = ''; } if (typeof rawJson !== 'string' || !rawJson.trim()) { if (button) { button.classList.add('d-none'); } return; } var parsedJson; try { parsedJson = JSON.parse(rawJson); } catch (_error) { if (button) { button.classList.add('d-none'); } return; } var compactJson = JSON.stringify(parsedJson); var formattedJson = JSON.stringify(parsedJson, null, 2); var isFormatted = true; function syncButtonLabel() { if (!button || !label) { return; } label.textContent = isFormatted ? String(button.getAttribute('data-json-toggle-label-compact') || 'Unformat JSON') : String(button.getAttribute('data-json-toggle-label-formatted') || 'Format JSON'); button.setAttribute('aria-pressed', isFormatted ? 'true' : 'false'); } function syncOutput() { output.textContent = isFormatted ? formattedJson : compactJson; syncButtonLabel(); } output.textContent = formattedJson; syncButtonLabel(); if (button) { button.addEventListener('click', function () { isFormatted = !isFormatted; syncOutput(); }); } }); } function initLocalDateTimes(root) { var scope = root && root.querySelectorAll ? root : document; var elements = scope.querySelectorAll('[data-local-datetime]'); Array.prototype.forEach.call(elements, function (element) { var rawValue = String(element.getAttribute('datetime') || element.getAttribute('data-local-datetime') || element.textContent || '').trim(); if (!rawValue) { return; } var date = new Date(rawValue); if (Number.isNaN(date.getTime())) { return; } element.textContent = formatDashboardDate(date); }); } function initTablePaginations(root) { var scope = root && root.querySelectorAll ? root : document; Array.prototype.forEach.call(scope.querySelectorAll('[data-table-pagination="auto"]'), function (container) { if (container.getAttribute('data-table-pagination-bound') === 'true') { return; } var table = container.querySelector('[data-table-searchable]'); var cardHeader = container.querySelector('.card-header'); var cardBody = container.querySelector('.card-body.table-responsive') || container.querySelector('.card-body'); var footer = container.querySelector('[data-table-pagination-controls]'); var footerSummary = null; var footerList = null; var syncFrame = 0; var state = { currentPage: 1, pageSize: 1, totalPages: 1 }; if (!table || !cardBody || !table.tBodies.length) { return; } container.setAttribute('data-table-pagination-bound', 'true'); function getRows() { return Array.prototype.slice.call(table.querySelectorAll('tbody tr[data-table-search-row]')); } function isSearchMatched(row) { return String(row.getAttribute('data-table-search-match') || 'true') !== 'false'; } function ensureFooter() { if (footer) { footerSummary = footer.querySelector('[data-table-pagination-summary]'); footerList = footer.querySelector('[data-table-pagination-list]'); return footer; } footer = document.createElement('div'); footer.className = 'card-footer d-none'; footer.setAttribute('data-table-pagination-controls', 'true'); footer.innerHTML = '' + '