Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new integration for NinjaTables, featuring a backend controller for data retrieval, a record helper for executing row actions, and a comprehensive frontend suite for authorization and field mapping. Additionally, the PR updates several existing integration components to a standard 500px minimum height and includes a minor dependency update. The review feedback highlights opportunities to align with WordPress database standards, correct a typo in a user-facing error message, and improve the robustness of the field mapping logic with additional safety checks.
| WHERE post_type = %s AND post_status = %s | ||
| ORDER BY post_title ASC", | ||
| self::POST_TYPE, |
| return [ | ||
| 'success' => false, | ||
| 'message' => wp_sprintf( | ||
| __('%s plugin is not installed or activate', 'bit-integrations'), |
| foreach ($fieldMap as $value) { | ||
| $triggerValue = $value->formField; | ||
| $actionValue = $value->columnName; | ||
|
|
||
| if ($triggerValue === 'custom' && isset($value->customValue)) { | ||
| $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $fieldValues); | ||
| } elseif (isset($fieldValues[$triggerValue]) && !\is_null($fieldValues[$triggerValue])) { | ||
| $dataFinal[$actionValue] = $fieldValues[$triggerValue]; | ||
| } | ||
| } |
There was a problem hiding this comment.
The generateReqDataFromFieldMap method lacks safety checks for columnName and formField. Mapping fields with an empty column name can lead to invalid keys in the resulting data array. Additionally, the logic here partially duplicates processRowFields; ensure consistency in how mapped data is handled.
foreach ($fieldMap as $value) {
$triggerValue = $value->formField ?? '';
$actionValue = $value->columnName ?? '';
if (empty($actionValue)) {
continue;
}
if ($triggerValue === 'custom' && isset($value->customValue)) {
$dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $fieldValues);
} elseif (isset($fieldValues[$triggerValue])) {
$dataFinal[$actionValue] = $fieldValues[$triggerValue];
}
}
🔍 WordPress Plugin Check Report
📊 Report
|
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
0 |
mismatched_plugin_name | Plugin name "Bit integrations - Easy Automator with no-code automation, integrate Webhook and automate 300+ Platform" is different from the name declared in plugin header "Bit Integrations". |
0 |
readme_parser_warnings_trimmed_section_description | The "Description" section is too long and was truncated. A maximum of 2500 characters is supported. |
🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check
There was a problem hiding this comment.
Pull request overview
Adds a new Ninja Tables action integration (frontend configuration UI + backend AJAX endpoints) and updates various integration step layouts.
Changes:
- Introduces Ninja Tables integration UI (authorization, action selection, table/row/user pickers, field mapping, edit flow).
- Adds backend Ninja Tables routes/controller + record execution helper for add/update row actions.
- Standardizes step-2 container
minHeightacross multiple existing integrations.
Reviewed changes
Copilot reviewed 28 out of 29 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Updates lockfile dependency (caniuse-lite) snapshot. |
| frontend/src/Utils/StaticData/tutorialLinks.js | Adds Ninja Tables documentation link entry. |
| frontend/src/components/Flow/New/SelectAction.jsx | Adds “Ninja Tables” to action selection list. |
| frontend/src/components/AllIntegrations/NewInteg.jsx | Wires Ninja Tables into the “new integration” router/switch. |
| frontend/src/components/AllIntegrations/EditInteg.jsx | Wires Ninja Tables into the “edit integration” router/switch. |
| frontend/src/components/AllIntegrations/IntegInfo.jsx | Wires Ninja Tables authorization into integration info view. |
| frontend/src/components/AllIntegrations/NinjaTables/staticData.js | Defines Ninja Tables action modules metadata. |
| frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesCommonFunc.js | Adds Ninja Tables refresh/auth helpers + mapped-field validation. |
| frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesAuthorization.jsx | Adds step-1 “authorization” UI for Ninja Tables. |
| frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesIntegLayout.jsx | Adds step-2 UI (action/table/row/user selection + mapping). |
| frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesFieldMap.jsx | Adds field mapping row UI for Ninja Tables. |
| frontend/src/components/AllIntegrations/NinjaTables/NinjaTables.jsx | Adds the new integration “create” flow container (steps 1–3). |
| frontend/src/components/AllIntegrations/NinjaTables/EditNinjaTables.jsx | Adds the edit flow container for an existing Ninja Tables action. |
| frontend/src/components/AllIntegrations/WPCourseware/WPCourseware.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/WPCafe/WPCafe.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/TutorLms/TutorLms.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/Telegram/Telegram.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/TeamsForWooCommerceMemberships/TeamsForWooCommerceMemberships.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/SeoPress/SeoPress.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/MailPoet/MailPoet.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/MailerPress/MailerPress.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/FluentCRM/FluentCrm.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/FluentCart/FluentCart.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/Encharge/Encharge.jsx | Updates step container minHeight (and fixes missing px). |
| frontend/src/components/AllIntegrations/Autonami/Autonami.jsx | Updates step container minHeight. |
| frontend/src/components/AllIntegrations/AcademyLms/AcademyLms.jsx | Updates step container minHeight. |
| backend/Actions/NinjaTables/Routes.php | Registers AJAX endpoints for authorize + refresh tables/rows/users/columns. |
| backend/Actions/NinjaTables/NinjaTablesController.php | Implements refresh endpoints and execution entrypoint. |
| backend/Actions/NinjaTables/RecordApiHelper.php | Implements execution logic + filter dispatch + logging for add/update. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesAuthorization.jsx
Show resolved
Hide resolved
| <button | ||
| onClick={handleAuthorize} | ||
| className="btn btcd-btn-lg purple sh-sm flx" | ||
| type="button" | ||
| disabled={isAuthorized}> | ||
| {isAuthorized | ||
| ? __('Connected ✔', 'bit-integrations') | ||
| : __('Connect to Ninja Tables', 'bit-integrations')} | ||
| {isLoading === 'auth' && <LoaderSm size={20} clr="#022217" className="ml-2" />} | ||
| </button> |
There was a problem hiding this comment.
When isInfo is true, the "Connect" button is still clickable and triggers handleAuthorize(), which uses formID, setIsLoading, and setSnackbar. In the integration info view these props are not provided, so clicking the button will throw. Hide/disable the connect UI (and skip authorization logic) when isInfo is true.
| setNinjaTablesConf(prevConf => ({ | ||
| ...prevConf, | ||
| default: { | ||
| ...prevConf.default, |
There was a problem hiding this comment.
handleFetch spreads prevConf.default without guarding for undefined. If an existing config ever lacks the default object (e.g., older saved configs), this will throw a TypeError during refresh. Use a fallback like ...(prevConf.default || {}) when constructing the new default object.
| ...prevConf.default, | |
| ...(prevConf.default || {}), |
| return [ | ||
| 'success' => false, | ||
| 'message' => wp_sprintf( | ||
| __('%s plugin is not installed or activate', 'bit-integrations'), |
There was a problem hiding this comment.
Spelling/grammar in the default error message: "not installed or activate" should be "not installed or activated". This message is user-visible in logs/errors, so it should be corrected.
| __('%s plugin is not installed or activate', 'bit-integrations'), | |
| __('%s plugin is not installed or activated', 'bit-integrations'), |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 29 changed files in this pull request and generated 6 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case 'Ninja Tables': | ||
| return <NinjaTablesAuthorization ninjaTablesConf={integrationConf} step={1} isInfo /> |
There was a problem hiding this comment.
In the Integration Info view this component is instantiated without the state setters/handlers (e.g., setNinjaTablesConf, setIsLoading, setSnackbar). NinjaTablesAuthorization currently uses those unconditionally (input onChange / Connect button), which will throw at runtime. Either pass the required props here, or update NinjaTablesAuthorization to fully support an isInfo mode (disable inputs/buttons and avoid calling missing callbacks).
| bitsFetch(requestParams, 'ninja_tables_authorize').then(result => { | ||
| if (result?.success) { | ||
| setIsAuthorized(true) | ||
| setSnackbar({ | ||
| show: true, | ||
| msg: __('Connected with Ninja Tables successfully', 'bit-integrations') | ||
| }) | ||
| setIsLoading(false) | ||
| } else { | ||
| setShowAuthMsg(true) | ||
| setSnackbar({ | ||
| show: true, | ||
| msg: __( | ||
| result?.data | ||
| ? result.data | ||
| : 'Connection failed. Please make sure Ninja Tables is installed and activated', | ||
| 'bit-integrations' | ||
| ) | ||
| }) | ||
| setIsLoading(false) | ||
| } | ||
| }) |
There was a problem hiding this comment.
handleAuthorize() does not handle rejected promises. If bitsFetch throws/rejects, setIsLoading(false) is never called and the UI can get stuck in loading state. Add a catch/finally path that resets loading and shows a useful error message; also consider clearing showAuthMsg on successful authorization so the reminder doesn’t persist after a retry succeeds.
| bitsFetch(requestParams, 'ninja_tables_authorize').then(result => { | |
| if (result?.success) { | |
| setIsAuthorized(true) | |
| setSnackbar({ | |
| show: true, | |
| msg: __('Connected with Ninja Tables successfully', 'bit-integrations') | |
| }) | |
| setIsLoading(false) | |
| } else { | |
| setShowAuthMsg(true) | |
| setSnackbar({ | |
| show: true, | |
| msg: __( | |
| result?.data | |
| ? result.data | |
| : 'Connection failed. Please make sure Ninja Tables is installed and activated', | |
| 'bit-integrations' | |
| ) | |
| }) | |
| setIsLoading(false) | |
| } | |
| }) | |
| bitsFetch(requestParams, 'ninja_tables_authorize') | |
| .then(result => { | |
| if (result?.success) { | |
| setIsAuthorized(true) | |
| setShowAuthMsg(false) | |
| setSnackbar({ | |
| show: true, | |
| msg: __('Connected with Ninja Tables successfully', 'bit-integrations') | |
| }) | |
| } else { | |
| setShowAuthMsg(true) | |
| setSnackbar({ | |
| show: true, | |
| msg: __( | |
| result?.data | |
| ? result.data | |
| : 'Connection failed. Please make sure Ninja Tables is installed and activated', | |
| 'bit-integrations' | |
| ) | |
| }) | |
| } | |
| }) | |
| .catch(error => { | |
| setShowAuthMsg(true) | |
| setSnackbar({ | |
| show: true, | |
| msg: __( | |
| error?.message || | |
| 'Connection failed. Please make sure Ninja Tables is installed and activated', | |
| 'bit-integrations' | |
| ) | |
| }) | |
| }) | |
| .finally(() => { | |
| setIsLoading(false) | |
| }) |
| setSnackbar({ | ||
| show: true, | ||
| msg: successMsg | ||
| }) | ||
| } else { | ||
| throw new Error(result?.data || errorMsg) | ||
| } | ||
| } catch (error) { | ||
| setSnackbar({ | ||
| show: true, | ||
| msg: error.message || errorMsg | ||
| }) |
There was a problem hiding this comment.
throw new Error(result?.data || errorMsg) can produce an unhelpful message like "[object Object]" when result.data is not a string. Consider normalizing to a string (e.g., prefer result?.data?.message, JSON.stringify for objects, or fallback to errorMsg) before constructing the Error / snackbar message.
| className="icn-btn sh-sm ml-2" | ||
| type="button" | ||
| title={__('Remove', 'bit-integrations')} | ||
| aria-label="btn"> |
There was a problem hiding this comment.
The remove button has aria-label="btn", which isn’t descriptive for screen readers. Use an accessible label that describes the action (e.g., “Remove field mapping row”).
| aria-label="btn"> | |
| aria-label={__('Remove field mapping row', 'bit-integrations')}> |
| return [ | ||
| 'success' => false, | ||
| 'message' => wp_sprintf( | ||
| __('%s plugin is not installed or activate', 'bit-integrations'), |
There was a problem hiding this comment.
Typo/grammar in the translatable string: "is not installed or activate" should be "is not installed or activated".
| __('%s plugin is not installed or activate', 'bit-integrations'), | |
| __('%s plugin is not installed or activated', 'bit-integrations'), |
| foreach ($fieldMap as $value) { | ||
| $triggerValue = $value->formField; | ||
| $actionValue = $value->columnName; | ||
|
|
||
| if ($triggerValue === 'custom' && isset($value->customValue)) { | ||
| $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $fieldValues); | ||
| } elseif (isset($fieldValues[$triggerValue]) && !\is_null($fieldValues[$triggerValue])) { | ||
| $dataFinal[$actionValue] = $fieldValues[$triggerValue]; | ||
| } |
There was a problem hiding this comment.
generateReqDataFromFieldMap() writes to $dataFinal[$actionValue] without checking that $actionValue (columnName) is non-empty. With the initial/partially-configured field_map, this can create entries with an empty-string key and overwrite previous values. Skip items where columnName is empty before adding to $dataFinal.
No description provided.