Skip to content

Commit b3d5a6e

Browse files
authored
feat: Salesforce - on booking cancel, write to event object (#20601)
* Type app options * Refactor `writeToPersonRecord` to `writeToRecord` * Abstract writing to salesforce record options component * Add option to write to event object on cancellation * Add on booking cancel write to event object to schema * Refactor writing to record on booking option to use `<WriteToObjectSettings />` * Pass event object to crm delete method * When cancelling booking write to event object - App data isn't being read * V1 pass app data to crm service * Undo .env commit * Fix passing event to cancel crm cancel event method * Pass event type metadata to `EventManager` in `handleCancelBooking` * Handle writing to event record * Type fix
1 parent 4753bd7 commit b3d5a6e

File tree

10 files changed

+425
-314
lines changed

10 files changed

+425
-314
lines changed

apps/web/public/static/locales/en/common.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3076,5 +3076,7 @@
30763076
"enable_delegation_credential_description": "Grant Cal.com automatic access to the calendars of all organization members by enabling delegation credential.",
30773077
"disable_delegation_credential": "Disable Delegation Credential",
30783078
"disable_delegation_credential_description": "Once delegation credential is disabled, organization members who haven’t connected their calendars will need to do so manually.",
3079+
"salesforce_on_cancel_write_to_event": "On cancelled booking, write to event record instead of deleting event",
3080+
"salesforce_on_every_cancellation": "On every cancellation",
30793081
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
30803082
}

packages/app-store/salesforce/components/EventTypeAppCardInterface.tsx

Lines changed: 25 additions & 248 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { usePathname } from "next/navigation";
22
import { useState } from "react";
3-
import type z from "zod";
43

54
import { useAppContextWithSchema } from "@calcom/app-store/EventTypeAppContext";
65
import AppCard from "@calcom/app-store/_components/AppCard";
@@ -16,13 +15,9 @@ import { Select } from "@calcom/ui/components/form";
1615
import { Switch } from "@calcom/ui/components/form";
1716
import { showToast } from "@calcom/ui/components/toast";
1817

19-
import {
20-
SalesforceRecordEnum,
21-
WhenToWriteToRecord,
22-
SalesforceFieldType,
23-
DateFieldTypeData,
24-
} from "../lib/enums";
25-
import type { appDataSchema, writeToRecordEntrySchema } from "../zod";
18+
import { SalesforceRecordEnum } from "../lib/enums";
19+
import type { appDataSchema } from "../zod";
20+
import WriteToObjectSettings, { BookingActionEnum } from "./components/WriteToObjectSettings";
2621

2722
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
2823
const pathname = usePathname();
@@ -48,6 +43,8 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
4843
const onBookingWriteToRecordFields = getAppData("onBookingWriteToRecordFields") ?? {};
4944
const ignoreGuests = getAppData("ignoreGuests") ?? false;
5045
const roundRobinSkipFallbackToLeadOwner = getAppData("roundRobinSkipFallbackToLeadOwner") ?? false;
46+
const onCancelWriteToEventRecord = getAppData("onCancelWriteToEventRecord") ?? false;
47+
const onCancelWriteToEventRecordFields = getAppData("onCancelWriteToEventRecordFields") ?? {};
5148

5249
const { t } = useLocale();
5350

@@ -60,49 +57,6 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
6057
recordOptions.find((option) => option.value === createEventOn) ?? recordOptions[0]
6158
);
6259

