feat(eventsmanager): add Events Manager action integration - #201
feat(eventsmanager): add Events Manager action integration#201RishadAlam wants to merge 2 commits into
Conversation
Add Events Manager as an action integration with one action, Unregister User From Event, plus the React UI for configuring it. The free side stays hook-only — it fires bit_integrations_eventsmanager_* and the Pro plugin supplies the handler. Both inputs identify the booking being cancelled, so both go through the field map rather than a dropdown: the event comes from trigger data at run time, and pinning it to a hand-picked id would make the action usable for exactly one event. Mapping -1 cancels every active booking the user holds. bit-pi's getAllEvents is a fetch rather than a write, so it is not ported as an action and no refresh route exists for it.
There was a problem hiding this comment.
Code Review
This pull request introduces a new integration for the Events Manager plugin, allowing users to unregister a user from an event. It includes backend controllers, API helpers, and routes, alongside frontend React components for authorization, field mapping, and editing the integration. Feedback focuses on ensuring that non-persisted static fields are correctly rebuilt on mount when editing the integration by utilizing a useEffect hook in EventsManagerIntegLayout.jsx. Additionally, it is recommended to use self:: instead of static:: when calling the private static method generateReqDataFromFieldMap in RecordApiHelper.php to prevent potential 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.
| 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 Note from '../../Utilities/Note' | ||
| import { checkIsPro, getProLabel } from '../../Utilities/ProUtilHelpers' | ||
| import { addFieldMap } from '../IntegrationHelpers/IntegrationHelpers' | ||
| import { generateMappedField } from './EventsManagerCommonFunc' | ||
| import EventsManagerFieldMap from './EventsManagerFieldMap' | ||
| import { modules, UnregisterUserFields } from './staticData' |
There was a problem hiding this comment.
To support rebuilding the non-persisted static fields (like eventsManagerFields) on mount when editing an integration, we need to import the useEffect hook from React.
| 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 Note from '../../Utilities/Note' | |
| import { checkIsPro, getProLabel } from '../../Utilities/ProUtilHelpers' | |
| import { addFieldMap } from '../IntegrationHelpers/IntegrationHelpers' | |
| import { generateMappedField } from './EventsManagerCommonFunc' | |
| import EventsManagerFieldMap from './EventsManagerFieldMap' | |
| import { modules, UnregisterUserFields } from './staticData' | |
| import { useEffect } from 'react' | |
| 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 Note from '../../Utilities/Note' | |
| import { checkIsPro, getProLabel } from '../../Utilities/ProUtilHelpers' | |
| import { addFieldMap } from '../IntegrationHelpers/IntegrationHelpers' | |
| import { generateMappedField } from './EventsManagerCommonFunc' | |
| import EventsManagerFieldMap from './EventsManagerFieldMap' | |
| import { modules, UnregisterUserFields } from './staticData' |
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.
Declining, together with the paired suggestion this import is for.
eventsManagerFields is persisted with the flow — verified against a saved flow in this database, where the equivalent klaviyoFields key is stored in flow_details alongside field_map. So there's no unpopulated-on-edit case to rebuild, and adding useEffect here would leave an unused import.
Details in the reply on the effect itself.
| const btcbi = useRecoilValue($appConfigState) | ||
| const { isPro } = btcbi | ||
|
|
||
| const mainAction = eventsManagerConf?.mainAction |
There was a problem hiding this comment.
When editing an integration, static fields like eventsManagerFields are not persisted in the database. We must rebuild them on mount from the saved action to prevent empty options and component crashes.
| const mainAction = eventsManagerConf?.mainAction | |
| const mainAction = eventsManagerConf?.mainAction | |
| useEffect(() => { | |
| if (mainAction && (!eventsManagerConf.eventsManagerFields || eventsManagerConf.eventsManagerFields.length === 0)) { | |
| setEventsManagerConf(prevConf => | |
| create(prevConf, draftConf => { | |
| draftConf.eventsManagerFields = 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.
Declining — the premise doesn't hold for this integration: eventsManagerFields is persisted.
handleMainAction writes it into the conf, and the whole conf is what saveActionConf stores in flow_details. Confirmed against a real saved flow in this database, where the equivalent key is present:
{"name":"Klaviyo","type":"Klaviyo","field_map":[…],
"klaviyoFields":[{"key":"email","label":"Email","required":true}, …]}So on edit the field list comes back from the database and the map renders normally — there's no empty-options or crash case for the effect to guard.
A near-identical suggestion was declined on #200 for the same reason. This variant is better (it only fills when the array is missing, rather than overwriting saved config on mount), but it would still be guarding a state that can't currently be reached, and the import in the paired comment exists only to serve it.
If {Name}Fields going stale across plugin updates is the underlying worry, that one is real — but it applies to every integration in the repo, not this one, and the conditional-fill wouldn't help since the stale array is non-empty.
| ]; | ||
| } | ||
|
|
||
| $fieldData = static::generateReqDataFromFieldMap($fieldMap, $fieldValues); |
There was a problem hiding this comment.
Since generateReqDataFromFieldMap is a private static method, it is safer and more idiomatic to call it using self:: instead of static::. Using static:: invokes late static binding, which can lead to runtime errors if the class is extended, as private methods are not accessible to subclasses.
$fieldData = self::generateReqDataFromFieldMap($fieldMap, $fieldValues);There was a problem hiding this comment.
Declining, on consistency grounds rather than disagreement.
The technical point is fair — with a private method, static:: would resolve in the subclass scope and fatal if a child declared its own generateReqDataFromFieldMap. But RecordApiHelper is never subclassed (each integration ships its own), and the repo is near-unanimous the other way:
21 x static::generateReqDataFromFieldMap
2 x self::generateReqDataFromFieldMap
Making this one file the exception would leave a reader wondering what's different about Events Manager. If the preference is self::, that's worth doing as one sweep across all 23 call sites rather than piecemeal here.
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
Description
Adds Events Manager as an action integration with one action, Unregister User From Event, plus the React UI for configuring it. The free plugin stays hook-only: it fires
bit_integrations_eventsmanager_*and the Pro plugin supplies the handler.Pairs with Bit-Apps-Pro/bit-integrations-pro#139, which implements the handler and the ten triggers.
Motivation & Context
Events Manager was available in bit-pi but missing from the Bit Integrations free/pro pair. This ports it across.
bit-pi's
getAllEventsis a fetch rather than a write, so it is not carried over as an action — per the integration scope rules, fetched data belongs in a dropdown, not an action list.Type of Change
Key Changes
Backend (Actions)
EventsManagerControllerwith the activation check (class_exists('EM_Events')) and the authorize endpointRecordApiHelperfiring the literalbit_integrations_eventsmanager_unregister_user_from_eventhook, so the wiring is greppable from either pluginRoutes.phpwith the authorize routeAllTriggersName.phpFrontend
AllIntegrations/EventsManager/(wizard, authorization, integration layout, field map, shared helpers, edit view, static data)NewInteg.jsx,EditInteg.jsx,IntegInfo.jsxandSelectAction.jsx, and added the logoDesign notes
delete_order-1as the Event ID cancels every active booking the user holdsChecklist
Testing
Verified against Events Manager 7.4.0.1 on a live install — ESLint (zero warnings), Prettier,
php -land php-cs-fixer all clean.Changelog