Skip to content

Fix live updating issue of single product#3083

Open
truongwp wants to merge 2 commits intomasterfrom
fix-single-product-live-update
Open

Fix live updating issue of single product#3083
truongwp wants to merge 2 commits intomasterfrom
fix-single-product-live-update

Conversation

@truongwp
Copy link
Copy Markdown
Contributor

@truongwp truongwp commented Apr 20, 2026

Fixes https://github.com/Strategy11/formidable-pro/issues/6416

Before: See the issue.

After:

Screen.Recording.2026-04-21.at.00.36.33.mov

Summary by CodeRabbit

  • New Features

    • Product prices in the form builder now display with proper currency formatting based on your site settings.
  • Improvements

    • Single product fields now accurately display pricing and label updates in the builder preview.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

Warning

Rate limit exceeded

@truongwp has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 45 minutes and 45 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 45 minutes and 45 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 81a7767b-97b2-43fe-aa1a-c6c4403fb8dd

📥 Commits

Reviewing files that changed from the base of the PR and between 28310ed and c68304a.

📒 Files selected for processing (1)
  • js/src/admin/admin.js
📝 Walkthrough

Walkthrough

Added conditional currency data localization in PHP and implemented single product field handling in the JavaScript form builder, including price formatting utilities and label display logic based on product options.

Changes

Cohort / File(s) Summary
Backend Localization
classes/helpers/FrmAppHelper.php
Conditionally includes currency entry in localized admin script strings when on the form builder page, enabling JavaScript access to currency formatting settings.
Form Builder Product Handling
js/src/admin/admin.js
Added single product field detection and handling with new helper functions: formatProductPrice() (formats prices using localized currency settings), isSingleProductField() (detects single product field type), and updateSingleProductLabel() (renders product label with formatted price). Implements early-return control flow to short-circuit standard option rendering for single product fields.

Sequence Diagram

sequenceDiagram
    participant Admin as Admin Page
    participant Builder as Form Builder JS
    participant LocalScript as Localized Currency Data
    participant UI as Builder UI

    Admin->>LocalScript: Load form builder page
    LocalScript->>Builder: Provide frm_admin_js.currency settings
    
    Builder->>Builder: User updates product field
    Builder->>Builder: Check if field is single product type
    
    alt Single Product Field
        Builder->>Builder: Get product option label
        Builder->>Builder: formatProductPrice() with currency data
        Builder->>UI: updateSingleProductLabel(label + price)
        UI->>UI: Display formatted product with price
    else Standard Field
        Builder->>UI: Render normal option updates
    end
    
    Builder->>Builder: adjustConditionalLogicOptionOrders()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A form builder bloom, with prices aligned,
Single products now shine with currency refined,
Helper functions hop through the code so bright,
Formatting and labels dance in perfect sight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main change: fixing live updating for single product fields in the form builder.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-single-product-live-update

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@deepsource-io
Copy link
Copy Markdown

deepsource-io Bot commented Apr 20, 2026

DeepSource Code Review

