From 6ae38784d99399b73f969fce726da2f11ff6f0e9 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 4 Jul 2026 16:58:14 -0500 Subject: [PATCH] =?UTF-8?q?fix(profile):=20remote=20destinations=20tab=20b?= =?UTF-8?q?roken=20=E2=80=94=20lazy-init=20Bootstrap=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remote destinations table stayed stuck at "Loading…" and the "Add Destination" button did nothing. Root cause: the profile-edit DOMContentLoaded handler instantiated the Bootstrap modal eagerly at the top (`bootstrap.Modal.getOrCreateInstance`). In Joomla 6, Bootstrap loads as a deferred ES module, so `bootstrap` is undefined at DOMContentLoaded — the reference threw a ReferenceError that aborted the entire handler, so loadRemotes() never ran and the Add button was never bound. (The purge and stepped-backup modals worked because they resolve the modal lazily inside click handlers.) - Resolve the modal lazily via getModal() at click-time (matches the working modals) - Explicitly load the bootstrap.modal web asset so window.bootstrap.Modal is registered Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i --- .../com_mokosuitebackup/tmpl/profile/edit.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/packages/com_mokosuitebackup/tmpl/profile/edit.php b/source/packages/com_mokosuitebackup/tmpl/profile/edit.php index 85ccad8..5ec55d5 100644 --- a/source/packages/com_mokosuitebackup/tmpl/profile/edit.php +++ b/source/packages/com_mokosuitebackup/tmpl/profile/edit.php @@ -17,6 +17,7 @@ use Joomla\CMS\Session\Session; HTMLHelper::_('behavior.formvalidator'); HTMLHelper::_('behavior.keepalive'); +HTMLHelper::_('bootstrap.modal'); $profileId = (int) $this->item->id; $token = Session::getFormToken(); @@ -274,7 +275,11 @@ document.addEventListener('DOMContentLoaded', function() { const emptyMsg = document.getElementById('remoteDestEmpty'); const loadingTr = document.getElementById('remoteDestLoading'); const modalEl = document.getElementById('remoteModal'); - const modal = bootstrap.Modal.getOrCreateInstance(modalEl); + // Lazy: resolve the Bootstrap modal at click-time. Bootstrap loads as a + // deferred ES module, so `bootstrap` is not defined yet at DOMContentLoaded; + // referencing it here would throw and abort the whole handler (leaving the + // table stuck at "Loading…" and the Add button unbound). + const getModal = () => bootstrap.Modal.getOrCreateInstance(modalEl); // Type badge colours const typeBadge = {sftp: 'bg-primary', s3: 'bg-warning text-dark', google_drive: 'bg-success'}; @@ -507,7 +512,7 @@ document.addEventListener('DOMContentLoaded', function() { } updateTypeFields(); - modal.show(); + getModal().show(); } // ---- Type selector toggles field visibility ---- @@ -587,7 +592,7 @@ document.addEventListener('DOMContentLoaded', function() { return; } - modal.hide(); + getModal().hide(); loadRemotes(); }) .catch(() => {