feat: add Webba Booking Calendar action integration#174
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new integration for the Webba Booking Calendar, adding backend API helpers, controllers, routes, and frontend React components for authorization, configuration, and field mapping. The review feedback suggests several improvements: in the backend, we should check for WP_Error objects before accessing responses as arrays, and declare controller methods as static where $this is not used. On the frontend, we should add error handling to the authorization fetch request, safely check for the existence of DOM elements before modifying their scroll position, and use a safer string conversion method for option values to prevent runtime crashes.
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.
| $responseType = isset($response['success']) && $response['success'] ? 'success' : 'error'; | ||
| LogHandler::save($this->_integrationID, ['type' => 'WebbaBooking', 'type_name' => $mainAction], $responseType, $response); |
There was a problem hiding this comment.
When handling responses that may return a WP_Error object, always use the is_wp_error() function to validate the response before accessing it as an array or object. This prevents fatal errors in PHP 8.0+.
if (is_wp_error($response)) {
$response = [
'success' => false,
'message' => $response->get_error_message()
];
}
$responseType = isset($response['success']) && $response['success'] ? 'success' : 'error';
LogHandler::save($this->_integrationID, ['type' => 'WebbaBooking', 'type_name' => $mainAction], $responseType, $response);References
- In PHP, when handling responses that may return a WP_Error object, always use the is_wp_error() function to validate the response before accessing it as an array or object. This prevents fatal errors in PHP 8.0+.
There was a problem hiding this comment.
Done — added an is_wp_error() guard before treating $response as an array:
if (is_wp_error($response)) {
$response = [
"success" => false,
"message" => $response->get_error_message()
];
}| public function refreshServices() | ||
| { |
There was a problem hiding this comment.
Declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
public static function refreshServices()
{References
- In PHP, declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
There was a problem hiding this comment.
Done — refreshServices() is now static (no $this usage; matches the convention in sibling controllers e.g. Bookly).
| public function refreshStaff() | ||
| { |
There was a problem hiding this comment.
Declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
public static function refreshStaff()
{References
- In PHP, declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
There was a problem hiding this comment.
Done — refreshStaff() is now static.
| public function refreshCategories() | ||
| { |
There was a problem hiding this comment.
Declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
public static function refreshCategories()
{References
- In PHP, declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
There was a problem hiding this comment.
Done — refreshCategories() is now static.
| public function refreshLocations() | ||
| { |
There was a problem hiding this comment.
Declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
public static function refreshLocations()
{References
- In PHP, declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
There was a problem hiding this comment.
Done — refreshLocations() is now static.
| public function refreshCoupons() | ||
| { |
There was a problem hiding this comment.
Declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
public static function refreshCoupons()
{References
- In PHP, declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
There was a problem hiding this comment.
Done — refreshCoupons() is now static.
| public function execute($integrationData, $fieldValues) | ||
| { |
There was a problem hiding this comment.
Declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
public static function execute($integrationData, $fieldValues)
{References
- In PHP, declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.
There was a problem hiding this comment.
Skipped intentionally. execute() is the Flow-engine entry point — it is resolved and invoked as an instance method ($controller->execute($flowData, $triggerData)) and is declared non-static across all action controllers (e.g. Bookly, BookingPress) for contract consistency. Making it static would deviate from that contract, so it stays an instance method. The stateless refresh* helpers were made static as suggested.
| const authorizeHandler = () => { | ||
| setIsLoading('auth') | ||
| bitsFetch({}, 'webba_booking_authorize').then(result => { | ||
| if (result?.success) { | ||
| setIsAuthorized(true) | ||
| setSnackbar({ | ||
| show: true, | ||
| msg: __('Connected with Webba Booking Successfully', 'bit-integrations') | ||
| }) | ||
| } | ||
| setIsLoading(false) | ||
| setShowAuthMsg(true) | ||
| }) | ||
| } |
There was a problem hiding this comment.
Add a .catch() block to the bitsFetch promise to handle network or request failures gracefully and prevent the UI from being stuck in a loading state.
const authorizeHandler = () => {
setIsLoading('auth')
bitsFetch({}, 'webba_booking_authorize')
.then(result => {
if (result?.success) {
setIsAuthorized(true)
setSnackbar({
show: true,
msg: __('Connected with Webba Booking Successfully', 'bit-integrations')
})
}
setIsLoading(false)
setShowAuthMsg(true)
})
.catch(() => {
setIsLoading(false)
setShowAuthMsg(true)
})
}There was a problem hiding this comment.
Done — added a .catch() so a failed request resets the loading state instead of leaving the UI stuck:
bitsFetch({}, "webba_booking_authorize")
.then(result => { /* ... */ })
.catch(() => {
setIsLoading(false)
setShowAuthMsg(true)
})| setTimeout(() => { | ||
| document.getElementById('btcd-settings-wrp').scrollTop = 0 | ||
| }, 300) |
There was a problem hiding this comment.
There was a problem hiding this comment.
Done — guarded the lookup before touching scrollTop:
const settingsWrp = document.getElementById("btcd-settings-wrp")
if (settingsWrp) {
settingsWrp.scrollTop = 0
}| options={(webbaBookingConf?.[optionSource] ?? []).map(opt => ({ | ||
| label: opt.label, | ||
| value: opt.value.toString() | ||
| }))} |
There was a problem hiding this comment.
Use String(opt.value ?? '') instead of opt.value.toString() to safely handle cases where opt.value might be null or undefined, preventing potential runtime crashes.
| options={(webbaBookingConf?.[optionSource] ?? []).map(opt => ({ | |
| label: opt.label, | |
| value: opt.value.toString() | |
| }))} | |
| options={(webbaBookingConf?.[optionSource] ?? []).map(opt => ({ | |
| label: opt.label, | |
| value: String(opt.value ?? '') | |
| }))} |
There was a problem hiding this comment.
Done — switched to String(opt.value ?? "") to safely handle null/undefined values.
🔍 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 a new Webba Booking Calendar action integration. Lets any Bit Integrations trigger create and manage Webba Booking records — bookings, coupons, services, categories, staff members, and locations — by mapping trigger fields to Webba entities. The action layer is wired in the free plugin and delegates the actual writes to Pro hooks.
Motivation & Context
Webba Booking is a popular WordPress appointment plugin. Users want to push data from their forms/triggers into Webba (e.g. create a booking when a form is submitted) and keep booking state in sync. This integration exposes that capability through the standard Bit Integrations action wizard.
Type of Change
Key Changes
Backend (Action)
WebbaBookingController— authorization check (WBK_Booking_Factorypresence) plus dropdown-population endpoints for services, staff, categories, locations, statuses, bookings, and coupons (read from Webba models / validated tables).RecordApiHelper— maps trigger fields to Webba fields and dispatches one of 13 actions (create/update/approve/cancel/delete booking, set paid, create/update coupon, create/update service, create category, create staff, create location) via prefixed Pro hooks; logs success/error throughLogHandler.Routes.php— AJAX routes for auth and all refresh endpoints.WebbaBookinginAllTriggersName.php(Pro,Webba Booking Calendar).Frontend
WebbaBooking/integration: action wizard (WebbaBooking.jsx),WebbaBookingAuthorization,WebbaBookingIntegLayout,WebbaBookingFieldMap,EditWebbaBooking,WebbaBookingCommonFunc, andstaticData.NewInteg,EditInteg,IntegInfo, andSelectAction; added to the custom-form integrations list inwebhookIntegrations.js.webbaBooking.webpintegration icon.Cleanup
sureDash.svg(no longer referenced anywhere in the frontend).Checklist
Changelog