63-
const fieldTypeOptions = [
64-
{ label: t("text"), value: SalesforceFieldType.TEXT },
65-
{ label: t("date"), value: SalesforceFieldType.DATE },
66-
{ label: t("phone").charAt(0).toUpperCase() + t("phone").slice(1), value: SalesforceFieldType.PHONE },
67-
{ label: t("checkbox"), value: SalesforceFieldType.CHECKBOX },
68-
{ label: t("picklist"), value: SalesforceFieldType.PICKLIST },
69-
{ label: t("custom"), value: SalesforceFieldType.CUSTOM },
70-
];
71-
72-
const [writeToPersonObjectFieldType, setWriteToPersonObjectFieldType] = useState(fieldTypeOptions[0]);
73-
74-
const whenToWriteToRecordOptions = [
75-
{ label: t("on_every_booking"), value: WhenToWriteToRecord.EVERY_BOOKING },
76-
{ label: t("only_if_field_is_empty"), value: WhenToWriteToRecord.FIELD_EMPTY },
77-
];
78-
79-
const [whenToWriteToPersonRecord, setWhenToWriteToPersonRecord] = useState(
80-
whenToWriteToRecordOptions.find((option) => option.value === WhenToWriteToRecord.FIELD_EMPTY) ??
81-
whenToWriteToRecordOptions[0]
82-
);
83-
84-
const dateFieldValueOptions = [
85-
{ label: t("booking_start_date"), value: DateFieldTypeData.BOOKING_START_DATE },
86-
{ label: t("booking_created_date"), value: DateFieldTypeData.BOOKING_CREATED_DATE },
87-
];
88-
89-
const checkboxFieldValueOptions = [
90-
{ label: t("true"), value: true },
91-
{ label: t("false"), value: false },
92-
];
93-
94-
const [dateFieldValue, setDateValue] = useState(dateFieldValueOptions[0]);
95-
const [checkboxFieldValue, setCheckboxFieldValue] = useState(checkboxFieldValueOptions[0]);
96-
97-
const [newOnBookingWriteToPersonObjectField, setNewOnBookingWriteToPersonObjectField] = useState<
98-
z.infer<typeof writeToRecordEntrySchema>
99-
>({
100-
field: "",
101-
fieldType: writeToPersonObjectFieldType.value,
102-
value: "",
103-
whenToWrite: WhenToWriteToRecord.FIELD_EMPTY,
104-
});
105-
10660
const checkOwnerOptions = [
10761
{ label: t("contact"), value: SalesforceRecordEnum.CONTACT },
10862
{ label: t("salesforce_lead"), value: SalesforceRecordEnum.LEAD },
@@ -306,206 +260,16 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
306260
</div>
307261

308262
<div className="mt-4">
309-
<Switch
310-
label={t("salesforce_on_booking_write_to_record", { record: createEventOn })}
311-
labelOnLeading
312-
checked={onBookingWriteToRecord}
313-
onCheckedChange={(checked) => {
263+
<WriteToObjectSettings
264+
bookingAction={BookingActionEnum.ON_BOOKING}
265+
optionLabel={t("salesforce_on_booking_write_to_record", { record: createEventOn })}
266+
optionEnabled={onBookingWriteToRecord}
267+
writeToObjectData={onBookingWriteToRecordFields}
268+
optionSwitchOnChange={(checked) => {
314269
setAppData("onBookingWriteToRecord", checked);
315270
}}
271+
updateWriteToObjectData={(data) => setAppData("onBookingWriteToRecordFields", data)}
316272
/>
317-
{onBookingWriteToRecord ? (
318-
<div className="ml-2 mt-2">
319-
<div className="grid grid-cols-5 gap-4">
320-
<div>{t("field_name")}</div>
321-
<div>{t("field_type")}</div>
322-
<div>{t("value")}</div>
323-
<div>{t("when_to_write")}</div>
324-
</div>
325-
<div>
326-
{Object.keys(onBookingWriteToRecordFields).map((key) => (
327-
<div className="mt-2 grid grid-cols-5 gap-4" key={key}>
328-
<div>
329-
<InputField value={key} readOnly />
330-
</div>
331-
<div>
332-
<Select
333-
value={fieldTypeOptions.find(
334-
(option) => option.value === onBookingWriteToRecordFields[key].fieldType
335-
)}
336-
isDisabled={true}
337-
/>
338-
</div>
339-
<div>
340-
{onBookingWriteToRecordFields[key].fieldType === SalesforceFieldType.DATE ? (
341-
<Select
342-
value={dateFieldValueOptions.find(
343-
(option) => option.value === onBookingWriteToRecordFields[key].value
344-
)}
345-
isDisabled={true}
346-
/>
347-
) : onBookingWriteToRecordFields[key].fieldType === SalesforceFieldType.CHECKBOX ? (
348-
<Select
349-
value={checkboxFieldValueOptions.find(
350-
(option) => option.value === onBookingWriteToRecordFields[key].value
351-
)}
352-
isDisabled={true}
353-
/>
354-
) : (
355-
<InputField value={onBookingWriteToRecordFields[key].value as string} readOnly />
356-
)}
357-
</div>
358-
<div>
359-
<Select
360-
value={whenToWriteToRecordOptions.find(
361-
(option) => option.value === onBookingWriteToRecordFields[key].whenToWrite
362-
)}
363-
isDisabled={true}
364-
/>
365-
</div>
366-
<div>
367-
<Button
368-
StartIcon="trash"
369-
variant="icon"
370-
color="destructive"
371-
onClick={() => {
372-
const newObject = onBookingWriteToRecordFields;
373-
delete onBookingWriteToRecordFields[key];
374-
setAppData("onBookingWriteToRecordFields", newObject);
375-
}}
376-
/>
377-
</div>
378-
</div>
379-
))}
380-
<div className="mt-2 grid grid-cols-5 gap-4">
381-
<div>
382-
<InputField
383-
value={newOnBookingWriteToPersonObjectField.field}
384-
onChange={(e) =>
385-
setNewOnBookingWriteToPersonObjectField({
386-
...newOnBookingWriteToPersonObjectField,
387-
field: e.target.value,
388-
})
389-
}
390-
/>
391-
</div>
392-
<div>
393-
<Select
394-
options={fieldTypeOptions}
395-
value={writeToPersonObjectFieldType}
396-
onChange={(e) => {
397-
if (e) {
398-
setWriteToPersonObjectFieldType(e);
399-
setNewOnBookingWriteToPersonObjectField({
400-
...newOnBookingWriteToPersonObjectField,
401-
fieldType: e.value,
402-
...(e.value === SalesforceFieldType.DATE && { value: dateFieldValue.value }),
403-
...(e.value === SalesforceFieldType.CHECKBOX && {
404-
value: checkboxFieldValue.value,
405-
}),
406-
});
407-
}
408-
}}
409-
/>
410-
</div>
411-
<div>
412-
{writeToPersonObjectFieldType.value === SalesforceFieldType.DATE ? (
413-
<Select
414-
options={dateFieldValueOptions}
415-
value={dateFieldValue}
416-
onChange={(e) => {
417-
if (e) {
418-
setDateValue(e);
419-
setNewOnBookingWriteToPersonObjectField({
420-
...newOnBookingWriteToPersonObjectField,
421-
value: e.value,
422-
});
423-
}
424-
}}
425-
/>
426-
) : writeToPersonObjectFieldType.value === SalesforceFieldType.CHECKBOX ? (
427-
<Select
428-
options={checkboxFieldValueOptions}
429-
value={checkboxFieldValue}
430-
onChange={(e) => {
431-
if (e) {
432-
setCheckboxFieldValue(e);
433-
setNewOnBookingWriteToPersonObjectField({
434-
...newOnBookingWriteToPersonObjectField,
435-
value: e.value,
436-
});
437-
}
438-
}}
439-
/>
440-
) : (
441-
<InputField
442-
value={newOnBookingWriteToPersonObjectField.value as string}
443-
onChange={(e) =>
444-
setNewOnBookingWriteToPersonObjectField({
445-
...newOnBookingWriteToPersonObjectField,
446-
value: e.target.value,
447-
})
448-
}
449-
/>
450-
)}
451-
</div>
452-
<div>
453-
<Select
454-
options={whenToWriteToRecordOptions}
455-
value={whenToWriteToPersonRecord}
456-
onChange={(e) => {
457-
if (e) {
458-
setWhenToWriteToPersonRecord(e);
459-
setNewOnBookingWriteToPersonObjectField({
460-
...newOnBookingWriteToPersonObjectField,
461-
whenToWrite: e.value,
462-
});
463-
}
464-
}}
465-
/>
466-
</div>
467-
</div>
468-
</div>
469-
<Button
470-
className="mt-2"
471-
size="sm"
472-
disabled={
473-
!(
474-
newOnBookingWriteToPersonObjectField.field &&
475-
newOnBookingWriteToPersonObjectField.fieldType &&
476-
newOnBookingWriteToPersonObjectField.value !== "" &&
477-
newOnBookingWriteToPersonObjectField.whenToWrite
478-
)
479-
}
480-
onClick={() => {
481-
if (
482-
Object.keys(onBookingWriteToRecordFields).includes(
483-
newOnBookingWriteToEventObjectField.field.trim()
484-
)
485-
) {
486-
showToast("Field already exists", "error");
487-
return;
488-
}
489-
490-
setAppData("onBookingWriteToRecordFields", {
491-
...onBookingWriteToRecordFields,
492-
[newOnBookingWriteToPersonObjectField.field.trim()]: {
493-
fieldType: newOnBookingWriteToPersonObjectField.fieldType,
494-
value: newOnBookingWriteToPersonObjectField.value,
495-
whenToWrite: newOnBookingWriteToPersonObjectField.whenToWrite,
496-
},
497-
});
498-
setNewOnBookingWriteToPersonObjectField({
499-
field: "",
500-
fieldType: writeToPersonObjectFieldType.value,
501-
value: "",
502-
whenToWrite: WhenToWriteToRecord.FIELD_EMPTY,
503-
});
504-
}}>
505-
{t("add_new_field")}
506-
</Button>
507-
</div>
508-
) : null}
509273
</div>
510274

511275
<div className="mt-4">
@@ -588,6 +352,19 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
588352
</div>
589353
) : null}
590354

355+
<div className="mt-4">
356+
<WriteToObjectSettings
357+
bookingAction={BookingActionEnum.ON_CANCEL}
358+
optionLabel={t("salesforce_on_cancel_write_to_event")}
359+
optionEnabled={onCancelWriteToEventRecord}
360+
writeToObjectData={onCancelWriteToEventRecordFields}
361+
optionSwitchOnChange={(checked) => {
362+
setAppData("onCancelWriteToEventRecord", checked);
363+
}}
364+
updateWriteToObjectData={(data) => setAppData("onCancelWriteToEventRecordFields", data)}
365+
/>
366+
</div>
367+
591368
<div className="ml-2 mt-4">
592369
<Switch
593370
label="Send no show attendee data to event object"

0 commit comments

Comments
 (0)