fix(frontend): container-logs misuse of wireModal blocked rest of features.js

container-logs.js called `wireModal(modal, null, closeModal)` — passing
the local `closeModal()` function as a third arg where wireModal expects
button elements. wireModal then did `closeModal.addEventListener('click',...)`,
threw TypeError, and because each module's IIFE is a top-level statement
in the concatenated features.js bundle, every IIFE *after* container-logs
silently skipped: snapshot, smart-arr-connect, notification-settings,
panel-tabs, backup-restore, resource-monitor, health-check, update-
management, docker-resources, compose-import, container-exec, audit-log,
weather, clock, card-badges, theme-builder, and license. Symptoms:
"Customize Theme" did nothing on click, license badge stuck at "FREE TIER"
(because license.js never ran), no weather, etc.

- container-logs.js: drop the wireModal call, wire backdrop click directly
  to the local closeModal so the SSE log stream actually stops on close.
- globals.js: harden wireModal — skip any closeBtn that isn't a real
  EventTarget. One typo upstream shouldn't take down the rest of features.js
  init silently.
This commit is contained in:
Sami
2026-05-17 02:34:51 -07:00
parent ac723630f2
commit 7e4c3a0963
2 changed files with 13 additions and 3 deletions
+4 -2
View File
@@ -398,8 +398,10 @@
}
});
// Wire modal (close on backdrop click)
wireModal(modal, null, closeModal);
// Wire backdrop click to closeModal so the log stream stops too.
// (Can't use wireModal here — it only does modal.classList.remove('show')
// on backdrop click, which would leak the SSE/stream connection.)
modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(); });
// Expose for use by service card buttons (grid.js calls openContainerLogsModal)
window.openContainerLogsModal = function(containerId, containerName) {
+9 -1
View File
@@ -293,7 +293,15 @@ function closeModal(id) {
function wireModal(modal, ...closeBtns) {
if (!modal) return;
modal.addEventListener('click', (e) => { if (e.target === modal) modal.classList.remove('show'); });
closeBtns.forEach(btn => btn?.addEventListener('click', () => modal.classList.remove('show')));
// Skip anything that isn't a real listener target. Without this guard, a
// single typo (e.g. passing a function or null instead of a button element)
// throws and aborts the rest of the calling module's init — which can take
// down license.js (FREE TIER badge), TOTP wiring, etc.
closeBtns.forEach(btn => {
if (btn && typeof btn.addEventListener === 'function') {
btn.addEventListener('click', () => modal.classList.remove('show'));
}
});
}
/** Toast-style notification (replaces all alert() usage) */