fix: show loading spinner and disable submit button during checkout provisioning#1222
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (50)
📝 WalkthroughWalkthroughModernizes 40+ JavaScript files to ES2015+ syntax—const/let, method shorthand, arrow functions, IIFE wrapping—while adding checkout provisioning visibility (disable buttons, show "provisioning" message) and refining thank-you page polling to improve user feedback during site creation. ChangesCheckout Provisioning User Feedback
JavaScript ES2015+ Syntax Modernization
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
🔨 Build Complete - Ready for Testing!📦 Download Build Artifact (Recommended)Download the zip build, upload to WordPress and test:
🌐 Test in WordPress Playground (Very Experimental)Click the link below to instantly test this PR in your browser - no installation needed! Login credentials: |
d463819 to
f67491a
Compare
|
Performance Test Results Performance test results for 51495ad are in 🛎️! Note: the numbers in parentheses show the difference to the previous (baseline) test run. Differences below 2% or 0.5 in absolute values are not shown. URL:
|
There was a problem hiding this comment.
Actionable comments posted: 19
🧹 Nitpick comments (2)
assets/js/url-preview.js (1)
20-30: ⚡ Quick winRemove duplicated
keyuphandler to avoid double execution.This block duplicates the previous
keyupbinding for the same selector/event and runs twice on each keystroke.As per coding guidelines, "Use WordPress ESLint config; indentation must be tabs; jQuery and Vue.js globals are available".Suggested fix
- $('.login').on('keyup', '`#field-site_url`', function(event) { - - event.preventDefault(); - - const $selector = $(this); - - const $target = $('`#wu-your-site`'); - - $target.text($selector.val()); - - }); // end on.keyUp;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@assets/js/url-preview.js` around lines 20 - 30, There are two identical keyup handlers bound to $('.login').on('keyup', '`#field-site_url`', ...) causing double execution; remove the duplicate block (or consolidate into a single handler) so only one listener updates $('`#wu-your-site`').text($(this).val()); alternatively, if you must re-bind, call $('.login').off('keyup', '`#field-site_url`') before .on to prevent duplicates and ensure indentation uses tabs and jQuery globals per ESLint guidelines.assets/js/vue-apps.js (1)
229-235: ⚡ Quick winAdd null guards in the
wubox:unloadcleanup path.Line 230-Line 233 assume both
#WUB_windowand[data-wu-app]always exist. If either is missing during teardown, this throws and skips cleanup.Proposed patch
document.body.addEventListener("wubox:unload", function() { const modal = document.getElementById("WUB_window"); - const app = modal.querySelector("ul[data-wu-app]"); + if (! modal) { + return; + } + const app = modal.querySelector("ul[data-wu-app]"); + if (! app) { + return; + } const app_name = "wu_" + app.dataset.wuApp; delete window[ app_name ]; delete window[ app_name + "_errors" ]; });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@assets/js/vue-apps.js` around lines 229 - 235, The wubox:unload handler assumes document.getElementById("WUB_window") and modal.querySelector("ul[data-wu-app]") always exist and will throw if absent; update the event listener to null-guard the modal and app (and the app.dataset.wuApp) before constructing app_name and deleting window properties (only call delete when the referenced elements/values are present) so cleanup never throws; target the listener function that references modal, app, app_name and the window[...] deletes when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@assets/js/admin-screen.js`:
- Around line 15-17: The template strings for $elem and $page_elem in
assets/js/admin-screen.js open anchor tags (<a>) but incorrectly close them with
</button>; update the closing tags to </a> for both variables (references: $elem
and $page_elem) so the generated HTML uses matching <a>...</a> tags and avoid
DOM parsing issues when appending.
In `@assets/js/checkout-forms-editor.js`:
- Around line 206-209: Guard the call to scrollIntoView by checking the result
of document.getElementById(element_id) before using it: after obtaining const
element = document.getElementById(element_id) (in the code that calls
element.scrollIntoView), add a null-check (if (!element) { /* handle or return
*/ }) and only call element.scrollIntoView({ behavior: 'smooth', block:
'center', inline: 'nearest' }) when element is non-null; optionally log or
handle the missing element case for debugging.
In `@assets/js/checkout.js`:
- Around line 1630-1642: Submit buttons are disabled via
jQuery(this).find('button[type="submit"]').prop('disabled', true') before
calling that.block(loadingMessage) but are never re-enabled on validation/API
failure paths; update all failure/exit branches that currently call unblock()
(including the paths around the other unblock() calls at the 1659-1661 region)
to also re-enable the buttons by calling
jQuery(this).find('button[type="submit"]').prop('disabled', false') (or capture
the form/buttons before disabling and call a single helper like
enableSubmitButtons() in every callback), ensuring every path that calls
that.block(...) or unblock() will restore the submit controls.
In `@assets/js/coupon-code.js`:
- Around line 169-175: Declare setupfee_off_with_symbol as a local variable
inside the applies_to_setup_fee block to avoid creating an implicit global;
mirror the pattern used for off_with_symbol by adding a local let/const
declaration before assigning it, and keep the existing conditional logic that
checks setup_fee_discount_type and uses setup_fee_discount_value and
accounting.formatMoney to produce the formatted value.
In `@assets/js/customizer.js`:
- Around line 20-28: The iframe load handler bound to
$('`#preview-stage-iframe`').on('load', ...) is being added repeatedly (it's
registered inside a message event), causing handler accumulation; fix by
ensuring a single handler is attached—either move the binding out of the message
event so it's registered once, or remove previous handlers before attaching (use
.off('load') then .on('load')), or use .one('load') if a single invocation is
desired; update the code around the message handler where the binding occurs and
target the $('`#preview-stage-iframe`') load handler (and any related
block.unblock() logic) accordingly.
In `@assets/js/dashboard-statistics.js`:
- Around line 161-168: In onClose, guard against partial range selections by
checking that selectedDates[0] and selectedDates[1] exist before calling
moment(...) and setting redirect.searchParams; only set 'start_date' if
selectedDates[0] is present and only set 'end_date' if selectedDates[1] is
present (or, if desired, set 'end_date' to the same value as 'start_date' when
only one date is chosen). Update the logic around selectedDates and redirect to
avoid accessing selectedDates[1] when it's undefined.
- Line 13: The assignment to mrr_graph creates an implicit global; update the
declaration at the Vue initialization to declare the variable explicitly (e.g.,
use const or let) instead of assigning to mrr_graph bare. Locate the Vue
instantiation that starts with "mrr_graph = new Vue({" and change it to an
explicit declaration (for example "const mrr_graph = new Vue({" or "let
mrr_graph = new Vue({") matching whether the graph is later reassigned.
In `@assets/js/edit-placeholders.js`:
- Around line 215-219: The code uses setInterval to clear that.saveMessage
repeatedly; change it to a one-shot setTimeout so the flash message is cleared
once after 6000ms. Replace the setInterval call around that.saveMessage with
setTimeout, and to avoid overlapping timers store the timer id (e.g.,
this._saveMessageTimer or that._saveMessageTimer), clear any existing timeout
with clearTimeout before creating a new one, and then set that.saveMessage = ''
inside the timeout callback.
- Around line 188-221: The AJAX success handler for the $.post call leaves the
component stuck in saving state on network/error paths; update the $.post
invocation around the block handling that.saving to add proper failure/always
handlers (use .fail() and/or .always()) so that on error you set that.saving =
false, set an appropriate that.saveMessage (or use data.message fallback), and
clear/stop the transient saveMessage timeout; ensure you also do not rely solely
on success to reset that.changed or that.delete and keep the existing call to
that.pull_data() only on success.
In `@assets/js/tax-rates.js`:
- Around line 371-375: The code uses setInterval to clear the one-time UI save
message (that.saveMessage = '') which causes repeated timers to stack; replace
the setInterval call with setTimeout so the message is cleared once after 6000ms
(i.e., change the invocation around setInterval(...) to setTimeout(...)) and
remove any unnecessary interval-clearing logic so repeated saves don't create
accumulating timers (refer to the setInterval usage and the that.saveMessage
assignment in this block).
- Around line 283-297: The bug is that using _.filter on this.data produces an
array and destroys the keyed object shape; replace the filter logic with an
object-preserving operation (e.g., _.omit or _.omitBy) so that that.data remains
an object keyed by category. Specifically, remove the use of
_.filter/cleaned_list and instead set that.data = _.omit(this.data,
that.tax_category) (or build a new object by copying all keys except
that.tax_category), and then set that.tax_category = Object.keys(that.data)[0]
to pick the first key; ensure the fallback default object
(default:{name:'Default',rates:[]}) is used when the result is empty.
In `@assets/js/template-previewer.js`:
- Around line 77-80: The iOS-specific patch is looking up body on the iframe
element subtree instead of the iframe's document, so in isIOS() branch locate
the iframe element (e.g., const iframeEl = document.getElementById("iframe")),
obtain its document via iframeEl.contentDocument ||
iframeEl.contentWindow?.document, then get doc.body and apply the class
"wu-fix-safari-preview" and style adjustments to that body; ensure you
null-check iframeEl and the retrieved document before mutating to avoid runtime
errors.
In `@assets/js/thank-you.js`:
- Line 132: Replace the hard-coded fetch("/wp-cron.php?doing_wp_cron") (and the
other two identical occurrences) with a site-aware URL provided via a localized
JS variable (e.g., window.wpData.wpCronUrl or similar injected with
wp_localize_script in PHP), and use that variable in the fetch calls (e.g.,
fetch(window.wpData.wpCronUrl + "?doing_wp_cron")). Update all three occurrences
in thank-you.js so they read from the localized variable and include a
reasonable fallback if the variable is missing.
- Around line 3-7: The TransitionText function currently ignores its has_icon
parameter by hardcoding has_icon: false; update the returned object inside
TransitionText to initialize the property from the parameter (e.g., has_icon:
has_icon or has_icon: Boolean(has_icon)) so callers can set the initial
spinner/icon state; keep the default parameter (has_icon = false) and ensure any
subsequent code in TransitionText references this property rather than the
hardcoded false.
- Around line 68-86: The resend handler currently awaits fetch and
request.json() without error handling, so failures leave the UI stuck; wrap the
fetch/response parsing and subsequent logic in a try/catch in the function that
does the POST (referencing the fetch call and request.json() in
assets/js/thank-you.js) and on any caught error call transitional_text.text(...)
with a generic/localized error message (using wu_thank_you.i18n or a fallback)
and the error class (e.g., "wu-text-red-600") then .done(); also ensure you
handle non-JSON or network errors and log the error to console for debugging
while keeping the user-facing message clear.
In `@assets/js/webhook-page.js`:
- Around line 80-84: The error handler for the AJAX webhook test (the
error(jqXHR) block that currently only calls wu_ajax_error(jqXHR)) fails to
clear the per-row loading indicator; update this error branch to also perform
the same loading-cleanup used on success (e.g., call the function that clears
the row loader or remove the loading class/spinner from the row element) so the
spinner is hidden after failures — ensure this cleanup runs before or after
calling wu_ajax_error(jqXHR) inside the error(jqXHR) handler.
In `@assets/js/wubox.js`:
- Line 508: The line setting the cursor style ("target.style.cursor = '';") is
space-indented but the project enforces tab indentation for .js files; replace
the leading spaces on that statement with a single tab so the line matches the
file's tab-based indentation and adheres to the WordPress ESLint config used
across this codebase.
- Around line 225-227: The code assumes event.submitter exists when creating
submitButton and appending it to formData; update the submit handler to guard
against a missing event.submitter by checking if event.submitter is truthy and
otherwise locating the submit control on the form (e.g.,
form.querySelector('button[type="submit"], input[type="submit"]') or a sensible
default), then use that control's value (or skip appending if none found) before
calling formData.append("submit", ...); refer to the submitButton variable,
event.submitter, and formData in your changes so the logic replaces the direct
event.submitter.value access with a safe fallback path.
- Around line 192-195: Guard against a missing inline source or empty child
before moving DOM nodes: in the block that uses params.inlineId, check that
document.getElementById(params.inlineId) returned a non-null element and that
element.children[0] (or element.firstElementChild) exists before calling
ajaxContent.insertAdjacentElement(...); likewise update the unloadAction to only
call element.insertAdjacentElement("afterbegin", ajaxContent.children[0]) when
both element and ajaxContent.children[0] exist (or bail/skip the move if either
is missing) so insertAdjacentElement never receives undefined.
---
Nitpick comments:
In `@assets/js/url-preview.js`:
- Around line 20-30: There are two identical keyup handlers bound to
$('.login').on('keyup', '`#field-site_url`', ...) causing double execution; remove
the duplicate block (or consolidate into a single handler) so only one listener
updates $('`#wu-your-site`').text($(this).val()); alternatively, if you must
re-bind, call $('.login').off('keyup', '`#field-site_url`') before .on to prevent
duplicates and ensure indentation uses tabs and jQuery globals per ESLint
guidelines.
In `@assets/js/vue-apps.js`:
- Around line 229-235: The wubox:unload handler assumes
document.getElementById("WUB_window") and modal.querySelector("ul[data-wu-app]")
always exist and will throw if absent; update the event listener to null-guard
the modal and app (and the app.dataset.wuApp) before constructing app_name and
deleting window properties (only call delete when the referenced elements/values
are present) so cleanup never throws; target the listener function that
references modal, app, app_name and the window[...] deletes when making this
change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 82f457a7-50f5-44b5-b2ba-3d23e9664fc0
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (50)
assets/js/activity-stream.jsassets/js/addons.jsassets/js/admin-notices.jsassets/js/admin-screen.jsassets/js/admin.jsassets/js/ajax-button.jsassets/js/app.jsassets/js/checkout-form-editor-modal.jsassets/js/checkout-forms-editor.jsassets/js/checkout.jsassets/js/color-field.jsassets/js/command-palette.jsassets/js/cookie-helpers.jsassets/js/coupon-code.jsassets/js/customizer.jsassets/js/dashboard-statistics.jsassets/js/dns-management.jsassets/js/dns-table.jsassets/js/domain-logs.jsassets/js/edit-placeholders.jsassets/js/email-edit-page.jsassets/js/fields.jsassets/js/functions.jsassets/js/gutenberg-support.jsassets/js/integration-test.jsassets/js/legacy-signup.jsassets/js/list-tables.jsassets/js/network-activate.jsassets/js/payment-status-poll.jsassets/js/paypal-setup-wizard.jsassets/js/pricing-table.jsassets/js/screenshot-scraper.jsassets/js/setup-wizard-extra.jsassets/js/site-maintenance.jsassets/js/tax-rates.jsassets/js/template-library.jsassets/js/template-previewer.jsassets/js/template-switching.jsassets/js/thank-you.jsassets/js/toolbox.jsassets/js/tours.jsassets/js/url-preview.jsassets/js/view-logs.jsassets/js/visits-counter.jsassets/js/vue-apps.jsassets/js/webhook-page.jsassets/js/wu-password-reset.jsassets/js/wu-password-toggle.jsassets/js/wubox.jsinc/checkout/class-checkout.php
| let $elem = `<a id="wu-admin-screen-customize" href="${ wu_admin_screen.customize_link }" class="button show-settings">${ wu_admin_screen.i18n.customize_label }</button>`; | ||
|
|
||
| const $page_elem = `<a title="${ wu_admin_screen.i18n.page_customize_label }" id="wu-admin-screen-page-customize" href="${ wu_admin_screen.page_customize_link }" class="wubox button show-settings">${ wu_admin_screen.i18n.page_customize_label }</button>`; | ||
| const $page_elem = `<a title="${ wu_admin_screen.i18n.page_customize_label }" id="wu-admin-screen-page-customize" href="${ wu_admin_screen.page_customize_link }" class="wubox button show-settings">${ wu_admin_screen.i18n.page_customize_label }</button>`; |
There was a problem hiding this comment.
Fix mismatched closing tags in generated anchor HTML.
The template strings create <a ...> elements but close them with </button>, which can cause inconsistent DOM parsing when appended.
Proposed fix
- let $elem = `<a id="wu-admin-screen-customize" href="${ wu_admin_screen.customize_link }" class="button show-settings">${ wu_admin_screen.i18n.customize_label }</button>`;
+ let $elem = `<a id="wu-admin-screen-customize" href="${ wu_admin_screen.customize_link }" class="button show-settings">${ wu_admin_screen.i18n.customize_label }</a>`;
- const $page_elem = `<a title="${ wu_admin_screen.i18n.page_customize_label }" id="wu-admin-screen-page-customize" href="${ wu_admin_screen.page_customize_link }" class="wubox button show-settings">${ wu_admin_screen.i18n.page_customize_label }</button>`;
+ const $page_elem = `<a title="${ wu_admin_screen.i18n.page_customize_label }" id="wu-admin-screen-page-customize" href="${ wu_admin_screen.page_customize_link }" class="wubox button show-settings">${ wu_admin_screen.i18n.page_customize_label }</a>`;
- $elem = `<a id="wu-admin-screen-customize" href="${ wu_admin_screen.close_link }" class="button show-settings wu-font-medium"><span class="wu-text-sm wu-align-text-bottom wu-text-red-500 wu-mr-2 wu--ml-1 dashicons-wu-circle-with-cross"></span>${ wu_admin_screen.i18n.close_label }</button>`;
+ $elem = `<a id="wu-admin-screen-customize" href="${ wu_admin_screen.close_link }" class="button show-settings wu-font-medium"><span class="wu-text-sm wu-align-text-bottom wu-text-red-500 wu-mr-2 wu--ml-1 dashicons-wu-circle-with-cross"></span>${ wu_admin_screen.i18n.close_label }</a>`;Also applies to: 21-21
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@assets/js/admin-screen.js` around lines 15 - 17, The template strings for
$elem and $page_elem in assets/js/admin-screen.js open anchor tags (<a>) but
incorrectly close them with </button>; update the closing tags to </a> for both
variables (references: $elem and $page_elem) so the generated HTML uses matching
<a>...</a> tags and avoid DOM parsing issues when appending.
| const element = document.getElementById(element_id); | ||
|
|
||
| element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); | ||
| element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); | ||
|
|
There was a problem hiding this comment.
Guard scrollIntoView against missing elements.
document.getElementById(element_id) can return null; current code will throw before finishing the UI flow.
Suggested fix
- const element = document.getElementById(element_id);
- element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
+ const element = document.getElementById(element_id);
+ if (element) {
+ element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const element = document.getElementById(element_id); | |
| element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); | |
| element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); | |
| const element = document.getElementById(element_id); | |
| if (element) { | |
| element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@assets/js/checkout-forms-editor.js` around lines 206 - 209, Guard the call to
scrollIntoView by checking the result of document.getElementById(element_id)
before using it: after obtaining const element =
document.getElementById(element_id) (in the code that calls
element.scrollIntoView), add a null-check (if (!element) { /* handle or return
*/ }) and only call element.scrollIntoView({ behavior: 'smooth', block:
'center', inline: 'nearest' }) when element is non-null; optionally log or
handle the missing element case for debugging.
| // Disable all submit buttons to prevent double-clicks | ||
| jQuery(this).find('button[type="submit"]').prop('disabled', true); | ||
|
|
||
| // Show loading message with status text | ||
| const loadingMessage = '<div style="text-align: center;">' + | ||
| '<div class="spinner is-active wu-float-none" style="float: none !important; margin-bottom: 12px;"></div>' + | ||
| '<div style="font-size: 14px; line-height: 1.5;">' + | ||
| (wu_checkout.i18n.provisioning_site || 'Provisioning your site — this can take up to 60 seconds.') + | ||
| '</div>' + | ||
| '</div>'; | ||
|
|
||
| that.block(loadingMessage); | ||
|
|
There was a problem hiding this comment.
Re-enable submit controls on failed submit paths.
At Line 1631, submits are disabled, but failure paths only call unblock() and never restore submit buttons. After a validation/API error, users can get stuck unable to retry.
Proposed fix
unblock() {
-
- jQuery(this.$el).wu_unblock();
+ jQuery(this.$el).wu_unblock();
+ jQuery(this.$el)
+ .find('button[type="submit"], input[type="submit"]')
+ .prop('disabled', false);
},Also applies to: 1659-1661
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@assets/js/checkout.js` around lines 1630 - 1642, Submit buttons are disabled
via jQuery(this).find('button[type="submit"]').prop('disabled', true') before
calling that.block(loadingMessage) but are never re-enabled on validation/API
failure paths; update all failure/exit branches that currently call unblock()
(including the paths around the other unblock() calls at the 1659-1661 region)
to also re-enable the buttons by calling
jQuery(this).find('button[type="submit"]').prop('disabled', false') (or capture
the form/buttons before disabling and call a single helper like
enableSubmitButtons() in every callback), ensuring every path that calls
that.block(...) or unblock() will restore the submit controls.
| if (applies_to_setup_fee) { | ||
|
|
||
| } | ||
|
|
||
| if(applies_to_setup_fee) { | ||
| if (setup_fee_discount_type != '"absolute"') { | ||
| setupfee_off_with_symbol = ''.concat(setup_fee_discount_value, '%'); | ||
| } else { | ||
| setupfee_off_with_symbol = accounting.formatMoney(parseFloat(setup_fee_discount_value)); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify undeclared assignment and declaration coverage for setupfee_off_with_symbol.
rg -n -C2 '\bsetupfee_off_with_symbol\b' assets/js/coupon-code.js
rg -n -C2 '\b(let|const|var)\s+setupfee_off_with_symbol\b' assets/js/coupon-code.jsRepository: Ultimate-Multisite/ultimate-multisite
Length of output: 628
🏁 Script executed:
# Show the function containing lines 169-175 to verify no outer scope declaration
sed -n '1,200p' assets/js/coupon-code.js | head -200 | tail -+140Repository: Ultimate-Multisite/ultimate-multisite
Length of output: 149
🏁 Script executed:
# Show broader context around lines 169-175
sed -n '140,200p' assets/js/coupon-code.jsRepository: Ultimate-Multisite/ultimate-multisite
Length of output: 1998
Declare setupfee_off_with_symbol locally to avoid implicit global writes.
setupfee_off_with_symbol is assigned at lines 172 and 174 without let/const/var, creating an implicit global variable. Add a local declaration within the if (applies_to_setup_fee) block to match the pattern used for off_with_symbol (line 160).
Suggested fix
if (applies_to_setup_fee) {
+ let setupfee_off_with_symbol = '';
if (setup_fee_discount_type != '"absolute"') {
setupfee_off_with_symbol = ''.concat(setup_fee_discount_value, '%');
} else {
setupfee_off_with_symbol = accounting.formatMoney(parseFloat(setup_fee_discount_value));
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (applies_to_setup_fee) { | |
| } | |
| if(applies_to_setup_fee) { | |
| if (setup_fee_discount_type != '"absolute"') { | |
| setupfee_off_with_symbol = ''.concat(setup_fee_discount_value, '%'); | |
| } else { | |
| setupfee_off_with_symbol = accounting.formatMoney(parseFloat(setup_fee_discount_value)); | |
| } | |
| if (applies_to_setup_fee) { | |
| let setupfee_off_with_symbol = ''; | |
| if (setup_fee_discount_type != '"absolute"') { | |
| setupfee_off_with_symbol = ''.concat(setup_fee_discount_value, '%'); | |
| } else { | |
| setupfee_off_with_symbol = accounting.formatMoney(parseFloat(setup_fee_discount_value)); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@assets/js/coupon-code.js` around lines 169 - 175, Declare
setupfee_off_with_symbol as a local variable inside the applies_to_setup_fee
block to avoid creating an implicit global; mirror the pattern used for
off_with_symbol by adding a local let/const declaration before assigning it, and
keep the existing conditional logic that checks setup_fee_discount_type and uses
setup_fee_discount_value and accounting.formatMoney to produce the formatted
value.
| $('#preview-stage-iframe').on('load', function() { | ||
|
|
||
| if (block) { | ||
| if (block) { | ||
|
|
||
| block.unblock(); | ||
| block.unblock(); | ||
|
|
||
| } // end if; | ||
| } // end if; | ||
|
|
||
| }); | ||
| }); |
There was a problem hiding this comment.
Prevent repeated load handler accumulation on iframe.
This listener is registered on every message event, so handlers stack up over time.
Suggested fix
- $('`#preview-stage-iframe`').on('load', function() {
+ $('`#preview-stage-iframe`').off('load').one('load', function() {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| $('#preview-stage-iframe').on('load', function() { | |
| if (block) { | |
| if (block) { | |
| block.unblock(); | |
| block.unblock(); | |
| } // end if; | |
| } // end if; | |
| }); | |
| }); | |
| $('`#preview-stage-iframe`').off('load').one('load', function() { | |
| if (block) { | |
| block.unblock(); | |
| } // end if; | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@assets/js/customizer.js` around lines 20 - 28, The iframe load handler bound
to $('`#preview-stage-iframe`').on('load', ...) is being added repeatedly (it's
registered inside a message event), causing handler accumulation; fix by
ensuring a single handler is attached—either move the binding out of the message
event so it's registered once, or remove previous handlers before attaching (use
.off('load') then .on('load')), or use .one('load') if a single invocation is
desired; update the code around the message handler where the binding occurs and
target the $('`#preview-stage-iframe`') load handler (and any related
block.unblock() logic) accordingly.
| } | ||
| })); | ||
| }); | ||
| fetch("/wp-cron.php?doing_wp_cron"); |
There was a problem hiding this comment.
Avoid hard-coded /wp-cron.php URLs.
Absolute root paths can break on WordPress subdirectory installs; this should come from a localized/site-aware URL.
Suggested direction
- fetch("/wp-cron.php?doing_wp_cron");
+ fetch(wu_thank_you.wp_cron_url);And use the same replacement for Lines 172 and 175.
Also applies to: 172-172, 175-175
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@assets/js/thank-you.js` at line 132, Replace the hard-coded
fetch("/wp-cron.php?doing_wp_cron") (and the other two identical occurrences)
with a site-aware URL provided via a localized JS variable (e.g.,
window.wpData.wpCronUrl or similar injected with wp_localize_script in PHP), and
use that variable in the fetch calls (e.g., fetch(window.wpData.wpCronUrl +
"?doing_wp_cron")). Update all three occurrences in thank-you.js so they read
from the localized variable and include a reasonable fallback if the variable is
missing.
| error(jqXHR) { | ||
|
|
||
| wu_ajax_error(jqXHR); | ||
| wu_ajax_error(jqXHR); | ||
|
|
||
| }, | ||
| }); | ||
| }, |
There was a problem hiding this comment.
Clear loading state on webhook test failure.
On Line 80, the error path reports via wu_ajax_error(jqXHR) but never hides the per-row loading indicator enabled before the request, so the spinner can remain stuck after failures.
Suggested fix
jQuery(document).on('click', '`#action_button`', function (event) {
@@
- $.ajax({
+ const webhook_id = $(this).data('object');
+
+ $.ajax({
method: 'post',
url: ajaxurl,
data: {
@@
- webhook_id: $(this).data('object'),
+ webhook_id,
@@
error(jqXHR) {
+ $('[data-loading="wu_action_button_loading_' + webhook_id + '"]').addClass('hidden');
wu_ajax_error(jqXHR);
},
});🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@assets/js/webhook-page.js` around lines 80 - 84, The error handler for the
AJAX webhook test (the error(jqXHR) block that currently only calls
wu_ajax_error(jqXHR)) fails to clear the per-row loading indicator; update this
error branch to also perform the same loading-cleanup used on success (e.g.,
call the function that clears the row loader or remove the loading class/spinner
from the row element) so the spinner is hidden after failures — ensure this
cleanup runs before or after calling wu_ajax_error(jqXHR) inside the
error(jqXHR) handler.
| const element = document.getElementById(params.inlineId); | ||
| ajaxContent.insertAdjacentElement("beforeend", element == null ? void 0 : element.children[ 0 ]); | ||
| const unloadAction = () => { | ||
| element == null ? void 0 : element.insertAdjacentElement("afterbegin", ajaxContent.children[ 0 ]); |
There was a problem hiding this comment.
Guard missing inline source element before DOM move.
Line 193 can throw when params.inlineId is invalid or the source has no child node, because insertAdjacentElement receives undefined. That crashes the box open flow.
Proposed fix
const createInlineBox = (boxWindow, boxOverlay, loaded, caption, params) => {
const ajaxContent = baseAjaxElement(boxWindow, boxOverlay, caption, params);
const element = document.getElementById(params.inlineId);
- ajaxContent.insertAdjacentElement("beforeend", element == null ? void 0 : element.children[ 0 ]);
+ const inlineChild = element && element.children.length ? element.children[0] : null;
+ if (! ajaxContent || ! inlineChild) {
+ loaded();
+ return;
+ }
+ ajaxContent.insertAdjacentElement("beforeend", inlineChild);
const unloadAction = () => {
element == null ? void 0 : element.insertAdjacentElement("afterbegin", ajaxContent.children[ 0 ]);
document.body.removeEventListener("wubox:unload", unloadAction);
};🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@assets/js/wubox.js` around lines 192 - 195, Guard against a missing inline
source or empty child before moving DOM nodes: in the block that uses
params.inlineId, check that document.getElementById(params.inlineId) returned a
non-null element and that element.children[0] (or element.firstElementChild)
exists before calling ajaxContent.insertAdjacentElement(...); likewise update
the unloadAction to only call element.insertAdjacentElement("afterbegin",
ajaxContent.children[0]) when both element and ajaxContent.children[0] exist (or
bail/skip the move if either is missing) so insertAdjacentElement never receives
undefined.
| const submitButton = event.submitter.value; | ||
| const formData = new FormData(form); | ||
| formData.append("submit", submitButton); |
There was a problem hiding this comment.
Handle submit events without event.submitter.
Line 225 assumes event.submitter is always present. If it is null, event.submitter.value throws and aborts submission/error handling.
Proposed fix
- const submitButton = event.submitter.value;
+ const submitButton = event.submitter ? event.submitter.value : "";
const formData = new FormData(form);
- formData.append("submit", submitButton);
+ if (submitButton) {
+ formData.append("submit", submitButton);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const submitButton = event.submitter.value; | |
| const formData = new FormData(form); | |
| formData.append("submit", submitButton); | |
| const submitButton = event.submitter ? event.submitter.value : ""; | |
| const formData = new FormData(form); | |
| if (submitButton) { | |
| formData.append("submit", submitButton); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@assets/js/wubox.js` around lines 225 - 227, The code assumes event.submitter
exists when creating submitButton and appending it to formData; update the
submit handler to guard against a missing event.submitter by checking if
event.submitter is truthy and otherwise locating the submit control on the form
(e.g., form.querySelector('button[type="submit"], input[type="submit"]') or a
sensible default), then use that control's value (or skip appending if none
found) before calling formData.append("submit", ...); refer to the submitButton
variable, event.submitter, and formData in your changes so the logic replaces
the direct event.submitter.value access with a safe fallback path.
SummaryAdds client-side UX improvements to prevent double-clicks and provide user feedback during the ~30s checkout provisioning wait. Changes
Verification
aidevops.sh v3.15.63 plugin for OpenCode v1.15.5 with claude-haiku-4-5 spent 2m and 4,877 tokens on this as a headless worker. Merged via PR #1222 to main. |
🔨 Build Complete - Ready for Testing!📦 Download Build Artifact (Recommended)Download the zip build, upload to WordPress and test:
🌐 Test in WordPress Playground (Very Experimental)Click the link below to instantly test this PR in your browser - no installation needed! Login credentials: |
Summary
Adds client-side UX improvements to prevent double-clicks and provide user feedback during the ~30s checkout provisioning wait.
Changes
provisioning_sitei18n string to checkout class for the status message.block()method to accept an optional message parameter for custom loading messages.Verification
wu_cap_*request fires twice from a single click sequence.Resolves #1220
aidevops.sh v3.15.63 plugin for OpenCode v1.15.5 with claude-haiku-4-5 spent 2m and 4,877 tokens on this as a headless worker.
Summary by CodeRabbit
New Features
Style & Refactoring