feat(wptablebuilder): add WP Table Builder action integration#200
feat(wptablebuilder): add WP Table Builder action integration#200RishadAlam wants to merge 3 commits into
Conversation
Add WP Table Builder as an action integration with four write actions: Create Table, Update Table, Delete Table and Add Row to Table. The free side stays hook-only — it fires bit_integrations_wptablebuilder_* and the Pro plugin supplies the handlers. Add Row needs its target table's column list while the flow is being configured, so the table is picked from a fetched dropdown rather than the field map, and refresh_wptablebuilder_columns reads the column labels from the first row of the stored markup. Columns are positional (cell_0..cell_N) because WP Table Builder cells carry no stable identifier. Create, Update and Delete keep table_id in the field map so a flow can target a different table per run.
…eBuilderController
There was a problem hiding this comment.
Code Review
This pull request introduces a new integration for WP Table Builder, allowing users to create, update, delete tables, and add rows. It includes backend controllers, helpers, and routes, as well as frontend React components for authorization, layout, and field mapping. The review feedback highlights several improvement opportunities: rebuilding non-persisted configuration fields on mount when editing, adding proper error handling and loading state resets in asynchronous catch blocks, implementing defensive checks to prevent PHP warnings and undefined property notices, and providing default parameters to prevent runtime errors.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| useEffect(() => { | ||
| if (needsColumns && !wpTableBuilderConf?.tables?.length) { | ||
| refreshTables(wpTableBuilderConf, setWpTableBuilderConf, setIsLoading) | ||
| } | ||
| }, [needsColumns]) |
There was a problem hiding this comment.
When editing an integration, non-persisted configuration fields (like static fields) should be rebuilt on mount from the saved action without modifying the saved field mappings. This ensures that the static fields are populated correctly and prevents empty options or component crashes.
useEffect(() => {
if (needsColumns && !wpTableBuilderConf?.tables?.length) {
refreshTables(wpTableBuilderConf, setWpTableBuilderConf, setIsLoading)
}
}, [needsColumns])
useEffect(() => {
if (mainAction && mainAction !== 'add_row') {
setWpTableBuilderConf(prevConf =>
create(prevConf, draftConf => {
draftConf.wpTableBuilderFields = fieldsByAction[mainAction] || []
})
)
}
}, [mainAction])References
- When editing an integration, ensure that non-persisted configuration fields (like static fields) are rebuilt on mount from the saved action without modifying the saved field mappings, to prevent empty options and component crashes.
There was a problem hiding this comment.
Not applying this one.
wpTableBuilderFields is persisted with the flow, so an edit screen already opens with the correct definitions — there is no empty-options or crash case to fix here. Rebuilding it in a mount effect would rewrite saved configuration merely as a side effect of opening the edit view, which is a heavier contract than the problem warrants.
Add Row is the case that genuinely cannot rely on persisted definitions, since its columns come from the selected table rather than from staticData. That path already refetches — the table list on mount, and the columns via the Refresh Columns control.
The broader point about static field definitions going stale across plugin updates is a real one, but it applies to every integration in the repo, not just this one, so it belongs in a repo-wide change rather than here.
| typeof result?.data === 'string' ? result.data : __('Failed to fetch tables', 'bit-integrations') | ||
| ) | ||
| }) | ||
| .catch(() => setIsLoading(false)) |
There was a problem hiding this comment.
When handling asynchronous API requests, always append a .catch block to ensure that loading states are reset and error messages are displayed if the request fails or encounters a network error.
.catch(() => {
setIsLoading(false)
toast.error(__('An error occurred while fetching tables', 'bit-integrations'))
})References
- When handling asynchronous API requests, always append a
.catchblock to ensure that loading states are reset and error messages are displayed if the request fails or encounters a network error.
There was a problem hiding this comment.
Done — the .catch now clears the loading state and surfaces a toast:
.catch(() => {
setIsLoading(false)
toast.error(__('An error occurred while fetching tables', 'bit-integrations'))
})Previously a network failure left the dropdown silently empty with no explanation.
| : __('Failed to fetch columns', 'bit-integrations') | ||
| ) | ||
| }) | ||
| .catch(() => setIsLoading(false)) |
There was a problem hiding this comment.
When handling asynchronous API requests, always append a .catch block to ensure that loading states are reset and error messages are displayed if the request fails or encounters a network error.
.catch(() => {
setIsLoading(false)
toast.error(__('An error occurred while fetching columns', 'bit-integrations'))
})References
- When handling asynchronous API requests, always append a
.catchblock to ensure that loading states are reset and error messages are displayed if the request fails or encounters a network error.
There was a problem hiding this comment.
Done, same treatment as the tables fetch:
.catch(() => {
setIsLoading(false)
toast.error(__('An error occurred while fetching columns', 'bit-integrations'))
})This one matters a little more — a silent failure here leaves the field map empty, which reads as "this table has no columns" rather than "the request failed".
| { | ||
| self::isExists(); | ||
|
|
||
| if (empty($requestParams->selectedTable)) { |
There was a problem hiding this comment.
There was a problem hiding this comment.
Declining — empty() already covers this.
empty($requestParams->selectedTable) does not evaluate the property read when $requestParams is null; it returns true silently. Verified on the PHP this runs against:
PHP 8.4.23
empty($null->p) bool(true) no warning
isset($null->p) bool(false) no warning
Only a direct read warns. So empty($requestParams) || is a second condition that can never change the outcome.
The same review flagged a case where the read is direct — $item->formField in RecordApiHelper — and that one was real and is now fixed.
| ) | ||
| } | ||
|
|
||
| export const generateMappedField = fields => { |
There was a problem hiding this comment.
Done:
export const generateMappedField = (fields = []) => {Both current callers already pass an array (fieldsByAction[value] || [] and result.data?.columns || []), so this is guarding future callers rather than fixing a live crash — but it costs nothing.
| $triggerValue = $item->formField; | ||
| $actionValue = $item->wpTableBuilderField; |
There was a problem hiding this comment.
Good catch, applied:
$triggerValue = $item->formField ?? '';
$actionValue = $item->wpTableBuilderField ?? '';These are direct property reads, which is what makes this one different from the isset()/empty() cases elsewhere in this review — a direct read on a missing property does emit Warning: Undefined property, so the ?? is doing real work here.
🔍 WordPress Plugin Check Report
📊 Report
|
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
0 |
mismatched_plugin_name | Plugin name "Bit integrations - Form Integration, Webhook, Spreadsheets, CRM, LMS & Email Automation" is different from the name declared in plugin header "Bit Integrations". |
🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check
Description
Adds WP Table Builder as an action integration with four actions — Create Table, Update Table, Delete Table and Add Row to Table — plus the React UI for configuring them. The free plugin stays hook-only: it fires
bit_integrations_wptablebuilder_*and the Pro plugin supplies the handlers.Pairs with Bit-Apps-Pro/bit-integrations-pro#138, which implements the handlers and the four triggers.
Motivation & Context
WP Table Builder was one of the integrations available in bit-pi but missing from the Bit Integrations free/pro pair. This ports it across, dropping the read-only actions (Get Table, Get Row, Get Column, Get Cell, List Rows) per the integration scope rules — fetched data belongs in a dropdown, not an action.
Add Row is new relative to the bit-pi port: bit-pi only exposed whole-table writes, so building a table row-by-row from form submissions was not possible.
Type of Change
Key Changes
Backend (Actions)
WpTableBuilderControllerwith the activation check, plusrefreshTables()andrefreshColumns()for the Add Row dropdown and its column listRecordApiHelperwith one switch case per action, each firing a literalbit_integrations_wptablebuilder_*hook so the wiring is greppable from either pluginRoutes.phpexposing the authorize route and the two refresh routesAllTriggersName.phpFrontend
AllIntegrations/WpTableBuilder/(wizard, authorization, integration layout, field map, utilities, edit view, shared helpers, static data)NewInteg.jsx,EditInteg.jsx,IntegInfo.jsx,SelectAction.jsxandwebhookIntegrations.js, and added the logoDesign notes
table_idthrough the field map, so one flow can target a different table per runcell_0…cell_N) because WP Table Builder cells carry no stable identifier — reordering columns in WP Table Builder means refreshing them herereact-multiple-select-dropdown-litematchingdefaultValuein an effect keyed ondefaultValuealone and never on the optionsChecklist
Testing
Verified against WP Table Builder 2.1.18 on a live install:
data-wptb-*attribute, class and inline stylephp -land php-cs-fixer all cleanChangelog