diff --git a/.last-branch b/.last-branch index d11047e..7313130 100644 --- a/.last-branch +++ b/.last-branch @@ -1 +1 @@ -v20260120-09-runchecks-modal-sequence-fix +v20260120-10-runchecks-bootstrap-compat-fix diff --git a/containers/backupchecks/src/templates/main/run_checks.html b/containers/backupchecks/src/templates/main/run_checks.html index fe1a50f..f1a713d 100644 --- a/containers/backupchecks/src/templates/main/run_checks.html +++ b/containers/backupchecks/src/templates/main/run_checks.html @@ -1013,11 +1013,9 @@ table.addEventListener('change', function (e) { if (btnAutotaskLink) { var linkModalEl = document.getElementById('autotaskLinkModal'); - var linkModal = linkModalEl ? bootstrap.Modal.getOrCreateInstance(linkModalEl) : null; // Avoid stacked Bootstrap modals: temporarily hide the main Run Checks modal // and re-open it when the Autotask link modal is closed. var mainModalEl = document.getElementById('runChecksModal'); - var mainModal = mainModalEl ? bootstrap.Modal.getOrCreateInstance(mainModalEl) : null; var reopenMainAfterLinkModal = false; var atlSearch = document.getElementById('atl_search'); var atlRefresh = document.getElementById('atl_refresh'); @@ -1025,15 +1023,13 @@ table.addEventListener('change', function (e) { var atlTbody = document.getElementById('atl_tbody'); var atlEmpty = document.getElementById('atl_empty'); - if (linkModalEl) { - linkModalEl.addEventListener('hidden.bs.modal', function () { - if (reopenMainAfterLinkModal && mainModal) { - reopenMainAfterLinkModal = false; - // Re-open the main modal so the normal Run Checks workflow continues. - mainModal.show(); - } - }); - } + _modalOnHidden(linkModalEl, function () { + if (reopenMainAfterLinkModal && mainModalEl) { + reopenMainAfterLinkModal = false; + // Re-open the main modal so the normal Run Checks workflow continues. + _modalShow(mainModalEl); + } + }); function renderAtlRows(items) { if (!atlTbody) return; @@ -1057,7 +1053,7 @@ table.addEventListener('change', function (e) { .then(function (j) { if (!j || j.status !== 'ok') throw new Error((j && j.message) || 'Failed.'); if (atlStatus) atlStatus.textContent = ''; - if (linkModal) linkModal.hide(); + _modalHide(linkModalEl); // Refresh modal data so UI reflects stored ticket linkage. var keepRunId = currentRunId; @@ -1131,11 +1127,11 @@ table.addEventListener('change', function (e) { renderAtlRows([]); // Show the existing Run Checks popup first, then switch to the Autotask popup. // This prevents the main popup from breaking due to stacked modal backdrops. - if (mainModal) { + if (mainModalEl) { reopenMainAfterLinkModal = true; - mainModal.hide(); + _modalHide(mainModalEl); } - if (linkModal) linkModal.show(); + _modalShow(linkModalEl); loadExistingTickets(); }); } @@ -1315,6 +1311,47 @@ table.addEventListener('change', function (e) { } } + // Modal helpers + // The global "bootstrap" namespace is not always available (e.g. Bootstrap 4 + jQuery). + // Use a small compatibility layer so Run Checks modals keep working. + function _modalShow(modalEl) { + if (!modalEl) return; + if (window.bootstrap && window.bootstrap.Modal && window.bootstrap.Modal.getOrCreateInstance) { + window.bootstrap.Modal.getOrCreateInstance(modalEl).show(); + return; + } + if (window.jQuery) { + window.jQuery(modalEl).modal('show'); + return; + } + // Last resort: basic display (should not normally be needed) + modalEl.classList.add('show'); + modalEl.style.display = 'block'; + } + + function _modalHide(modalEl) { + if (!modalEl) return; + if (window.bootstrap && window.bootstrap.Modal && window.bootstrap.Modal.getOrCreateInstance) { + window.bootstrap.Modal.getOrCreateInstance(modalEl).hide(); + return; + } + if (window.jQuery) { + window.jQuery(modalEl).modal('hide'); + return; + } + modalEl.classList.remove('show'); + modalEl.style.display = 'none'; + } + + function _modalOnHidden(modalEl, handler) { + if (!modalEl || !handler) return; + if (window.jQuery) { + window.jQuery(modalEl).one('hidden.bs.modal', handler); + return; + } + modalEl.addEventListener('hidden.bs.modal', handler); + } + function openJobModal(jobId) { if (!jobId) return; currentJobId = jobId; @@ -1323,8 +1360,7 @@ table.addEventListener('change', function (e) { if (btnMarkSuccessOverride) btnMarkSuccessOverride.disabled = true; var modalEl = document.getElementById('runChecksModal'); - var modal = bootstrap.Modal.getOrCreateInstance(modalEl); - modal.show(); + _modalShow(modalEl); document.getElementById('rcm_loading').style.display = 'block'; document.getElementById('rcm_content').style.display = 'none'; diff --git a/docs/changelog.md b/docs/changelog.md index 94a0181..7485cfe 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -444,6 +444,13 @@ Changes: - Automatically reopens the Run Checks modal when the Autotask popup is closed. - Prevented broken backdrops, focus loss, and non-responsive popups caused by multiple active modals. +## v20260120-10-runchecks-bootstrap-compat-fix + +- Fixed Run Checks page crash caused by referencing the Bootstrap 5 global "bootstrap" object when it is not available. +- Added Bootstrap 4/5 compatible modal helpers (show/hide/hidden event) using jQuery modal API when needed. +- Updated Run Checks modal opening and Autotask link modal flow to use the compatibility helpers. +- Restored normal Run Checks popup behavior (click handlers execute again because the page no longer errors on load). + *** ## v0.1.21