feat: include record IDs in Salesforce assignment reason strings (#22561)

* feat: include record IDs in Salesforce assignment reason strings

- Add recordId parameter to assignmentReasonHandler function
- Include Contact ID, Lead ID, and Account ID in assignment reason strings
- Update entire call chain to pass record IDs from CRM service
- Maintain backward compatibility with optional recordId parameter

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

* fix: resolve lint warnings in assignment reason handler implementation

- Change Record<string, any> to Record<string, unknown> in BookingHandlerInput type
- Remove unused eventTypeId variable in getAttributeRoutingConfig function

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

* fix: revert to Record<string, any> with ESLint disable for BookingHandlerInput

- Revert from Record<string, unknown> to Record<string, any> to maintain type compatibility
- Add ESLint disable comment to suppress no-explicit-any warning
- Maintains consistency with handleNewRecurringBooking.ts pattern

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

* feat: pass CRM record ID from booker state to handleNewBooking

- Add crmRecordId field to booker store interface and initialization
- Update mapBookingToMutationInput to include record ID from booker state
- Modify handleNewBooking to extract record ID from bookingData parameter
- Add crmRecordId to BookingCreateBody schema in Prisma layer
- Follow existing pattern for CRM fields (teamMemberEmail, crmOwnerRecordType, crmAppSlug)
- Ensures record ID flows: booker store → booking form → mapBookingToMutationInput → handleNewBooking

This replaces the previous backend CRM service extraction approach with
frontend booker state approach as requested by the user.

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

* Pass crmRecordId as prop

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
authored and GitHub committed 2025-07-21 14:31:31 +00:00
1 parent e7535b9db3
commit 3784421f02
22 files changed
+147 -36

No files matched your search

@@ -122,12 +122,14 @@ export async function getCRMData(
const crmContactOwnerEmail = query["cal.crmContactOwnerEmail"];
const crmContactOwnerRecordType = query["cal.crmContactOwnerRecordType"];
const crmAppSlugParam = query["cal.crmAppSlug"];
const crmRecordIdParam = query["cal.crmRecordId"];
let teamMemberEmail = Array.isArray(crmContactOwnerEmail) ? crmContactOwnerEmail[0] : crmContactOwnerEmail;
let crmOwnerRecordType = Array.isArray(crmContactOwnerRecordType)
? crmContactOwnerRecordType[0]
: crmContactOwnerRecordType;
let crmAppSlug = Array.isArray(crmAppSlugParam) ? crmAppSlugParam[0] : crmAppSlugParam;
let crmRecordId = Array.isArray(crmRecordIdParam) ? crmRecordIdParam[0] : crmRecordIdParam;
if (!teamMemberEmail || !crmOwnerRecordType || !crmAppSlug) {
const { getTeamMemberEmailForResponseOrContactUsingUrlQuery } = await import(
@@ -137,6 +139,7 @@ export async function getCRMData(
email,
recordType,
crmAppSlug: crmAppSlugQuery,
recordId: crmRecordIdQuery,
} = await getTeamMemberEmailForResponseOrContactUsingUrlQuery({
query,
eventData,
@@ -145,11 +148,13 @@ export async function getCRMData(
teamMemberEmail = email ?? undefined;
crmOwnerRecordType = recordType ?? undefined;
crmAppSlug = crmAppSlugQuery ?? undefined;
crmRecordId = crmRecordIdQuery ?? undefined;
}
return {
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
};
}
@@ -29,9 +29,9 @@ function mockGetCRMContactOwnerForRRLeadSkip({
}) {
vi.mocked(getCRMContactOwnerForRRLeadSkip).mockImplementation((_bookerEmail, _eventMetadata) => {
if (_bookerEmail === bookerEmail) {
return Promise.resolve({ email: teamMemberEmail, recordType: null, crmAppSlug: null });
return Promise.resolve({ email: teamMemberEmail, recordType: null, crmAppSlug: null, recordId: null });
}
return Promise.resolve({ email: null, recordType: null, crmAppSlug: null });
return Promise.resolve({ email: null, recordType: null, crmAppSlug: null, recordId: null });
});
}
@@ -45,9 +45,9 @@ function mockBookingFormHandler({
vi.mocked(bookingFormHandlers.salesforce).mockImplementation(
(_bookerEmail, _attributeRoutingConfig, _eventTypeId) => {
if (_bookerEmail === bookerEmail) {
return Promise.resolve({ email: teamMemberEmail, recordType: null });
return Promise.resolve({ email: teamMemberEmail, recordType: null, recordId: null });
}
return Promise.resolve({ email: null, recordType: null });
return Promise.resolve({ email: null, recordType: null, recordId: null });
}
);
}
@@ -92,6 +92,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
const crmContactOwnerEmail = query["cal.crmContactOwnerEmail"];
const crmContactOwnerRecordType = query["cal.crmContactOwnerRecordType"];
const crmAppSlugParam = query["cal.crmAppSlug"];
const crmRecordIdParam = query["cal.crmRecordId"];
// Handle string[] type from query params
let teamMemberEmail = Array.isArray(crmContactOwnerEmail) ? crmContactOwnerEmail[0] : crmContactOwnerEmail;
@@ -101,6 +102,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
: crmContactOwnerRecordType;
let crmAppSlug = Array.isArray(crmAppSlugParam) ? crmAppSlugParam[0] : crmAppSlugParam;
let crmRecordId = Array.isArray(crmRecordIdParam) ? crmRecordIdParam[0] : crmRecordIdParam;
if (!teamMemberEmail || !crmOwnerRecordType || !crmAppSlug) {
const { getTeamMemberEmailForResponseOrContactUsingUrlQuery } = await import(
@@ -110,6 +112,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
email,
recordType,
crmAppSlug: crmAppSlugQuery,
recordId,
} = await getTeamMemberEmailForResponseOrContactUsingUrlQuery({
query,
eventData,
@@ -118,6 +121,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
teamMemberEmail = email ?? undefined;
crmOwnerRecordType = recordType ?? undefined;
crmAppSlug = crmAppSlugQuery ?? undefined;
crmRecordId = recordId ?? undefined;
}
const organizationSettings = getOrganizationSEOSettings(team);
@@ -167,6 +171,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
isSEOIndexable: allowSEOIndexing,
},
};
+2
View File
@@ -34,6 +34,7 @@ function Type({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
isEmbed,
useApiV2,
}: PageProps) {
@@ -64,6 +65,7 @@ function Type({
teamMemberEmail={teamMemberEmail}
crmOwnerRecordType={crmOwnerRecordType}
crmAppSlug={crmAppSlug}
crmRecordId={crmRecordId}
/>
</main>
</BookingPageErrorBoundary>
+13 -3
View File
@@ -10,8 +10,13 @@ import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
export async function getCRMContactOwnerForRRLeadSkip(
bookerEmail: string,
eventTypeMetadata: Prisma.JsonValue
): Promise<{ email: string | null; recordType: string | null; crmAppSlug: string | null }> {
const nullReturnValue = { email: null, recordType: null, crmAppSlug: "" };
): Promise<{
email: string | null;
recordType: string | null;
crmAppSlug: string | null;
recordId: string | null;
}> {
const nullReturnValue = { email: null, recordType: null, crmAppSlug: "", recordId: null };
const parsedEventTypeMetadata = EventTypeMetaDataSchema.safeParse(eventTypeMetadata);
if (!parsedEventTypeMetadata.success || !parsedEventTypeMetadata.data?.apps) return nullReturnValue;
@@ -24,7 +29,12 @@ export async function getCRMContactOwnerForRRLeadSkip(
const endTime = performance.now();
logger.info(`Fetching from CRM took ${endTime - startTime}ms`);
if (!contact?.length || !contact[0].ownerEmail) return nullReturnValue;
return { email: contact[0].ownerEmail ?? null, recordType: contact[0].recordType ?? null, crmAppSlug };
return {
email: contact[0].ownerEmail ?? null,
recordType: contact[0].recordType ?? null,
crmAppSlug,
recordId: contact[0].id ?? null,
};
}
async function getCRMManagerWithRRLeadSkip(apps: z.infer<typeof EventTypeAppMetadataSchema>) {
@@ -5,7 +5,7 @@ type AppBookingFormHandler = (
attendeeEmail: string,
attributeRoutingConfig: AttributeRoutingConfig,
eventTypeId: number
) => Promise<{ email: string | null; recordType: string | null }>;
) => Promise<{ email: string | null; recordType: string | null; recordId: string | null }>;
const appBookingFormHandler: Record<string, AppBookingFormHandler> = {
salesforce: routingFormBookingFormHandler,
@@ -40,10 +40,16 @@ export default async function routerGetCrmContactOwnerEmail({
const eventTypeMetadata = eventType.metadata;
if (!eventTypeMetadata) return null;
let contactOwner: { email: string | null; recordType: string | null; crmAppSlug: string | null } = {
let contactOwner: {
email: string | null;
recordType: string | null;
crmAppSlug: string | null;
recordId: string | null;
} = {
email: null,
recordType: null,
crmAppSlug: null,
recordId: null,
};
// Determine if there is a CRM option enabled in the chosen route
for (const appSlug of enabledAppSlugs) {
@@ -267,6 +267,7 @@ describe("handleResponse", () => {
email: "owner@example.com",
recordType: "contact",
crmAppSlug: "hubspot",
recordId: "123",
});
vi.mocked(findTeamMembersMatchingAttributeLogic).mockResolvedValue({
teamMembersMatchingAttributeLogic: [{ userId: 123, result: "MATCH" as any }],
@@ -96,6 +96,7 @@ const _handleResponse = async ({
let crmContactOwnerEmail: string | null = null;
let crmContactOwnerRecordType: string | null = null;
let crmAppSlug: string | null = null;
let crmRecordId: string | null = null;
let timeTaken: Record<string, number | null> = {};
if (chosenRoute) {
if (isRouter(chosenRoute)) {
@@ -119,6 +120,7 @@ const _handleResponse = async ({
crmContactOwnerEmail = contactOwnerQuery?.email ?? null;
crmContactOwnerRecordType = contactOwnerQuery?.recordType ?? null;
crmAppSlug = contactOwnerQuery?.crmAppSlug ?? null;
crmRecordId = contactOwnerQuery?.recordId ?? null;
})(),
(async () => {
const teamMembersMatchingAttributeLogicWithResult =
@@ -212,6 +214,7 @@ const _handleResponse = async ({
crmContactOwnerEmail,
crmContactOwnerRecordType,
crmAppSlug,
crmRecordId,
attributeRoutingConfig: chosenRoute
? "attributeRoutingConfig" in chosenRoute
? chosenRoute.attributeRoutingConfig
@@ -8,27 +8,52 @@ export async function assignmentReasonHandler({
recordType,
teamMemberEmail,
routingFormResponseId,
recordId,
}: {
recordType: string;
teamMemberEmail: string;
routingFormResponseId: number;
recordId?: string;
}) {
const returnObject = { reasonEnum: AssignmentReasonEnum.SALESFORCE_ASSIGNMENT };
switch (recordType) {
case SalesforceRecordEnum.CONTACT:
return { ...returnObject, assignmentReason: `Salesforce contact owner: ${teamMemberEmail}` };
return {
...returnObject,
assignmentReason: `Salesforce contact owner: ${teamMemberEmail}${
recordId ? ` (Contact ID: ${recordId})` : ""
}`,
};
case SalesforceRecordEnum.LEAD:
return { ...returnObject, assignmentReason: `Salesforce lead owner: ${teamMemberEmail}` };
return {
...returnObject,
assignmentReason: `Salesforce lead owner: ${teamMemberEmail}${
recordId ? ` (Lead ID: ${recordId})` : ""
}`,
};
case SalesforceRecordEnum.ACCOUNT:
return { ...returnObject, assignmentReason: `Salesforce account owner: ${teamMemberEmail}` };
return {
...returnObject,
assignmentReason: `Salesforce account owner: ${teamMemberEmail}${
recordId ? ` (Account ID: ${recordId})` : ""
}`,
};
case RoutingReasons.ACCOUNT_LOOKUP_FIELD:
const assignmentReason = await handleAccountLookupFieldReason(routingFormResponseId, teamMemberEmail);
const assignmentReason = await handleAccountLookupFieldReason(
routingFormResponseId,
teamMemberEmail,
recordId
);
return { ...returnObject, assignmentReason };
}
}
async function handleAccountLookupFieldReason(routingFormResponseId: number, teamMemberEmail: string) {
async function handleAccountLookupFieldReason(
routingFormResponseId: number,
teamMemberEmail: string,
recordId?: string
) {
const routingFormResponse = await prisma.app_RoutingForms_FormResponse.findUnique({
where: {
id: routingFormResponseId,
@@ -67,6 +92,8 @@ async function handleAccountLookupFieldReason(routingFormResponseId: number, tea
const accountLookupFieldName = salesforceConfig?.rrSKipToAccountLookupFieldName;
return accountLookupFieldName
? `Salesforce account lookup field: ${accountLookupFieldName} - ${teamMemberEmail}`
? `Salesforce account lookup field: ${accountLookupFieldName} - ${teamMemberEmail}${
recordId ? ` (Account ID: ${recordId})` : ""
}`
: undefined;
}
@@ -16,7 +16,7 @@ const routingFormBookingFormHandler = async (
!salesforceSettings.rrSkipToAccountLookupField ||
!salesforceSettings.rrSKipToAccountLookupFieldName
)
return { email: null, recordType: null };
return { email: null, recordType: null, recordId: null };
const appData = await EventTypeService.getEventTypeAppDataFromId(eventTypeId, "salesforce");
@@ -24,7 +24,7 @@ const routingFormBookingFormHandler = async (
const credential = await CredentialRepository.findFirstByIdWithKeyAndUser({ id: credentialId });
if (!credential) return { email: null, recordType: null };
if (!credential) return { email: null, recordType: null, recordId: null };
const crm = new SalesforceCRMService(credential, {});
@@ -34,7 +34,11 @@ const routingFormBookingFormHandler = async (
SalesforceRecordEnum.ACCOUNT
);
return { email: userLookupEmail?.email ?? null, recordType: RoutingReasons.ACCOUNT_LOOKUP_FIELD as string };
return {
email: userLookupEmail?.email ?? null,
recordType: RoutingReasons.ACCOUNT_LOOKUP_FIELD as string,
recordId: null,
};
};
export default routingFormBookingFormHandler;
+9 -1
View File
@@ -36,6 +36,7 @@ type StoreInitializeType = {
teamMemberEmail?: string | null;
crmOwnerRecordType?: string | null;
crmAppSlug?: string | null;
crmRecordId?: string | null;
isPlatform?: boolean;
allowUpdatingUrlParams?: boolean;
};
@@ -163,6 +164,7 @@ export type BookerStore = {
teamMemberEmail?: string | null;
crmOwnerRecordType?: string | null;
crmAppSlug?: string | null;
crmRecordId?: string | null;
isPlatform?: boolean;
allowUpdatingUrlParams?: boolean;
};
@@ -306,6 +308,7 @@ export const useBookerStore = createWithEqualityFn<BookerStore>((set, get) => ({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
isPlatform = false,
allowUpdatingUrlParams = true,
}: StoreInitializeType) => {
@@ -324,7 +327,8 @@ export const useBookerStore = createWithEqualityFn<BookerStore>((set, get) => ({
get().rescheduledBy === rescheduledBy &&
get().teamMemberEmail === teamMemberEmail &&
get().crmOwnerRecordType === crmOwnerRecordType &&
get().crmAppSlug
get().crmAppSlug === crmAppSlug &&
get().crmRecordId === crmRecordId
)
return;
set({
@@ -347,6 +351,7 @@ export const useBookerStore = createWithEqualityFn<BookerStore>((set, get) => ({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
isPlatform,
allowUpdatingUrlParams,
});
@@ -456,6 +461,7 @@ export const useInitializeBookerStore = ({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
isPlatform = false,
allowUpdatingUrlParams = true,
}: StoreInitializeType) => {
@@ -479,6 +485,7 @@ export const useInitializeBookerStore = ({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
isPlatform,
allowUpdatingUrlParams,
});
@@ -501,6 +508,7 @@ export const useInitializeBookerStore = ({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
isPlatform,
allowUpdatingUrlParams,
]);
@@ -100,6 +100,7 @@ export interface BookerProps {
teamMemberEmail?: string | null;
crmOwnerRecordType?: string | null;
crmAppSlug?: string | null;
crmRecordId?: string | null;
areInstantMeetingParametersSet?: boolean | null;
userLocale?: string | null;
hasValidLicense?: boolean;
@@ -27,6 +27,7 @@ export type BookingOptions = {
teamMemberEmail?: string | null;
crmOwnerRecordType?: string | null;
crmAppSlug?: string | null;
crmRecordId?: string | null;
orgSlug?: string;
routingFormSearchParams?: RoutingFormSearchParams;
isDryRunProp?: boolean;
@@ -49,6 +50,7 @@ export const mapBookingToMutationInput = ({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
orgSlug,
routingFormSearchParams,
isDryRunProp,
@@ -86,6 +88,7 @@ export const mapBookingToMutationInput = ({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
orgSlug,
routedTeamMemberIds,
routingFormResponseId,
@@ -378,6 +378,7 @@ export type PlatformParams = {
};
export type BookingHandlerInput = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
bookingData: Record<string, any>;
userId?: number;
// These used to come from headers but now we're passing them as params
@@ -597,6 +598,7 @@ async function handler(
});
const contactOwnerEmail = skipContactOwner ? null : contactOwnerFromReq;
const crmRecordId: string | undefined = reqBody.crmRecordId ?? undefined;
let routingFormResponse = null;
@@ -1397,6 +1399,7 @@ async function handler(
teamMemberEmail: contactOwnerEmail,
recordType: reqBody.crmOwnerRecordType,
routingFormResponseId,
recordId: crmRecordId,
});
} else if (routingFormResponseId && teamId) {
assignmentReason = await AssignmentReasonRecorder.routingFormRoute({
@@ -149,18 +149,25 @@ export default class AssignmentReasonRecorder {
teamMemberEmail,
recordType,
routingFormResponseId,
recordId,
}: {
bookingId: number;
crmAppSlug: string;
teamMemberEmail: string;
recordType: string;
routingFormResponseId: number;
recordId?: string;
}) {
const appAssignmentReasonHandler = (await import("./appAssignmentReasonHandler")).default;
const appHandler = appAssignmentReasonHandler[crmAppSlug];
if (!appHandler) return;
const crmRoutingReason = await appHandler({ recordType, teamMemberEmail, routingFormResponseId });
const crmRoutingReason = await appHandler({
recordType,
teamMemberEmail,
routingFormResponseId,
recordId,
});
if (!crmRoutingReason || !crmRoutingReason.assignmentReason) return;
@@ -5,10 +5,12 @@ type AppAssignmentReasonHandler = ({
recordType,
teamMemberEmail,
routingFormResponseId,
recordId,
}: {
recordType: string;
teamMemberEmail: string;
routingFormResponseId: number;
recordId?: string;
}) => Promise<{ assignmentReason: string | undefined; reasonEnum: AssignmentReasonEnum } | undefined>;
const appBookingFormHandler: Record<string, AppAssignmentReasonHandler> = {
@@ -21,7 +21,7 @@ interface EventData {
length: number;
}
const returnNullValue = { email: null, recordType: null, crmAppSlug: null };
const returnNullValue = { email: null, recordType: null, crmAppSlug: null, recordId: null };
async function findUserByEmailWhoIsAHostOfEventType({
email,
@@ -57,7 +57,7 @@ async function getAttributeRoutingConfig(
if ("route" in data) {
return data.route.attributeRoutingConfig ?? null;
}
const { routingFormResponseId, eventTypeId } = data;
const { routingFormResponseId } = data;
const routingFormResponseQuery = await prisma.app_RoutingForms_FormResponse.findUnique({
where: {
@@ -117,7 +117,12 @@ function getEnabledRoutingFormAppSlugFromQuery(query: ParsedUrlQuery) {
async function getOwnerEmailFromCrm(
eventData: EventData,
email: string
): Promise<{ email: string | null; recordType: string | null; crmAppSlug: string | null }> {
): Promise<{
email: string | null;
recordType: string | null;
crmAppSlug: string | null;
recordId: string | null;
}> {
const crmContactOwner = await getCRMContactOwnerForRRLeadSkip(email, eventData.metadata);
if (!crmContactOwner?.email) return returnNullValue;
@@ -159,7 +164,7 @@ async function getTeamMemberEmailUsingRoutingFormHandler({
attributeRoutingConfig: AttributeRoutingConfig | null;
crmAppSlug: string;
}) {
const nullReturnValue = { email: null, skipContactOwner: false, recordType: "" };
const nullReturnValue = { email: null, skipContactOwner: false, recordType: "", recordId: "" };
if (!attributeRoutingConfig) return nullReturnValue;
@@ -171,7 +176,11 @@ async function getTeamMemberEmailUsingRoutingFormHandler({
const appHandler = appBookingFormHandler[crmAppSlug];
if (!appHandler) return nullReturnValue;
const { email: userEmail, recordType } = await appHandler(bookerEmail, attributeRoutingConfig, eventTypeId);
const {
email: userEmail,
recordType,
recordId,
} = await appHandler(bookerEmail, attributeRoutingConfig, eventTypeId);
if (!userEmail) return nullReturnValue;
@@ -180,7 +189,7 @@ async function getTeamMemberEmailUsingRoutingFormHandler({
if (!userQuery) return nullReturnValue;
return { ...nullReturnValue, email: userEmail, recordType };
return { ...nullReturnValue, email: userEmail, recordType, recordId };
}
async function getTeamMemberEmailForResponseOrContact({
@@ -198,7 +207,12 @@ async function getTeamMemberEmailForResponseOrContact({
*/
chosenRoute?: LocalRoute;
crmAppSlug?: string;
}) {
}): Promise<{
email: string | null;
recordType: string | null;
crmAppSlug: string | null;
recordId: string | null;
}> {
const eventTypeId = eventData.id;
if (eventData.schedulingType !== SchedulingType.ROUND_ROBIN) return returnNullValue;
@@ -215,15 +229,17 @@ async function getTeamMemberEmailForResponseOrContact({
safeStringify({ attributeRoutingConfigGetterData, crmAppSlug })
);
const attributeRoutingConfig = await getAttributeRoutingConfig(attributeRoutingConfigGetterData);
const { email, skipContactOwner, recordType } = await getTeamMemberEmailUsingRoutingFormHandler({
bookerEmail,
eventTypeId,
attributeRoutingConfig,
crmAppSlug,
});
const { email, skipContactOwner, recordType, recordId } = await getTeamMemberEmailUsingRoutingFormHandler(
{
bookerEmail,
eventTypeId,
attributeRoutingConfig,
crmAppSlug,
}
);
if (skipContactOwner) return returnNullValue;
if (email) return { email, recordType, crmAppSlug };
if (email) return { email, recordType, crmAppSlug, recordId };
} else {
log.debug("Getting the contact owner email from CRM");
return await getOwnerEmailFromCrm(eventData, bookerEmail);
@@ -240,7 +256,12 @@ export async function getTeamMemberEmailForResponseOrContactUsingUrlQuery({
query: ParsedUrlQuery;
eventData: EventData;
chosenRoute?: LocalRoute;
}) {
}): Promise<{
email: string | null;
recordType: string | null;
crmAppSlug: string | null;
recordId: string | null;
}> {
// Without email no lookup is possible
if (!query.email || typeof query.email !== "string") {
return returnNullValue;
@@ -158,6 +158,7 @@ export const BookerPlatformWrapper = (
teamMemberEmail,
crmAppSlug,
crmOwnerRecordType,
crmRecordId: props.crmRecordId,
eventId: event?.data?.id,
rescheduleUid: props.rescheduleUid ?? null,
bookingUid: props.bookingUid ?? null,
@@ -64,7 +64,6 @@ export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => {
// This event isn't processed by BookingPageTagManager because BookingPageTagManager hasn't loaded when it is fired. I think we should have a queue in fire method to handle this.
sdkActionManager?.fire("navigatedToBooker", {});
}, []);
useInitializeBookerStore({
...props,
eventId: props.entity.eventTypeId ?? event?.data?.id,
@@ -62,6 +62,7 @@ export const useHandleBookEvent = ({
const teamMemberEmail = useBookerStore((state) => state.teamMemberEmail);
const crmOwnerRecordType = useBookerStore((state) => state.crmOwnerRecordType);
const crmAppSlug = useBookerStore((state) => state.crmAppSlug);
const crmRecordId = useBookerStore((state) => state.crmRecordId);
const handleError = (err: any) => {
const errorMessage = err?.message ? t(err.message) : t("can_you_try_again");
showToast(errorMessage, "error");
@@ -108,6 +109,7 @@ export const useHandleBookEvent = ({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
crmRecordId,
orgSlug: orgSlug ? orgSlug : undefined,
routingFormSearchParams,
isDryRunProp: isBookingDryRun,
+1
View File
@@ -23,6 +23,7 @@ export const bookingCreateBodySchema = z.object({
orgSlug: z.string().optional(),
teamMemberEmail: z.string().nullish(),
crmOwnerRecordType: z.string().nullish(),
crmRecordId: z.string().nullish(),
routedTeamMemberIds: z.array(z.number()).nullish(),
routingFormResponseId: z.number().optional(),
skipContactOwner: z.boolean().optional(),