We reviewed changes in 59c95e2...c68304a on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
PHP Apr 20, 2026 5:51p.m. Review ↗
JavaScript Apr 20, 2026 5:51p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@js/src/admin/admin.js`:
- Around line 6219-6220: The preview concatenation can produce "undefined"
because currency.symbol_padding may be missing; update the places building
leftSymbol and rightSymbol (the expressions referencing currency.symbol_left,
currency.symbol_right and currency.symbol_padding) to default symbol_padding to
an empty string (e.g., use currency.symbol_padding ?? '' or equivalent) before
concatenation so missing padding won't render "undefined".
- Around line 6244-6271: In updateSingleProductLabel, after you read price from
optWrapper, also locate the hidden product input inside optWrapper (e.g., const
hiddenInput = optWrapper.querySelector('input[type="hidden"][data-frmprice]') )
and update its stored price so live calculations use the new value; set
hiddenInput.dataset.frmprice = price and hiddenInput.value = price (guarding for
hiddenInput existence) before updating labelEl.innerHTML so both the visible
label and the hidden price attribute stay in sync.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 36a7eb9a-5e98-45bb-af86-1fdab09b5fa2

📥 Commits

Reviewing files that changed from the base of the PR and between 59c95e2 and 28310ed.

📒 Files selected for processing (3)
  • classes/helpers/FrmAppHelper.php
  • js/formidable_admin.js
  • js/src/admin/admin.js

Comment thread js/src/admin/admin.js Outdated
Comment on lines +6219 to +6220
const leftSymbol = currency.symbol_left ? ( currency.symbol_left + currency.symbol_padding ) : '';
const rightSymbol = currency.symbol_right ? ( currency.symbol_padding + currency.symbol_right ) : '';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Default symbol_padding before concatenating it.

If localized currency data is missing symbol_padding, the preview can render undefined next to the symbol.

Proposed fix
-		const leftSymbol  = currency.symbol_left ? ( currency.symbol_left + currency.symbol_padding ) : '';
-		const rightSymbol = currency.symbol_right ? ( currency.symbol_padding + currency.symbol_right ) : '';
+		const symbolPadding = currency.symbol_padding ?? '';
+		const leftSymbol  = currency.symbol_left ? ( currency.symbol_left + symbolPadding ) : '';
+		const rightSymbol = currency.symbol_right ? ( symbolPadding + currency.symbol_right ) : '';
📝 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.

Suggested change
const leftSymbol = currency.symbol_left ? ( currency.symbol_left + currency.symbol_padding ) : '';
const rightSymbol = currency.symbol_right ? ( currency.symbol_padding + currency.symbol_right ) : '';
const symbolPadding = currency.symbol_padding ?? '';
const leftSymbol = currency.symbol_left ? ( currency.symbol_left + symbolPadding ) : '';
const rightSymbol = currency.symbol_right ? ( symbolPadding + currency.symbol_right ) : '';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@js/src/admin/admin.js` around lines 6219 - 6220, The preview concatenation
can produce "undefined" because currency.symbol_padding may be missing; update
the places building leftSymbol and rightSymbol (the expressions referencing
currency.symbol_left, currency.symbol_right and currency.symbol_padding) to
default symbol_padding to an empty string (e.g., use currency.symbol_padding ??
'' or equivalent) before concatenation so missing padding won't render
"undefined".

