fix: remote destinations tab broken (Add Destination + stuck loading) #208

Merged
jmiller merged 2 commits from fix/remote-destinations-modal into main 2026-07-04 22:00:46 +00:00
Owner

Problem

On the profile editor's Remote tab (reported on suite.dev):

  • The remote destinations table is stuck at "Loading…".
  • Clicking Add Destination does nothing.

Root cause

Both symptoms share one cause. The DOMContentLoaded handler in tmpl/profile/edit.php instantiated the Bootstrap modal eagerly near the top:

const modal = bootstrap.Modal.getOrCreateInstance(modalEl);

In Joomla 6, Bootstrap ships as a deferred ES module, so window.bootstrap is not defined yet when this inline script runs at DOMContentLoaded. The reference throws a ReferenceError that aborts the entire handler — so loadRemotes() is never called (table stays "Loading…") and the Add Destination click listener is never bound (button dead).

The purge and stepped-backup modals were unaffected because they resolve bootstrap.Modal lazily inside their click handlers (by which time the module has loaded).

Fix

  • Resolve the modal lazily via getModal() at click-time, matching the working modals — so the table load and button binding no longer depend on Bootstrap being ready at DOMContentLoaded.
  • Explicitly load the bootstrap.modal web asset so window.bootstrap.Modal is registered.

Test plan

  • Open a saved backup profile → Remote tab: the destinations table loads (shows rows or "none configured"), not stuck on "Loading…".
  • Click Add Destination → the modal opens.
  • Add/edit/save a destination → modal closes and the table refreshes.
## Problem On the profile editor's **Remote** tab (reported on suite.dev): - The remote destinations table is stuck at **"Loading…"**. - Clicking **Add Destination** does nothing. ## Root cause Both symptoms share one cause. The `DOMContentLoaded` handler in `tmpl/profile/edit.php` instantiated the Bootstrap modal **eagerly** near the top: ```js const modal = bootstrap.Modal.getOrCreateInstance(modalEl); ``` In Joomla 6, Bootstrap ships as a **deferred ES module**, so `window.bootstrap` is not defined yet when this inline script runs at `DOMContentLoaded`. The reference throws a `ReferenceError` that **aborts the entire handler** — so `loadRemotes()` is never called (table stays "Loading…") and the Add Destination click listener is never bound (button dead). The purge and stepped-backup modals were unaffected because they resolve `bootstrap.Modal` **lazily inside their click handlers** (by which time the module has loaded). ## Fix - Resolve the modal **lazily** via `getModal()` at click-time, matching the working modals — so the table load and button binding no longer depend on Bootstrap being ready at `DOMContentLoaded`. - Explicitly load the `bootstrap.modal` web asset so `window.bootstrap.Modal` is registered. ## Test plan - [ ] Open a **saved** backup profile → Remote tab: the destinations table loads (shows rows or "none configured"), not stuck on "Loading…". - [ ] Click **Add Destination** → the modal opens. - [ ] Add/edit/save a destination → modal closes and the table refreshes.
jmiller added 1 commit 2026-07-04 22:00:19 +00:00
fix(profile): remote destinations tab broken — lazy-init Bootstrap modal
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 11s
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Universal: PR Check / Secret Scan (pull_request) Successful in 10s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 14s
Generic: Repo Health / Access control (pull_request) Successful in 3s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 14s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 39s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 45s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
4ca9e23630
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
jmiller force-pushed fix/remote-destinations-modal from 3ef36fd41e to 4ca9e23630 2026-07-04 22:00:19 +00:00 Compare
jmiller merged commit 183b2d2d6f into main 2026-07-04 22:00:46 +00:00
jmiller deleted branch fix/remote-destinations-modal 2026-07-04 22:00:49 +00:00
Sign in to join this conversation.