feat(integrations): add B2BKing action integration#164
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new integration for B2BKing, including backend controllers, API helpers, and a comprehensive frontend UI for configuration and field mapping. Key feedback includes fixing potential PHP warnings by ensuring $utilities is an object and validating the return value of get_user_by. Additionally, it is recommended to declare refreshGroups as a static method to match its usage, validate user emails before processing actions, and improve the robustness of the frontend MultiSelect component when handling group data.
| $integrationDetails = $integrationData->flow_details; | ||
| $integId = $integrationData->id; | ||
| $fieldMap = $integrationDetails->field_map; | ||
| $utilities = isset($integrationDetails->utilities) ? $integrationDetails->utilities : []; |
There was a problem hiding this comment.
The $utilities variable is initialized as an empty array [] if it doesn't exist. However, in RecordApiHelper::execute, it is accessed as an object (e.g., $utilities->selected_group). This will cause a PHP notice/warning when the utilities are missing. It should be initialized as an empty object instead.
$utilities = $integrationDetails->utilities ?? (object) [];There was a problem hiding this comment.
Fixed in this PR — the fallback is now (object) [] instead of []:
$utilities = isset($integrationDetails->utilities) ? $integrationDetails->utilities : (object) [];| $user = get_user_by($field, $value); | ||
|
|
||
| if (empty($user->user_email)) { | ||
| return static::formattedUserTemp(); | ||
| } |
There was a problem hiding this comment.
The get_user_by function can return false if no user is found. Accessing ->user_email on a boolean value will trigger a warning in PHP 8.0+. You should verify that $user is a valid object before accessing its properties.
$user = get_user_by($field, $value);
if (!$user || empty($user->user_email)) {
return static::formattedUserTemp();
}There was a problem hiding this comment.
Fixed — added an instanceof WP_User guard (more strict than !, handles both false and unexpected return types):
$user = get_user_by($field, $value);
if (!$user instanceof WP_User) {
return static::formattedUserTemp();
}| wp_send_json_success(true); | ||
| } | ||
|
|
||
| public function refreshGroups() |
There was a problem hiding this comment.
Fixed — refreshGroups is now public static. Note: Routes.php uses [B2BKingController::class, 'refreshGroups'] as a callable array (not a direct static call), and Route::post uses ReflectionMethod::isStatic() to decide whether to instantiate — so the existing code worked either way. Making it static is still the correct convention since it doesn't use $this.
public static function refreshGroups()| 'message' => wp_sprintf(__('%s plugin is not installed or activated', 'bit-integrations'), 'Bit Integrations Pro'), | ||
| ]; | ||
|
|
||
| $email = $fieldData['user_email'] ?? ''; |
There was a problem hiding this comment.
It is recommended to validate that the user email is not empty before proceeding with actions like approving a customer or updating a group. If the email is missing, the integration should log an error or return a failure response early.
$email = $fieldData['user_email'] ?? '';
if (empty($email)) {
return [
'success' => false,
'message' => __('User email is required', 'bit-integrations'),
];
}There was a problem hiding this comment.
Fixed — added early return with logging before the switch:
$email = $fieldData['user_email'] ?? '';
if (empty($email)) {
$errorResponse = [
'success' => false,
'message' => __('User email is required', 'bit-integrations'),
];
LogHandler::save($this->_integrationID, ['type' => 'B2BKing', 'type_name' => $mainAction], 'error', $errorResponse);
return $errorResponse;
}| options={ | ||
| b2bKingConf?.allGroups && | ||
| Array.isArray(b2bKingConf.allGroups) && | ||
| b2bKingConf.allGroups.map(group => ({ | ||
| label: group.label, | ||
| value: String(group.value) | ||
| })) | ||
| } |
There was a problem hiding this comment.
The options prop for MultiSelect might receive undefined or false if allGroups is not yet loaded or is not an array. It is safer to default to an empty array to ensure the component renders correctly.
| options={ | |
| b2bKingConf?.allGroups && | |
| Array.isArray(b2bKingConf.allGroups) && | |
| b2bKingConf.allGroups.map(group => ({ | |
| label: group.label, | |
| value: String(group.value) | |
| })) | |
| } | |
| options={ | |
| (Array.isArray(b2bKingConf?.allGroups) ? b2bKingConf.allGroups : []).map(group => ({ | |
| label: group.label, | |
| value: String(group.value) | |
| })) | |
| } |
There was a problem hiding this comment.
Fixed — options now always resolves to a mapped array (empty if allGroups is not yet loaded):
options={(Array.isArray(b2bKingConf?.allGroups) ? b2bKingConf.allGroups : []).map(group => ({
label: group.label,
value: String(group.value)
}))}
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new B2BKing action integration to Bit Integrations, adding backend endpoints/execution plumbing and a frontend configuration + field-mapping flow so users can automate B2BKing customer approval, B2B enablement, and customer group updates.
Changes:
- Added B2BKing integration to frontend action selection, new/edit integration renderers, and integration info authorization rendering.
- Implemented new frontend B2BKing setup UI (authorization, action selection, group refresh/select, field mapping, edit flow).
- Added backend B2BKing action routes/controller + execution helper, plus a utility user lookup helper.
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/components/Flow/New/SelectAction.jsx | Registers B2BKing as an available action integration type in the action picker. |
| frontend/src/components/AllIntegrations/NewInteg.jsx | Lazy-loads and renders the new B2BKing integration setup flow. |
| frontend/src/components/AllIntegrations/IntegInfo.jsx | Adds B2BKing authorization/info view rendering in the Integration Info screen. |
| frontend/src/components/AllIntegrations/EditInteg.jsx | Hooks up edit-screen rendering for existing B2BKing integrations. |
| frontend/src/components/AllIntegrations/B2BKing/staticData.js | Defines supported B2BKing actions and required fields. |
| frontend/src/components/AllIntegrations/B2BKing/B2BKing.jsx | Implements the 3-step “new integration” UI flow for B2BKing. |
| frontend/src/components/AllIntegrations/B2BKing/B2BKingAuthorization.jsx | Implements “authorization” / plugin-existence check UI for B2BKing. |
| frontend/src/components/AllIntegrations/B2BKing/B2BKingIntegLayout.jsx | Implements action selection, group selection/refresh, and field map rendering. |
| frontend/src/components/AllIntegrations/B2BKing/B2BKingFieldMap.jsx | Implements individual field-mapping row UI including custom values and smart tags. |
| frontend/src/components/AllIntegrations/B2BKing/B2BKingCommonFunc.js | Shared UI helpers: input handling, group refresh, mapping validation, required-map generation. |
| frontend/src/components/AllIntegrations/B2BKing/EditB2BKing.jsx | Implements the edit-integration configuration screen for B2BKing. |
| backend/Actions/B2BKing/Routes.php | Registers AJAX routes for B2BKing authorize + group refresh. |
| backend/Actions/B2BKing/B2BKingController.php | Adds existence checks, group refresh endpoint, and action execution entrypoint. |
| backend/Actions/B2BKing/RecordApiHelper.php | Implements action execution + mapped payload generation + logging for B2BKing actions. |
| backend/Core/Util/User.php | Adds getUserByField() helper for user lookup. |
| backend/Core/Util/AllTriggersName.php | Adds a B2BKing entry to the “Pro triggers list” (currently appears mismatched). |
Comments suppressed due to low confidence (1)
frontend/src/components/AllIntegrations/B2BKing/B2BKingIntegLayout.jsx:25
setSnackbaris declared as a prop but never used in this component. With the repo’s ESLint config (includesno-unused-vars), this will be flagged. Remove the unused prop or use it for feedback messaging.
export default function B2BKingIntegLayout({
formFields,
b2bKingConf,
setB2BKingConf,
isLoading,
setIsLoading,
setSnackbar
}) {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $user = get_user_by($field, $value); | ||
|
|
||
| if (empty($user->user_email)) { |
There was a problem hiding this comment.
Fixed. Added !\ instanceof WP_User guard before accessing ->user_email, so a false return from get_user_by() returns the empty template instead of triggering a PHP 8 warning.
| 'ARMember' => ['name' => 'ARMember', 'isPro' => true, 'is_active' => false], | ||
| 'Asgaros' => ['name' => 'Asgaros Forum', 'isPro' => true, 'is_active' => false], | ||
| 'AvadaForms' => ['name' => 'Avada Forms', 'isPro' => true, 'is_active' => false], | ||
| 'B2BKing' => ['name' => 'B2BKing', 'isPro' => true, 'is_active' => false], | ||
| 'Beaver' => ['name' => 'Beaver', 'isPro' => true, 'is_active' => false], |
There was a problem hiding this comment.
The B2BKing entry is intentional — it serves as a placeholder for a planned B2BKing trigger in the Pro plugin. The isPro: true flag means it only surfaces as an upsell item for free users, not as an active trigger. It will be backed by a Pro trigger implementation in a future release.
| $email = $fieldData['user_email'] ?? ''; | ||
| switch ($mainAction) { | ||
| case 'update_customer_group': | ||
| $groupId = isset($utilities->selected_group) ? $utilities->selected_group : ''; |
There was a problem hiding this comment.
Fixed. Changed the fallback in B2BKingController::execute from [] to (object) [] so $utilities is always an object, preventing the PHP 8 fatal when $utilities->selected_group is accessed.
| $integrationDetails = $integrationData->flow_details; | ||
| $integId = $integrationData->id; | ||
| $fieldMap = $integrationDetails->field_map; | ||
| $utilities = isset($integrationDetails->utilities) ? $integrationDetails->utilities : []; |
There was a problem hiding this comment.
Fixed. Changed the fallback from [] to (object) [] in B2BKingController::execute:
$utilities = isset($integrationDetails->utilities) ? $integrationDetails->utilities : (object) [];This ensures $utilities is always an object, consistent with how RecordApiHelper accesses it.
| @@ -0,0 +1,171 @@ | |||
| import { create } from 'mutative' | |||
| import MultiSelect from 'react-multiple-select-dropdown-lite' | |||
There was a problem hiding this comment.
Fixed. Added the CSS import to B2BKingIntegLayout.jsx so the dropdown renders correctly in both new-flow and edit-flow contexts.
|
|
||
| export default function EditB2BKing({ allIntegURL }) { | ||
| const navigate = useNavigate() | ||
| const { id, formID } = useParams() |
There was a problem hiding this comment.
Fixed. Removed formID from the useParams() destructuring in EditB2BKing.jsx.
| {isLoading === 'auth' && ( | ||
| <div className="flx mt-5"> | ||
| <LoaderSm size={25} clr="#022217" className="mr-2" /> | ||
| {__('Checking if B2BKing is authorized!!!', 'bit-integrations')} |
There was a problem hiding this comment.
Fixed. Changed the loading message to 'Checking B2BKing connection…'.
| type="button"> | ||
| + | ||
| </button> | ||
| <button | ||
| onClick={() => delFieldMap(i, b2bKingConf, setB2BKingConf)} | ||
| className="icn-btn sh-sm ml-1" | ||
| type="button" | ||
| aria-label="btn"> |
There was a problem hiding this comment.
Fixed. Changed aria-label from "btn" to a descriptive __('Remove field mapping', 'bit-integrations').
| case 'AsgarosForum': | ||
| return <AsgarosForumAuthorization asgarosForumConf={integrationConf} step={1} isInfo /> | ||
| case 'B2BKing': | ||
| return <B2BKingAuthorization b2bKingConf={integrationConf} step={1} isInfo /> |
There was a problem hiding this comment.
Fixed. Added isInfo prop to B2BKingAuthorization. In info mode: the name input gets disabled={isInfo}, and all interactive elements (connect button, next button, loading/auth-result UI) are wrapped in {!isInfo && (...)}. This matches the pattern used in other Authorization components like NotificationXAuthorization.
| export default function B2BKingAuthorization({ | ||
| b2bKingConf, | ||
| setB2BKingConf, | ||
| step, | ||
| nextPage, | ||
| isLoading, | ||
| setIsLoading, | ||
| setSnackbar | ||
| }) { |
There was a problem hiding this comment.
Fixed. Added isInfo to the component's props and wrapped all interactive handlers (input onChange, buttons) inside {!isInfo && (...)} so they are not rendered in info mode. The name input also gets disabled={isInfo}.
…andling, and UI improvements
Description
This PR adds a new B2BKing action integration in Bit Integrations, including backend execution and frontend configuration flows. Users can now approve customers, enable B2B for users, and update customer groups through mapped form data.
Motivation & Context
This change enables automation workflows to manage B2BKing user state directly from Bit Integrations actions. It reduces manual account/group management work for stores using B2BKing.
Related Links: (if applicable)
Type of Change
Key Changes
Backend
B2BKingControllerauthorization, existence checks, action execution, and group refresh endpoint handling.RecordApiHelperexecution logic for:LogHandler.B2BKingaction availability.Frontend
Checklist
Changelog