From 6dfe4805e4fb375e64041bb8fdde1fb34e2f86ff Mon Sep 17 00:00:00 2001 From: Rishad Alam <101513331+RishadAlam@users.noreply.github.com> Date: Fri, 1 May 2026 18:15:09 +0600 Subject: [PATCH 1/3] feat: Add BookingPress integration - Implemented BookingPress integration in the frontend, including components for editing, creating, and authorizing the integration. - Added backend functionality for BookingPress actions, including authorization and record handling. - Introduced field mapping for BookingPress actions, allowing users to map form fields to BookingPress fields. - Created static data for BookingPress actions and fields. - Added a new image asset for BookingPress integration. --- .../BookingPress/BookingPressController.php | 37 ++++++ .../Actions/BookingPress/RecordApiHelper.php | 118 +++++++++++++++++ backend/Actions/BookingPress/Routes.php | 10 ++ backend/Core/Util/AllTriggersName.php | 1 + .../Utils/StaticData/webhookIntegrations.js | 1 + .../BookingPress/BookingPress.jsx | 104 +++++++++++++++ .../BookingPressAuthorization.jsx | 111 ++++++++++++++++ .../BookingPress/BookingPressCommonFunc.js | 32 +++++ .../BookingPress/BookingPressFieldMap.jsx | 110 ++++++++++++++++ .../BookingPress/BookingPressIntegLayout.jsx | 120 ++++++++++++++++++ .../BookingPress/EditBookingPress.jsx | 76 +++++++++++ .../BookingPress/staticData.js | 38 ++++++ .../components/AllIntegrations/EditInteg.jsx | 3 + .../components/AllIntegrations/IntegInfo.jsx | 3 + .../components/AllIntegrations/NewInteg.jsx | 10 ++ .../src/components/Flow/New/SelectAction.jsx | 1 + .../src/resource/img/integ/bookingPress.webp | Bin 0 -> 4154 bytes 17 files changed, 775 insertions(+) create mode 100644 backend/Actions/BookingPress/BookingPressController.php create mode 100644 backend/Actions/BookingPress/RecordApiHelper.php create mode 100644 backend/Actions/BookingPress/Routes.php create mode 100644 frontend/src/components/AllIntegrations/BookingPress/BookingPress.jsx create mode 100644 frontend/src/components/AllIntegrations/BookingPress/BookingPressAuthorization.jsx create mode 100644 frontend/src/components/AllIntegrations/BookingPress/BookingPressCommonFunc.js create mode 100644 frontend/src/components/AllIntegrations/BookingPress/BookingPressFieldMap.jsx create mode 100644 frontend/src/components/AllIntegrations/BookingPress/BookingPressIntegLayout.jsx create mode 100644 frontend/src/components/AllIntegrations/BookingPress/EditBookingPress.jsx create mode 100644 frontend/src/components/AllIntegrations/BookingPress/staticData.js create mode 100644 frontend/src/resource/img/integ/bookingPress.webp diff --git a/backend/Actions/BookingPress/BookingPressController.php b/backend/Actions/BookingPress/BookingPressController.php new file mode 100644 index 000000000..6ceef1895 --- /dev/null +++ b/backend/Actions/BookingPress/BookingPressController.php @@ -0,0 +1,37 @@ +flow_details; + $integId = $integrationData->id; + $fieldMap = $integrationDetails->field_map; + + if (empty($fieldMap)) { + return new WP_Error('field_map_empty', __('Field map is empty', 'bit-integrations')); + } + + return (new RecordApiHelper($integrationDetails, $integId))->execute($fieldValues, $fieldMap); + } +} diff --git a/backend/Actions/BookingPress/RecordApiHelper.php b/backend/Actions/BookingPress/RecordApiHelper.php new file mode 100644 index 000000000..3db344e61 --- /dev/null +++ b/backend/Actions/BookingPress/RecordApiHelper.php @@ -0,0 +1,118 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function execute($fieldValues, $fieldMap) + { + if (!class_exists('BookingPress')) { + return [ + 'success' => false, + 'message' => __('BookingPress is not installed or activated', 'bit-integrations'), + ]; + } + + $fieldData = static::generateReqDataFromFieldMap($fieldMap, $fieldValues); + $mainAction = $this->_integrationDetails->mainAction ?? 'cancel_appointment'; + + $defaultResponse = [ + 'success' => false, + // translators: %s: Plugin name + 'message' => wp_sprintf(__('%s plugin is not installed or activated', 'bit-integrations'), 'Bit Integrations Pro'), + ]; + + switch ($mainAction) { + case 'cancel_appointment': + $response = Hooks::apply(Config::withPrefix('bookingpress_cancel_appointment'), $defaultResponse, $fieldData); + $type = 'appointment'; + $actionType = 'cancel_appointment'; + + break; + + case 'update_appointment_status': + $response = Hooks::apply(Config::withPrefix('bookingpress_update_appointment_status'), $defaultResponse, $fieldData); + $type = 'appointment'; + $actionType = 'update_appointment_status'; + + break; + + case 'create_customer': + $response = Hooks::apply(Config::withPrefix('bookingpress_create_customer'), $defaultResponse, $fieldData); + $type = 'customer'; + $actionType = 'create_customer'; + + break; + + case 'update_customer': + $response = Hooks::apply(Config::withPrefix('bookingpress_update_customer'), $defaultResponse, $fieldData); + $type = 'customer'; + $actionType = 'update_customer'; + + break; + + case 'delete_appointment': + $response = Hooks::apply(Config::withPrefix('bookingpress_delete_appointment'), $defaultResponse, $fieldData); + $type = 'appointment'; + $actionType = 'delete_appointment'; + + break; + + case 'delete_customer': + $response = Hooks::apply(Config::withPrefix('bookingpress_delete_customer'), $defaultResponse, $fieldData); + $type = 'customer'; + $actionType = 'delete_customer'; + + break; + + default: + $response = [ + 'success' => false, + 'message' => __('Invalid action', 'bit-integrations'), + ]; + $type = 'BookingPress'; + $actionType = 'unknown'; + + break; + } + + $responseType = isset($response['success']) && $response['success'] ? 'success' : 'error'; + LogHandler::save($this->_integrationID, ['type' => $type, 'type_name' => $actionType], $responseType, $response); + + return $response; + } + + private function generateReqDataFromFieldMap($fieldMap, $fieldValues) + { + $dataFinal = []; + foreach ($fieldMap as $item) { + if (empty($item->formField) || empty($item->bookingPressField)) { + continue; + } + + $triggerValue = $item->formField; + $actionValue = $item->bookingPressField; + + $dataFinal[$actionValue] = $triggerValue === 'custom' && isset($item->customValue) + ? Common::replaceFieldWithValue($item->customValue, $fieldValues) + : $fieldValues[$triggerValue] ?? ''; + } + + return $dataFinal; + } +} diff --git a/backend/Actions/BookingPress/Routes.php b/backend/Actions/BookingPress/Routes.php new file mode 100644 index 000000000..e4e17c50e --- /dev/null +++ b/backend/Actions/BookingPress/Routes.php @@ -0,0 +1,10 @@ + ['name' => 'Buddypress', 'isPro' => true, 'is_active' => false], 'BbPress' => ['name' => 'bbPress', 'isPro' => true, 'is_active' => false], 'BookingCalendarContactForm' => ['name' => 'Booking Calendar Contact Form', 'isPro' => true, 'is_active' => false], + 'BookingPress' => ['name' => 'BookingPress', 'isPro' => true, 'is_active' => false], 'CalculatedFieldsForm' => ['name' => 'Calculated Fields Form Pro', 'isPro' => true, 'is_active' => false], 'CartFlow' => ['name' => 'CartFlow', 'isPro' => true, 'is_active' => false], 'CustomTrigger' => ['name' => 'Custom Trigger', 'isPro' => true, 'is_active' => false], diff --git a/frontend/src/Utils/StaticData/webhookIntegrations.js b/frontend/src/Utils/StaticData/webhookIntegrations.js index 69b3101d6..ff9562767 100644 --- a/frontend/src/Utils/StaticData/webhookIntegrations.js +++ b/frontend/src/Utils/StaticData/webhookIntegrations.js @@ -94,6 +94,7 @@ export const customFormIntegrations = [ 'WeDocs', 'UserRegistrationMembership', 'UltimateAffiliatePro', + 'BookingPress', ] export const actionHookIntegrations = ['ActionHook'] diff --git a/frontend/src/components/AllIntegrations/BookingPress/BookingPress.jsx b/frontend/src/components/AllIntegrations/BookingPress/BookingPress.jsx new file mode 100644 index 000000000..1f63c4a45 --- /dev/null +++ b/frontend/src/components/AllIntegrations/BookingPress/BookingPress.jsx @@ -0,0 +1,104 @@ +import { useState } from 'react' +import 'react-multiple-select-dropdown-lite/dist/index.css' +import { useNavigate, useParams } from 'react-router' +import BackIcn from '../../../Icons/BackIcn' +import { __ } from '../../../Utils/i18nwrap' +import SnackMsg from '../../Utilities/SnackMsg' +import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers' +import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' +import BookingPressAuthorization from './BookingPressAuthorization' +import { checkMappedFields } from './BookingPressCommonFunc' +import BookingPressIntegLayout from './BookingPressIntegLayout' + +export default function BookingPress({ formFields, setFlow, flow, allIntegURL }) { + const navigate = useNavigate() + const { formID } = useParams() + const [isLoading, setIsLoading] = useState(false) + const [step, setStep] = useState(1) + const [snack, setSnackbar] = useState({ show: false }) + const [bookingPressConf, setBookingPressConf] = useState({ + name: 'BookingPress', + type: 'BookingPress', + field_map: [{ formField: '', bookingPressField: '' }], + mainAction: '', + }) + + const nextPage = val => { + setTimeout(() => { + document.getElementById('btcd-settings-wrp').scrollTop = 0 + }, 300) + + if (val === 3) { + if (!checkMappedFields(bookingPressConf)) { + setSnackbar({ + show: true, + msg: __('Please map all required fields to continue.', 'bit-integrations'), + }) + return + } + + if (bookingPressConf.name !== '' && bookingPressConf.field_map.length > 0) { + setStep(val) + } + } else { + setStep(val) + } + } + + return ( +
+ +
+ + {/* STEP 1 */} + + + {/* STEP 2 */} +
+ +
+
+
+ +
+ + {/* STEP 3 */} + + saveIntegConfig(flow, setFlow, allIntegURL, bookingPressConf, navigate, '', '', setIsLoading) + } + isLoading={isLoading} + /> +
+ ) +} diff --git a/frontend/src/components/AllIntegrations/BookingPress/BookingPressAuthorization.jsx b/frontend/src/components/AllIntegrations/BookingPress/BookingPressAuthorization.jsx new file mode 100644 index 000000000..059530a71 --- /dev/null +++ b/frontend/src/components/AllIntegrations/BookingPress/BookingPressAuthorization.jsx @@ -0,0 +1,111 @@ +import { useState } from 'react' +import BackIcn from '../../../Icons/BackIcn' +import bitsFetch from '../../../Utils/bitsFetch' +import { __ } from '../../../Utils/i18nwrap' +import LoaderSm from '../../Loaders/LoaderSm' +import TutorialLink from '../../Utilities/TutorialLink' + +export default function BookingPressAuthorization({ + bookingPressConf, + setBookingPressConf, + step, + nextPage, + isLoading, + setIsLoading, + setSnackbar +}) { + const [isAuthorized, setIsAuthorized] = useState(false) + const [showAuthMsg, setShowAuthMsg] = useState(false) + + const authorizeHandler = () => { + setIsLoading('auth') + bitsFetch({}, 'bookingpress_authorize').then(result => { + if (result?.success) { + setIsAuthorized(true) + setSnackbar({ + show: true, + msg: __('Connected with BookingPress Successfully', 'bit-integrations'), + }) + } + setIsLoading(false) + setShowAuthMsg(true) + }) + } + + const handleInput = e => { + const newConf = { ...bookingPressConf } + newConf[e.target.name] = e.target.value + setBookingPressConf(newConf) + } + + return ( +
+ +
+ {__('Integration Name:', 'bit-integrations')} +
+ + + {isLoading === 'auth' && ( +
+ + {__('Checking if BookingPress is authorized!!!', 'bit-integrations')} +
+ )} + + {showAuthMsg && !isAuthorized && !isLoading && ( +
+
+
+ +
+
+ {__('BookingPress is not activated or not installed', 'bit-integrations')} +
+
+
+ )} + + {showAuthMsg && isAuthorized && !isLoading && ( +
+
+ +
+
{__('BookingPress is activated', 'bit-integrations')}
+
+ )} + + +
+ +
+ ) +} diff --git a/frontend/src/components/AllIntegrations/BookingPress/BookingPressCommonFunc.js b/frontend/src/components/AllIntegrations/BookingPress/BookingPressCommonFunc.js new file mode 100644 index 000000000..4214ffe32 --- /dev/null +++ b/frontend/src/components/AllIntegrations/BookingPress/BookingPressCommonFunc.js @@ -0,0 +1,32 @@ +import { create } from 'mutative' + +export const handleInput = (e, bookingPressConf, setBookingPressConf) => { + const { name, value } = e.target + setBookingPressConf(prevConf => + create(prevConf, draftConf => { + draftConf[name] = value + }) + ) +} + +export const checkMappedFields = bookingPressConf => { + const mappedFields = bookingPressConf?.field_map + ? bookingPressConf.field_map.filter( + mappedField => + !mappedField.formField || + !mappedField.bookingPressField || + (mappedField.formField === 'custom' && !mappedField.customValue) + ) + : [] + return mappedFields.length === 0 +} + +export const generateMappedField = fields => { + const requiredFlds = fields.filter(fld => fld.required === true) + return requiredFlds.length > 0 + ? requiredFlds.map(field => ({ + formField: '', + bookingPressField: field.key, + })) + : [{ formField: '', bookingPressField: '' }] +} diff --git a/frontend/src/components/AllIntegrations/BookingPress/BookingPressFieldMap.jsx b/frontend/src/components/AllIntegrations/BookingPress/BookingPressFieldMap.jsx new file mode 100644 index 000000000..67f2a59a9 --- /dev/null +++ b/frontend/src/components/AllIntegrations/BookingPress/BookingPressFieldMap.jsx @@ -0,0 +1,110 @@ +import { useRecoilValue } from 'recoil' +import { $appConfigState } from '../../../GlobalStates' +import { __, sprintf } from '../../../Utils/i18nwrap' +import { SmartTagField } from '../../../Utils/StaticData/SmartTagField' +import TagifyInput from '../../Utilities/TagifyInput' +import { + addFieldMap, + delFieldMap, + handleCustomValue, + handleFieldMapping, +} from '../GlobalIntegrationHelper' + +export default function BookingPressFieldMap({ + i, + formFields, + field, + bookingPressConf, + setBookingPressConf, +}) { + const btcbi = useRecoilValue($appConfigState) + const { isPro } = btcbi + + const requiredFlds = bookingPressConf?.bookingPressFields?.filter(fld => fld.required === true) || [] + const nonRequiredFlds = bookingPressConf?.bookingPressFields?.filter(fld => fld.required === false) || [] + + return ( +
+
+
+ + + {field.formField === 'custom' && ( + handleCustomValue(e, i, bookingPressConf, setBookingPressConf)} + label={__('Custom Value', 'bit-integrations')} + className="mr-2" + type="text" + value={field.customValue} + placeholder={__('Custom Value', 'bit-integrations')} + formFields={formFields} + /> + )} + + +
+ {i >= requiredFlds.length && ( + <> + + + + )} +
+
+ ) +} diff --git a/frontend/src/components/AllIntegrations/BookingPress/BookingPressIntegLayout.jsx b/frontend/src/components/AllIntegrations/BookingPress/BookingPressIntegLayout.jsx new file mode 100644 index 000000000..96dd94170 --- /dev/null +++ b/frontend/src/components/AllIntegrations/BookingPress/BookingPressIntegLayout.jsx @@ -0,0 +1,120 @@ +import { create } from 'mutative' +import MultiSelect from 'react-multiple-select-dropdown-lite' +import { useRecoilValue } from 'recoil' +import { $appConfigState } from '../../../GlobalStates' +import { __ } from '../../../Utils/i18nwrap' +import { checkIsPro, getProLabel } from '../../Utilities/ProUtilHelpers' +import { addFieldMap } from '../IntegrationHelpers/IntegrationHelpers' +import { generateMappedField } from './BookingPressCommonFunc' +import BookingPressFieldMap from './BookingPressFieldMap' +import { + appointmentIdField, + CreateCustomerFields, + DeleteCustomerFields, + modules, + UpdateAppointmentStatusFields, + UpdateCustomerFields +} from './staticData' + +export default function BookingPressIntegLayout({ + formID, + formFields, + bookingPressConf, + setBookingPressConf, + setSnackbar, +}) { + const btcbi = useRecoilValue($appConfigState) + const { isPro } = btcbi + + const handleMainAction = value => { + setBookingPressConf(prevConf => + create(prevConf, draftConf => { + draftConf.mainAction = value + + switch (value) { + case 'cancel_appointment': + draftConf.bookingPressFields = appointmentIdField + break + case 'update_appointment_status': + draftConf.bookingPressFields = UpdateAppointmentStatusFields + break + case 'create_customer': + draftConf.bookingPressFields = CreateCustomerFields + break + case 'update_customer': + draftConf.bookingPressFields = UpdateCustomerFields + break + case 'delete_appointment': + draftConf.bookingPressFields = appointmentIdField + break + case 'delete_customer': + draftConf.bookingPressFields = DeleteCustomerFields + break + default: + draftConf.bookingPressFields = [] + } + + draftConf.field_map = generateMappedField(draftConf.bookingPressFields) + }) + ) + } + + return ( + <> +
+
+ {__('Action:', 'bit-integrations')} + handleMainAction(value)} + options={modules?.map(action => ({ + label: checkIsPro(isPro, action.is_pro) ? action.label : getProLabel(action.label), + value: action.name, + disabled: checkIsPro(isPro, action.is_pro) ? false : true, + }))} + singleSelect + closeOnSelect + /> +
+ + {bookingPressConf?.mainAction && bookingPressConf.bookingPressFields && ( +
+ {__('Map Fields', 'bit-integrations')} +
+
+
+ {__('Form Fields', 'bit-integrations')} +
+
+ {__('BookingPress Fields', 'bit-integrations')} +
+
+ + {bookingPressConf?.field_map?.map((itm, i) => ( + + ))} +
+ +
+
+
+ )} + + ) +} diff --git a/frontend/src/components/AllIntegrations/BookingPress/EditBookingPress.jsx b/frontend/src/components/AllIntegrations/BookingPress/EditBookingPress.jsx new file mode 100644 index 000000000..55958b2d3 --- /dev/null +++ b/frontend/src/components/AllIntegrations/BookingPress/EditBookingPress.jsx @@ -0,0 +1,76 @@ +import { useState } from 'react' +import { useNavigate, useParams } from 'react-router' +import { useRecoilState, useRecoilValue } from 'recoil' +import { $actionConf, $formFields, $newFlow } from '../../../GlobalStates' +import { __ } from '../../../Utils/i18nwrap' +import SnackMsg from '../../Utilities/SnackMsg' +import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' +import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' +import SetEditIntegComponents from '../IntegrationHelpers/SetEditIntegComponents' +import { checkMappedFields, handleInput } from './BookingPressCommonFunc' +import BookingPressIntegLayout from './BookingPressIntegLayout' + +export default function EditBookingPress({ allIntegURL }) { + const navigate = useNavigate() + const { id, formID } = useParams() + + const [bookingPressConf, setBookingPressConf] = useRecoilState($actionConf) + const [flow, setFlow] = useRecoilState($newFlow) + const formFields = useRecoilValue($formFields) + const [isLoading, setIsLoading] = useState(false) + const [snack, setSnackbar] = useState({ show: false }) + + return ( +
+ + +
+ {__('Integration Name:', 'bit-integrations')} + handleInput(e, bookingPressConf, setBookingPressConf)} + name="name" + value={bookingPressConf.name} + type="text" + placeholder={__('Integration Name...', 'bit-integrations')} + /> +
+
+ + + + + + + saveActionConf({ + flow, + setFlow, + allIntegURL, + conf: bookingPressConf, + navigate, + id, + edit: 1, + setIsLoading, + setSnackbar, + }) + } + disabled={!checkMappedFields(bookingPressConf)} + isLoading={isLoading} + dataConf={bookingPressConf} + setDataConf={setBookingPressConf} + formFields={formFields} + /> +
+
+ ) +} diff --git a/frontend/src/components/AllIntegrations/BookingPress/staticData.js b/frontend/src/components/AllIntegrations/BookingPress/staticData.js new file mode 100644 index 000000000..15d84cdaa --- /dev/null +++ b/frontend/src/components/AllIntegrations/BookingPress/staticData.js @@ -0,0 +1,38 @@ +import { __ } from '../../../Utils/i18nwrap' + +export const modules = [ + { name: 'cancel_appointment', label: __('Cancel Appointment', 'bit-integrations'), is_pro: true }, + { name: 'update_appointment_status', label: __('Update Appointment Status', 'bit-integrations'), is_pro: true }, + { name: 'create_customer', label: __('Create Customer', 'bit-integrations'), is_pro: true }, + { name: 'update_customer', label: __('Update Customer', 'bit-integrations'), is_pro: true }, + { name: 'delete_appointment', label: __('Delete Appointment', 'bit-integrations'), is_pro: true }, + { name: 'delete_customer', label: __('Delete Customer', 'bit-integrations'), is_pro: true }, +] + +export const appointmentIdField = [ + { key: 'appointment_id', label: __('Appointment ID', 'bit-integrations'), required: true }, +] + +export const UpdateAppointmentStatusFields = [ + { key: 'appointment_id', label: __('Appointment ID', 'bit-integrations'), required: true }, + { key: 'status', label: __('Status', 'bit-integrations'), required: true }, +] + +export const CreateCustomerFields = [ + { key: 'first_name', label: __('First Name', 'bit-integrations'), required: true }, + { key: 'last_name', label: __('Last Name', 'bit-integrations'), required: true }, + { key: 'email', label: __('Email', 'bit-integrations'), required: true }, + { key: 'phone', label: __('Phone', 'bit-integrations'), required: false }, +] + +export const UpdateCustomerFields = [ + { key: 'customer_id', label: __('Customer ID', 'bit-integrations'), required: true }, + { key: 'bookingpress_user_firstname', label: __('First Name', 'bit-integrations'), required: false }, + { key: 'bookingpress_user_lastname', label: __('Last Name', 'bit-integrations'), required: false }, + { key: 'bookingpress_user_email', label: __('Email', 'bit-integrations'), required: false }, + { key: 'bookingpress_user_phone', label: __('Phone', 'bit-integrations'), required: false }, +] + +export const DeleteCustomerFields = [ + { key: 'customer_id', label: __('Customer ID', 'bit-integrations'), required: true }, +] diff --git a/frontend/src/components/AllIntegrations/EditInteg.jsx b/frontend/src/components/AllIntegrations/EditInteg.jsx index c4201bb07..f3a0cf09e 100644 --- a/frontend/src/components/AllIntegrations/EditInteg.jsx +++ b/frontend/src/components/AllIntegrations/EditInteg.jsx @@ -74,6 +74,7 @@ const EditLearnDash = lazy(() => import('./LearnDash/EditLearnDash')) const EditRestrictContent = lazy(() => import('./RestrictContent/EditRestrictContent')) const EditAffiliate = lazy(() => import('./Affiliate/EditAffiliate')) const EditBuddyBoss = lazy(() => import('./BuddyBoss/EditBuddyBoss')) +const EditBookingPress = lazy(() => import('./BookingPress/EditBookingPress')) const EditGoogleContacts = lazy(() => import('./GoogleContacts/EditGoogleContacts')) const EditKirimEmail = lazy(() => import('./KirimEmail/EditKirimEmail')) const EditGamiPress = lazy(() => import('./GamiPress/EditGamiPress')) @@ -395,6 +396,8 @@ const IntegType = memo(({ allIntegURL, flow }) => { return case 'BuddyBoss': return + case 'BookingPress': + return case 'GamiPress': return case 'Google Contacts': diff --git a/frontend/src/components/AllIntegrations/IntegInfo.jsx b/frontend/src/components/AllIntegrations/IntegInfo.jsx index 03de88b5b..04756add1 100644 --- a/frontend/src/components/AllIntegrations/IntegInfo.jsx +++ b/frontend/src/components/AllIntegrations/IntegInfo.jsx @@ -175,6 +175,7 @@ const UltimateAffiliateProAuthorization = lazy( () => import('./UltimateAffiliatePro/UltimateAffiliateProAuthorization') ) const FluentCartAuthorization = lazy(() => import('./FluentCart/FluentCartAuthorization')) +const BookingPressAuthorization = lazy(() => import('./BookingPress/BookingPressAuthorization')) const PeepSoAuthorization = lazy(() => import('./PeepSo/PeepSoAuthorization')) const NinjaTablesAuthorization = lazy(() => import('./NinjaTables/NinjaTablesAuthorization')) const WCAffiliateAuthorization = lazy(() => import('./WCAffiliate/WCAffiliateAuthorization')) @@ -637,6 +638,8 @@ export default function IntegInfo() { ) case 'FluentCart': return + case 'BookingPress': + return case 'PeepSo': return case 'Ninja Tables': diff --git a/frontend/src/components/AllIntegrations/NewInteg.jsx b/frontend/src/components/AllIntegrations/NewInteg.jsx index 505955d35..f849fa767 100644 --- a/frontend/src/components/AllIntegrations/NewInteg.jsx +++ b/frontend/src/components/AllIntegrations/NewInteg.jsx @@ -75,6 +75,7 @@ const WhatsApp = lazy(() => import('./WhatsApp/WhatsApp')) const LearnDesh = lazy(() => import('./LearnDash/LearnDash')) const Affiliate = lazy(() => import('./Affiliate/Affiliate')) const BuddyBoss = lazy(() => import('./BuddyBoss/BuddyBoss')) +const BookingPress = lazy(() => import('./BookingPress/BookingPress')) const GoogleContacts = lazy(() => import('./GoogleContacts/GoogleContacts')) const KirimEmail = lazy(() => import('./KirimEmail/KirimEmail')) const Salesforce = lazy(() => import('./Salesforce/Salesforce')) @@ -767,6 +768,15 @@ export default function NewInteg({ allIntegURL }) { setFlow={setFlow} /> ) + case 'BookingPress': + return ( + + ) case 'GamiPress': return ( @@;wkH2dhdnZ5E>wVHO?0 zO%cBBZQsiFzBl}5T$g!HONv3@U9H+mE)B3>h)KA0*MbbD2T% zAA&$hQ}7jZ0*nU0NL&OFc>A=Tm#zi~?cD_amKaQw2CLQxmxq~^3YJRvTw+8k(-$ke zE|@dZ;~D&()^}Xp!}YZF!FLGDtMg?2bl#Mz?_!+jJ6go>|M36t|M36t|M36t|M36t z|M36t|M36t|4?wJ?`TbZr?yw$RmtDrp#ftN?mioN;E$0iV^{vrss@nyy zhLpr-+{;EF$|6Br*539G04BUJin~npt=V4^0^ZvxI*a=98Yq9kKr@l#s;z&uoj4S- zS~~Mp&I&n#iS^G95g^h#)T*_Vi`5Lj?}ZGIQ#9|GS?wLfF;-AGAao7@08m2!odGHU z0RRC2001S8yM@rW3ee&2>%Wj3D4B=#pT+;ykMO@Tw72FTb$?L5w{F!{URYdZ{WI|U z`j5?j>Hq4#oc@4()$;@W5&pIKFWQUDC-blA-_<|3{r~;Lf2i_R_YnOY`TzQr_H6VA z{!jet(u@2@`2WBE!~fUs^?%wv0{@TyJ^BCe0Q3O$N9p(XAHrVvKev7+b&q(**={Yb zEA)@;|I>e9e>FZT@F##5-`VrKlRRi8UTMD{>T3-{CBUX{a@C74gO93d;V9rU-A#>U+cd&|9|;ezg^Eq5)_wt z{f7om8fkf77si`J#+yXOn?%N&M8=ypM0iz$&7C?5uIz{~I=HjD zi^u}|RN4u|AtzSo$aP1(;da1BBcBSCI?T_j_SR(es_mPQ?=?i#bL-QOIC=@6o|F`m zpw|yApQqZGDR1|VrgzN$=LJ;T)PCBw{!f{EGI$@hUmMsYO+w(j$9EP>CyvvjIE*&T zWG&3nh#P$Z>?;C(aLHux+H#b6A&E#t3DoO5#%Yml@ar{o{c*xgc?8wqg{KVLYkx^(!r1Iowp%B2!^v2ah9zYzH3I=2Y$!@vl^5$eC`54R6tZYx(s*O&>to zo#JUMfUGXgX?g6e>N}9zry~LVYbP{q@9|XMnO?iKPuokXaoSTM^>vO#3O>Tz{}U0} z>Tya4OVbV5-s!_fG$0`(1SOG0Nm~xEc;6#q#LCJX1l!e{-@bg5w2QNR;u*V14%X->#_~kEn!NhXT$p5Y6Oh)ZZPbP? zKxwHfida2Jm}+JI=)3WwF?@&KJ^6gCtT)wne%_JU9J+PP3mvS9jaZAYZs-*IT%9mTB|z7mbI|qWsoLL>KbT~>(l{K$81n|3Da#>F zG`AApS-!30uPS>sz|tjq(d=p-Yb?P_yS0nxNTm=uTfP`FcT@K$&>T%!y4(Yp#>jfQ z5kvJ7aSMtB$&De5&tmpHp>oU#550un8nR%;89y3n#k`9cJ)p_1IwRjw-J{*f zt2_73p#9)*$Pk~-Z0eXUeiV65l_|YLWAZAIQu)_pBEWAT{9hR&NgEFaZ#7<|_)%Ff zvvz{#n|5`)#Vy{%*9@_NlMlhto!6TtsXW=9MdblgD(NR-IOPYKO;*TeXlh0#DF(AXW*mjepD}O#y*1ZBC)P*as z9+b^Xd8{S)Eq)Ce2=;0*3ibhFko$H5!&GZeI&5^a+u!z<%INSZR`OTu6ti|Z0ykG* z{dA*z7gyeyJFnZZn^_RRG=x*gKu_aFRmJ>|dpin{8!BzK+MI48a8cP*#ja4C65sGc zs{#tp6-`uB5gs(Vu8RMJohh>wNY(9b0e@OACO_cM=M@UibTGwx`(6ihi?LxZ5 zkQim!C(@7_g#=L~#DnMcC+Ie`6v4Z8zUtd~3RwgH?pBd_VJ}`d9HS6yEZLl``?}6A zGq6&b$MGZ?t4ljkgzZy3H_dY_YCvdgwwq+yl58ffZ6yygdtzN)RbOk<0T3E{+XK=6 zrdE`S99>SJ7?nd{A-l`N_Y%CU}Rv6KzM5xH)saSjT z_)D3`ZUOif=zhO;609<KCC5YDf}{zbIS{xhf$0hRU+A zEaSv%8^6ke!LSDd8Pw8a)a|gs5go$r|K9m}WO7;5`9v-#*n>S^q7-Fp zuqS|4Q(B;bsknU(oAx9;JhoYtou~fv#FUjJ0^jT^l3i&A+rXy#QfIQ3> zgyyy@&3nKl0l?FsmZC6ONG`PB<&`@}567zwfXkE)4b_(y_S=m2haiyS}PN&MUAA?)FKnS;sywSKP%VYJxG-B@d8gaSdp1jSB zxUrVO@5-4y9ExAa7-&i@@4(!Xb}C986XymNWxqM_3MW6WE#y*F@qBNy!(xJB%Nmr{ zI?iE}UGnW+r5?c+#P|Oad*NOqKDrJ)LgG0_Kci8_5c#)>OiU1ode>e+HHK`{kw1Jo zUKSMdRVsq7t_WX5fyKzj%^pw~P#U;1)&W|2VBP{ATpXsq!3SJ^~CW0%WKa}cQVJ|Ez05fQ)YRLa-WG0Nws<*?q(p)h{>J~QpwTb;_mFdcw3v9ili29>Shca-^i&*P`SPU0Ab@7owPsIr#TYcU9m|4Owg3a_ODANFoZHGyLt!R_{z9O6JOBUy0oZ*KreiPM ziPhFzu9H253elW{{$zIAU0d(P6TFZ{Aek{V)ZO`rtt6Y3*WF%Ub;x_HrnCv3d#PGp z;y9(0QSRmDDS9k}Tl=+~hi&Z0wZCEOpJ4K+(ld)Kr-l4QYed|ce*^OIu0!3?H9!CW E0FX&f00000 literal 0 HcmV?d00001 From 38e75b3e6427d1a67dedee9697858212c651c0a7 Mon Sep 17 00:00:00 2001 From: Rishad Alam <101513331+RishadAlam@users.noreply.github.com> Date: Fri, 1 May 2026 18:22:07 +0600 Subject: [PATCH 2/3] feat: Add bookingPress tutorial links and create .codex file --- frontend/src/Utils/StaticData/tutorialLinks.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/Utils/StaticData/tutorialLinks.js b/frontend/src/Utils/StaticData/tutorialLinks.js index 97d2258b4..26d7e34e2 100644 --- a/frontend/src/Utils/StaticData/tutorialLinks.js +++ b/frontend/src/Utils/StaticData/tutorialLinks.js @@ -670,6 +670,10 @@ const tutorialLinks = { userRegistrationMembership: { youTubeLink: '', docLink: 'https://bit-integrations.com/wp-docs/actions/user-registration-and-membership-as-action/' + }, + bookingPress: { + youTubeLink: '', + docLink: '' } } export default tutorialLinks From 7a75d9e1f6795a4b893ebd389142880f7cea5229 Mon Sep 17 00:00:00 2001 From: Rishad Alam <101513331+RishadAlam@users.noreply.github.com> Date: Sat, 2 May 2026 10:35:43 +0600 Subject: [PATCH 3/3] refactor: Update BookingPress integration to improve code consistency and readability --- .../Actions/BookingPress/RecordApiHelper.php | 2 +- .../BookingPress/BookingPress.jsx | 2 -- .../BookingPressAuthorization.jsx | 3 ++- .../BookingPress/BookingPressIntegLayout.jsx | 19 +++++++++---------- .../BookingPress/EditBookingPress.jsx | 3 +-- .../BookingPress/staticData.js | 8 ++++---- 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/backend/Actions/BookingPress/RecordApiHelper.php b/backend/Actions/BookingPress/RecordApiHelper.php index 3db344e61..0d0147085 100644 --- a/backend/Actions/BookingPress/RecordApiHelper.php +++ b/backend/Actions/BookingPress/RecordApiHelper.php @@ -28,7 +28,7 @@ public function execute($fieldValues, $fieldMap) ]; } - $fieldData = static::generateReqDataFromFieldMap($fieldMap, $fieldValues); + $fieldData = $this->generateReqDataFromFieldMap($fieldMap, $fieldValues); $mainAction = $this->_integrationDetails->mainAction ?? 'cancel_appointment'; $defaultResponse = [ diff --git a/frontend/src/components/AllIntegrations/BookingPress/BookingPress.jsx b/frontend/src/components/AllIntegrations/BookingPress/BookingPress.jsx index 1f63c4a45..5287ab414 100644 --- a/frontend/src/components/AllIntegrations/BookingPress/BookingPress.jsx +++ b/frontend/src/components/AllIntegrations/BookingPress/BookingPress.jsx @@ -12,7 +12,6 @@ import BookingPressIntegLayout from './BookingPressIntegLayout' export default function BookingPress({ formFields, setFlow, flow, allIntegURL }) { const navigate = useNavigate() - const { formID } = useParams() const [isLoading, setIsLoading] = useState(false) const [step, setStep] = useState(1) const [snack, setSnackbar] = useState({ show: false }) @@ -70,7 +69,6 @@ export default function BookingPress({ formFields, setFlow, flow, allIntegURL }) minHeight: step === 2 && '500px', }}> { - setIsLoading('auth') + setIsLoading(true) bitsFetch({}, 'bookingpress_authorize').then(result => { if (result?.success) { setIsAuthorized(true) diff --git a/frontend/src/components/AllIntegrations/BookingPress/BookingPressIntegLayout.jsx b/frontend/src/components/AllIntegrations/BookingPress/BookingPressIntegLayout.jsx index 96dd94170..fd76bd2f0 100644 --- a/frontend/src/components/AllIntegrations/BookingPress/BookingPressIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/BookingPress/BookingPressIntegLayout.jsx @@ -9,15 +9,14 @@ import { generateMappedField } from './BookingPressCommonFunc' import BookingPressFieldMap from './BookingPressFieldMap' import { appointmentIdField, - CreateCustomerFields, - DeleteCustomerFields, + createCustomerFields, + deleteCustomerFields, modules, - UpdateAppointmentStatusFields, - UpdateCustomerFields + updateAppointmentStatusFields, + updateCustomerFields } from './staticData' export default function BookingPressIntegLayout({ - formID, formFields, bookingPressConf, setBookingPressConf, @@ -36,19 +35,19 @@ export default function BookingPressIntegLayout({ draftConf.bookingPressFields = appointmentIdField break case 'update_appointment_status': - draftConf.bookingPressFields = UpdateAppointmentStatusFields + draftConf.bookingPressFields = updateAppointmentStatusFields break case 'create_customer': - draftConf.bookingPressFields = CreateCustomerFields + draftConf.bookingPressFields = createCustomerFields break case 'update_customer': - draftConf.bookingPressFields = UpdateCustomerFields + draftConf.bookingPressFields = updateCustomerFields break case 'delete_appointment': draftConf.bookingPressFields = appointmentIdField break case 'delete_customer': - draftConf.bookingPressFields = DeleteCustomerFields + draftConf.bookingPressFields = deleteCustomerFields break default: draftConf.bookingPressFields = [] @@ -72,7 +71,7 @@ export default function BookingPressIntegLayout({ options={modules?.map(action => ({ label: checkIsPro(isPro, action.is_pro) ? action.label : getProLabel(action.label), value: action.name, - disabled: checkIsPro(isPro, action.is_pro) ? false : true, + disabled: !checkIsPro(isPro, action.is_pro), }))} singleSelect closeOnSelect diff --git a/frontend/src/components/AllIntegrations/BookingPress/EditBookingPress.jsx b/frontend/src/components/AllIntegrations/BookingPress/EditBookingPress.jsx index 55958b2d3..ed12785ae 100644 --- a/frontend/src/components/AllIntegrations/BookingPress/EditBookingPress.jsx +++ b/frontend/src/components/AllIntegrations/BookingPress/EditBookingPress.jsx @@ -12,7 +12,7 @@ import BookingPressIntegLayout from './BookingPressIntegLayout' export default function EditBookingPress({ allIntegURL }) { const navigate = useNavigate() - const { id, formID } = useParams() + const { id } = useParams() const [bookingPressConf, setBookingPressConf] = useRecoilState($actionConf) const [flow, setFlow] = useRecoilState($newFlow) @@ -40,7 +40,6 @@ export default function EditBookingPress({ allIntegURL }) {