Skip to content

feat(integrations): add B2BKing action integration#164

Open
RishadAlam wants to merge 2 commits into
mainfrom
feat/b2bking
Open

feat(integrations): add B2BKing action integration#164
RishadAlam wants to merge 2 commits into
mainfrom
feat/b2bking

Conversation

@RishadAlam
Copy link
Copy Markdown
Member

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

  • 🐛 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • 📚 Documentation update
  • ⚡ Improvement
  • 🔄 Code refactor

Key Changes

Backend

  • Added B2BKingController authorization, existence checks, action execution, and group refresh endpoint handling.
  • Added RecordApiHelper execution logic for:
    • approving customers
    • enabling B2B for users
    • updating customer groups
  • Added field-map based payload generation with support for custom values.
  • Added integration action logging through LogHandler.
  • Added B2BKing routes for authorization and group refresh.
  • Updated integration registry to include B2BKing action availability.

Frontend

  • Added full B2BKing integration UI set:
    • integration setup
    • authorization
    • field mapping
    • edit flow
    • common utility functions
    • static action/field data
  • Added dynamic action-based field mapping behavior for all supported B2BKing actions.
  • Added group selection and group refresh UI for customer group updates.
  • Updated action selectors and integration loaders to register and render B2BKing.
  • Added B2BKing integration icon asset.

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Tests added/updated
  • Documentation updated if needed
  • README updated if needed

Changelog

    • Feature: Added a new B2BKing action integration to automate customer approval, B2B enablement, and group updates from workflow data.
    • New Actions: Added B2BKing actions for approving customers, enabling B2B users, and updating customer group assignments.
    • Improvement: Added a dedicated group refresh and selection flow to keep B2BKing customer group mapping accurate during integration setup.

Copilot AI review requested due to automatic review settings May 16, 2026 10:24
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

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 : [];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

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) [];

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in this PR — the fallback is now (object) [] instead of []:

$utilities = isset($integrationDetails->utilities) ? $integrationDetails->utilities : (object) [];

Comment on lines +30 to +34
$user = get_user_by($field, $value);

if (empty($user->user_email)) {
return static::formattedUserTemp();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

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();
        }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The refreshGroups method is called statically in Routes.php (line 11), but it is defined as an instance method. It should be declared as static to avoid potential issues and follow the pattern used in b2bKingAuthorize.

    public static function refreshGroups()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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'] ?? '';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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'),
            ];
        }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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;
}

Comment on lines +90 to +97
options={
b2bKingConf?.allGroups &&
Array.isArray(b2bKingConf.allGroups) &&
b2bKingConf.allGroups.map(group => ({
label: group.label,
value: String(group.value)
}))
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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.

Suggested change
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)
}))
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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)
}))}

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 16, 2026

✅ WordPress Plugin Check Report

✅ Status: Passed

📊 Report

All checks passed! No errors or warnings found.


🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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

  • setSnackbar is declared as a prop but never used in this component. With the repo’s ESLint config (includes no-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.

Comment on lines +30 to +32
$user = get_user_by($field, $value);

if (empty($user->user_email)) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

Comment on lines 20 to 24
'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],
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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 : '';
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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 : [];
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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'
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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()
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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')}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed. Changed the loading message to 'Checking B2BKing connection…'.

Comment on lines +89 to +96
type="button">
+
</button>
<button
onClick={() => delFieldMap(i, b2bKingConf, setB2BKingConf)}
className="icn-btn sh-sm ml-1"
type="button"
aria-label="btn">
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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 />
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

Comment on lines +7 to +15
export default function B2BKingAuthorization({
b2bKingConf,
setB2BKingConf,
step,
nextPage,
isLoading,
setIsLoading,
setSnackbar
}) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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}.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants