feat(common): B2B-4938 update account settings via native BC Storefro…#960
feat(common): B2B-4938 update account settings via native BC Storefro…#960bc-arezoo wants to merge 13 commits into
Conversation
…nt mutations Behind the PROJECT-7920.use_bc_account_settings flag, BC customers now save through customer.updateCustomer and B2B company users through company.updateCompanyUser, replacing the legacy name-based middleware mutations. - Build entityId-keyed formFields, resolving fieldEntityId from the field_<id> config fieldId and choice option ids from site.settings.formFields.customer. - Send only changed custom fields (normalized comparison) and allow clearing empty text/date fields; wire reCaptcha via a shared useStorefrontCaptcha hook (also adopted by ForgotPassword). - Fall back to the legacy path when the flag is off. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ests - Stop exporting CHOICE_FIELD_TYPES; it's only used internally by fieldTypeNeedsOptions. - Scope the native form-field tests to their own render container via within(result.container) so a leaked prior render can't cause a "found multiple elements" match under full-suite runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ings tests useStorefrontCaptcha issues an anonymous reCaptcha-config query that MSW can't intercept; the resulting console.warn tripped fail-on-console and the failing tests leaked their DOM (causing "found multiple elements" in later tests). Mock getStorefrontToken so no request is made and reCaptcha stays disabled. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Company users never loaded site.settings.formFields.customer, so buildUpdateCompanyUserInput ran without definitions and every choice/checkbox edit hit the empty-definitions guard or couldn't map option labels to entity ids. Fetch the definitions for both user types (in parallel with the account read) and pass them into buildUpdateCompanyUserInput, matching the BC path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The native path called the update mutation and reported success even when buildFormFieldsInput dropped every changed field (unmapped choice option, cleared number/checkbox, or missing entityId), so the user saw "saved" while the edit was silently lost. buildFormFieldsInput now also reports the unsendable fields; the submit handler surfaces an error and aborts if any changed field can't be represented, replacing the narrower empty-definitions choice guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d checkbox handling - dispatchUpdate: catch rejections from updateCustomer/updateCompanyUser so a network/proxy failure surfaces an error snackbar instead of throwing past the success flow. - Block the BC submit until the reCaptcha config has loaded (useStorefrontCaptcha now exposes isCaptchaConfigLoading) so a customer can't submit without a token when the storefront requires one. - Mark a checkbox unsendable when any selected label fails to resolve to an option entityId, so getUnsendableFormFields fails loudly instead of sending a partial selection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| } | ||
|
|
||
| export interface CustomerFormFieldDefinition { | ||
| __typename: string; |
There was a problem hiding this comment.
[Question]
is __typename is required ?
…no-op logout buildUpdateCustomerInput never emitted the password, so a BC customer's password change was dropped while the flow still logged them out — appearing to succeed without changing the password. customer.updateCustomer takes the password as a form field, so emit it under the Password field's entityId (from the definitions), and bail with an error before the logout-on-password-change when that entityId can't be resolved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| name: string; | ||
| value: any; | ||
| fieldType?: string; | ||
| fieldEntityId?: number; |
There was a problem hiding this comment.
yes, the flow is: collectChangedFormFields parses it from the config fieldId → carries it on the BcFormFieldsProps entry → buildFormFieldsInput consumes it to build customer.updateCustomer / company.updateCompanyUser input.
…ce/date support
- Fetch customer form-field definitions with the correct BC schema types
(Picklist/RadioButtons/Checkboxes options) so choice/checkbox option ids
resolve; fetch whenever any custom field exists (also feeds the entityId
label-fallback for non-`field_<id>` fields).
- Don't let a cleared/unresolvable field abort the whole save: blank
number/date/dropdown are skipped; representable clears (text '', checkbox [])
are sent; only genuinely unresolvable non-empty fields fail loudly.
- Compare dates on the date-only portion so an untouched date isn't re-sent, and
emit dates as UTC-midnight ISO without local-timezone shift.
- Route BC password changes through customer.changePassword (password-first,
captcha gates checked up front, force re-login if the profile update then
fails); add company.updateCompanyUser name-keyed extraFields.
- Stop leaking fieldType into the legacy mutation; drop the dead passwords group;
name the contact-group id (CONTACT_GROUP_ID).
- Tests: buildFormFieldsInput {formFields, unsendable}, clear/skip behavior,
date change-detection, and a BC changePassword integration test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…field definitions Nothing reads __typename on CustomerFormFieldDefinition (the password lookup that used it was replaced by customer.changePassword), so remove it from the CustomerFormFieldSettings query and the type. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…word ordering - Key custom form fields by bcLabel || label. getAccountFormFields leaves bcLabel empty when the config supplies only labelName (kept on `label`), so matching on bcLabel alone broke change detection, clears, and the definition entityId fallback; the same fallback is applied to the init default population. - BC dispatch: check the reCaptcha gates before the password change and force a re-login if the profile update fails after the password already changed, so a gate/profile failure can't leave a committed password with an out-of-sync session. Password stays first to fail fast on a wrong current password. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reorder the BC account-settings save so profile details are updated first and the password only changes if that succeeds; if the details update fails we return early and never commit the password. Also route company-user extra fields through a reusable buildExtraFieldsInput that flags array-valued (checkbox) fields as unsendable so the save fails loudly instead of dropping. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dable
The BC formFields input has no empty representation for number/date/choice
fields, so a cleared one was silently dropped by buildFormFieldsInput. When
the clear was the only edit, the save reported a false success ("no edits")
without saving. Flag these clears as unsendable so the existing guard in
AccountSetting surfaces an error instead, matching the module's fail-loudly
handling of unmapped choices, missing entityIds, and partial checkboxes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@bc-vasu As discussed yesterday, can you please confirm the |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 3b5015c. Configure here.
| } | ||
| } | ||
| } | ||
| }`; |
There was a problem hiding this comment.
updateCustomer ignores mutation errors
Medium Severity
The native BC customer profile save only treats top-level GraphQL errors as failure. The UpdateCustomer mutation does not request or check updateCustomer result errors, unlike changePassword and updateCompanyUser in the same change. Validation or business failures returned in the mutation payload can be treated as success.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 3b5015c. Configure here.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>


Jira: B2B-4938
What/Why?
Migrates account-settings updates from the legacy name-based b2b middleware mutations to the native BC Storefront GraphQL mutations, behind the PROJECT-7920.use_bc_account_settings flag:
Rollout/Rollback
Gated by the PROJECT-7920.use_bc_account_settings LaunchDarkly e flag; roll back by disabling it (falls straight back to thelegacy middleware path). Code-only change, no data migrations.
Testing
Known limitation: choice/checkbox custom fields require site.settings.formFields.customer, which currently returns empty through the b2b proxy; such edits surface an error rather than saving silently.
Note
Medium Risk
Touches customer profile, password, and B2B user updates with new payload shapes and reCaptcha gating; mitigated by feature flag rollback and loud failures for unmappable fields.
Overview
With
PROJECT-7920.use_bc_account_settingson, account saves go through native Storefront GraphQL instead of legacy B2B middleware: BC customers usecustomer.updateCustomer(optional reCaptcha) pluscustomer.changePassword; B2B company users usecompany.updateCompanyUser.Custom fields are sent only when changed, mapped into entityId-keyed typed
formFields(and B2B contact-group name-keyedextraFields).site.settings.formFields.customersupplies entityIds and choice options; edits that cannot be encoded are rejected rather than dropped silently.A shared
useStorefrontCaptchahook loads reCaptcha config for BC profile updates and Forgot Password. Flag off keeps the existing middleware update path.Reviewed by Cursor Bugbot for commit 623b2c6. Bugbot is set up for automated code reviews on this repo. Configure here.