Comment thread js/src/admin/admin.js
Comment on lines +6244 to +6271
function updateSingleProductLabel( fieldId ) {
const labelEl = document.querySelector( `#field_${ fieldId }_inner_container .frm_single_product_label` );
if ( ! labelEl ) {
return;
}

const firstRealOpt = document.querySelector( `#frm_field_${ fieldId }_opts .frm_single_option:not(.frm_option_template)` );
if ( ! firstRealOpt ) {
return;
}

const firstOptKey = firstRealOpt.dataset.optkey;
const optWrapper = document.getElementById( `frm_delete_field_${ fieldId }-${ firstOptKey }_container` );
if ( ! optWrapper ) {
return;
}

const label = optWrapper.querySelector( `.field_${ fieldId }_option` )?.value ?? '';
const price = optWrapper.querySelector( '.frm_product_price' )?.value ?? '';

const parts = [];
if ( label ) {
parts.push( label );
}
if ( price ) {
parts.push( formatProductPrice( price ) );
}
labelEl.innerHTML = purifyHtml( parts.join( ': ' ) );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Update the hidden product input along with the label.

The single-product template carries the selected product price on the hidden input as data-frmprice (classes/views/frm-fields/front-end/product-single.php:35). This path updates only the visible label, so live totals/calculations can keep reading a stale hidden input price after a builder price edit.

Proposed fix
 function updateSingleProductLabel( fieldId ) {
 	const labelEl = document.querySelector( `#field_${ fieldId }_inner_container .frm_single_product_label` );
 	if ( ! labelEl ) {
 		return;
 	}
 
-	const firstRealOpt = document.querySelector( `#frm_field_${ fieldId }_opts .frm_single_option:not(.frm_option_template)` );
-	if ( ! firstRealOpt ) {
-		return;
-	}
-
-	const firstOptKey = firstRealOpt.dataset.optkey;
-	const optWrapper  = document.getElementById( `frm_delete_field_${ fieldId }-${ firstOptKey }_container` );
-	if ( ! optWrapper ) {
+	const firstOpt = getMultipleOpts( fieldId )[ 0 ];
+	if ( ! firstOpt ) {
 		return;
 	}
 
-	const label = optWrapper.querySelector( `.field_${ fieldId }_option` )?.value ?? '';
-	const price = optWrapper.querySelector( '.frm_product_price' )?.value ?? '';
-
 	const parts = [];
-	if ( label ) {
-		parts.push( label );
+	if ( firstOpt.label ) {
+		parts.push( firstOpt.label );
 	}
-	if ( price ) {
-		parts.push( formatProductPrice( price ) );
+	if ( firstOpt.price ) {
+		parts.push( formatProductPrice( firstOpt.price ) );
 	}
 	labelEl.innerHTML = purifyHtml( parts.join( ': ' ) );
+
+	const metaInput = document.querySelector( `[name="item_meta[${ fieldId }]"]` );
+	if ( metaInput ) {
+		metaInput.value = firstOpt.saved;
+		metaInput.dataset.frmprice = firstOpt.price || '';
+	}
 }
📝 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.

Suggested change
function updateSingleProductLabel( fieldId ) {
const labelEl = document.querySelector( `#field_${ fieldId }_inner_container .frm_single_product_label` );
if ( ! labelEl ) {
return;
}
const firstRealOpt = document.querySelector( `#frm_field_${ fieldId }_opts .frm_single_option:not(.frm_option_template)` );
if ( ! firstRealOpt ) {
return;
}
const firstOptKey = firstRealOpt.dataset.optkey;
const optWrapper = document.getElementById( `frm_delete_field_${ fieldId }-${ firstOptKey }_container` );
if ( ! optWrapper ) {
return;
}
const label = optWrapper.querySelector( `.field_${ fieldId }_option` )?.value ?? '';
const price = optWrapper.querySelector( '.frm_product_price' )?.value ?? '';
const parts = [];
if ( label ) {
parts.push( label );
}
if ( price ) {
parts.push( formatProductPrice( price ) );
}
labelEl.innerHTML = purifyHtml( parts.join( ': ' ) );
function updateSingleProductLabel( fieldId ) {
const labelEl = document.querySelector( `#field_${ fieldId }_inner_container .frm_single_product_label` );
if ( ! labelEl ) {
return;
}
const firstOpt = getMultipleOpts( fieldId )[ 0 ];
if ( ! firstOpt ) {
return;
}
const parts = [];
if ( firstOpt.label ) {
parts.push( firstOpt.label );
}
if ( firstOpt.price ) {
parts.push( formatProductPrice( firstOpt.price ) );
}
labelEl.innerHTML = purifyHtml( parts.join( ': ' ) );
const metaInput = document.querySelector( `[name="item_meta[${ fieldId }]"]` );
if ( metaInput ) {
metaInput.value = firstOpt.saved;
metaInput.dataset.frmprice = firstOpt.price || '';
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@js/src/admin/admin.js` around lines 6244 - 6271, In updateSingleProductLabel,
after you read price from optWrapper, also locate the hidden product input
inside optWrapper (e.g., const hiddenInput =
optWrapper.querySelector('input[type="hidden"][data-frmprice]') ) and update its
stored price so live calculations use the new value; set
hiddenInput.dataset.frmprice = price and hiddenInput.value = price (guarding for
hiddenInput existence) before updating labelEl.innerHTML so both the visible
label and the hidden price attribute stay in sync.

@truongwp truongwp requested a review from Crabcyborg April 20, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant