refactor: v2 default booking fields (#18287)
* feat: allow title,notes,reschedule reason inputs * refactor: v2 event type default booking fields as in web app * wip: input validation * input field validation * fix: tests * chore: bump libraries * remove change in docs * fix input type * chore: ensure additional notes at the end * fixup! Merge branch 'main' into lauris/cal-4932-platform-refactor-default-booking-fields --------- Co-authored-by: Morgan Vernay <morgan@cal.com> Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
This commit is contained in:
co-authored by
Morgan Vernay
Morgan
parent
657f9dfd41
commit
c8172ab790
@@ -29,7 +29,7 @@
|
||||
"@axiomhq/winston": "^1.2.0",
|
||||
"@calcom/platform-constants": "*",
|
||||
"@calcom/platform-enums": "*",
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.78",
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.79",
|
||||
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2",
|
||||
"@calcom/platform-types": "*",
|
||||
"@calcom/platform-utils": "*",
|
||||
|
||||
@@ -952,7 +952,10 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
slug: recurringSeatedTventTypeSlug,
|
||||
});
|
||||
expect(firstBooking.attendees.length).toEqual(2);
|
||||
expect(firstBooking.attendees[0]).toEqual({
|
||||
const firstAttendee = firstBooking.attendees.find(
|
||||
(attendee) => attendee.name === nameAttendeeOne
|
||||
);
|
||||
expect(firstAttendee).toEqual({
|
||||
name: nameAttendeeOne,
|
||||
email: emailAttendeeOne,
|
||||
timeZone: body.attendee.timeZone,
|
||||
@@ -965,7 +968,10 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
},
|
||||
metadata: createdRecurringSeatedBooking[0].attendees[0].metadata,
|
||||
});
|
||||
expect(firstBooking.attendees[1]).toEqual({
|
||||
const secondAttendee = firstBooking.attendees.find(
|
||||
(attendee) => attendee.name === nameAttendeeTwo
|
||||
);
|
||||
expect(secondAttendee).toEqual({
|
||||
name: nameAttendeeTwo,
|
||||
email: emailAttendeeTwo,
|
||||
timeZone: body.attendee.timeZone,
|
||||
|
||||
+164
-84
@@ -33,11 +33,18 @@ import {
|
||||
ApiSuccessResponse,
|
||||
CreateEventTypeInput_2024_06_14,
|
||||
EventTypeOutput_2024_06_14,
|
||||
NameFieldInput_2024_06_14,
|
||||
NameDefaultFieldInput_2024_06_14,
|
||||
NotesDefaultFieldInput_2024_06_14,
|
||||
UpdateEventTypeInput_2024_06_14,
|
||||
} from "@calcom/platform-types";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
|
||||
const orderBySlug = (a: { slug: string }, b: { slug: string }) => {
|
||||
if (a.slug < b.slug) return -1;
|
||||
if (a.slug > b.slug) return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
describe("Event types Endpoints", () => {
|
||||
describe("Not authenticated", () => {
|
||||
let app: INestApplication;
|
||||
@@ -93,6 +100,68 @@ describe("Event types Endpoints", () => {
|
||||
let orgUserEventType1: EventType;
|
||||
let orgUserEventType2: EventType;
|
||||
let orgUserEventType3: EventType;
|
||||
|
||||
const defaultResponseBookingFieldName = {
|
||||
isDefault: true,
|
||||
type: "name",
|
||||
slug: "name",
|
||||
required: true,
|
||||
disableOnPrefill: false,
|
||||
};
|
||||
|
||||
const defaultResponseBookingFieldEmail = {
|
||||
isDefault: true,
|
||||
type: "email",
|
||||
slug: "email",
|
||||
required: true,
|
||||
disableOnPrefill: false,
|
||||
};
|
||||
|
||||
const defaultResponseBookingFieldLocation = {
|
||||
isDefault: true,
|
||||
type: "radioInput",
|
||||
slug: "location",
|
||||
required: false,
|
||||
hidden: false,
|
||||
disableOnPrefill: false,
|
||||
};
|
||||
|
||||
const defaultResponseBookingFieldTitle = {
|
||||
isDefault: true,
|
||||
type: "text",
|
||||
slug: "title",
|
||||
required: true,
|
||||
hidden: true,
|
||||
disableOnPrefill: false,
|
||||
};
|
||||
|
||||
const defaultResponseBookingFieldNotes = {
|
||||
isDefault: true,
|
||||
type: "textarea",
|
||||
slug: "notes",
|
||||
required: false,
|
||||
hidden: false,
|
||||
disableOnPrefill: false,
|
||||
};
|
||||
|
||||
const defaultResponseBookingFieldGuests = {
|
||||
isDefault: true,
|
||||
type: "multiemail",
|
||||
slug: "guests",
|
||||
required: false,
|
||||
hidden: false,
|
||||
disableOnPrefill: false,
|
||||
};
|
||||
|
||||
const defaultResponseBookingFieldRescheduleReason = {
|
||||
isDefault: true,
|
||||
type: "textarea",
|
||||
slug: "rescheduleReason",
|
||||
required: false,
|
||||
hidden: false,
|
||||
disableOnPrefill: false,
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await withApiAuth(
|
||||
userEmail,
|
||||
@@ -253,7 +322,7 @@ describe("Event types Endpoints", () => {
|
||||
});
|
||||
|
||||
it("should create an event type", async () => {
|
||||
const nameBookingField: NameFieldInput_2024_06_14 = {
|
||||
const nameBookingField: NameDefaultFieldInput_2024_06_14 = {
|
||||
type: "name",
|
||||
label: "Your name sir / madam",
|
||||
placeholder: "john doe",
|
||||
@@ -379,30 +448,25 @@ describe("Event types Endpoints", () => {
|
||||
);
|
||||
expect(createdEventType.color).toEqual(body.color);
|
||||
|
||||
const requestBookingFields = body.bookingFields || [];
|
||||
const expectedBookingFields = [
|
||||
{ isDefault: true, required: true, slug: "name", ...nameBookingField },
|
||||
{ isDefault: true, required: true, slug: "email", type: "email", disableOnPrefill: false },
|
||||
// note(Lauris): location booking field is added if multiple locations are passed
|
||||
{ ...defaultResponseBookingFieldName, ...nameBookingField },
|
||||
{ ...defaultResponseBookingFieldEmail },
|
||||
{ ...defaultResponseBookingFieldLocation },
|
||||
{ ...defaultResponseBookingFieldTitle },
|
||||
{ ...defaultResponseBookingFieldGuests },
|
||||
{ ...defaultResponseBookingFieldRescheduleReason },
|
||||
{
|
||||
isDefault: true,
|
||||
required: false,
|
||||
slug: "location",
|
||||
type: "radioInput",
|
||||
disableOnPrefill: false,
|
||||
type: "select",
|
||||
label: "select which language you want to learn",
|
||||
slug: "select-language",
|
||||
required: true,
|
||||
placeholder: "select language",
|
||||
options: ["javascript", "python", "cobol"],
|
||||
disableOnPrefill: true,
|
||||
hidden: false,
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
required: false,
|
||||
slug: "rescheduleReason",
|
||||
type: "textarea",
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
...requestBookingFields
|
||||
.filter((field) => field.type !== "name" && field.type !== "email")
|
||||
.map((field) => ({ isDefault: false, ...field })),
|
||||
{ ...defaultResponseBookingFieldNotes },
|
||||
];
|
||||
|
||||
expect(createdEventType.bookingFields).toEqual(expectedBookingFields);
|
||||
@@ -432,7 +496,9 @@ describe("Event types Endpoints", () => {
|
||||
expect(fetchedEventType?.lengthInMinutes).toEqual(eventType.lengthInMinutes);
|
||||
expect(fetchedEventType?.lengthInMinutesOptions).toEqual(eventType.lengthInMinutesOptions);
|
||||
expect(fetchedEventType?.locations).toEqual(eventType.locations);
|
||||
expect(fetchedEventType?.bookingFields).toEqual(eventType.bookingFields);
|
||||
expect(fetchedEventType?.bookingFields.sort(orderBySlug)).toEqual(
|
||||
eventType.bookingFields.sort(orderBySlug).filter((f) => ("hidden" in f ? !f?.hidden : true))
|
||||
);
|
||||
expect(fetchedEventType?.ownerId).toEqual(user.id);
|
||||
expect(fetchedEventType.bookingLimitsCount).toEqual(eventType.bookingLimitsCount);
|
||||
expect(fetchedEventType.onlyShowFirstAvailableSlot).toEqual(eventType.onlyShowFirstAvailableSlot);
|
||||
@@ -455,7 +521,6 @@ describe("Event types Endpoints", () => {
|
||||
});
|
||||
|
||||
it(`/GET/event-types by username and orgSlug`, async () => {
|
||||
console.log(organization);
|
||||
const response = await request(app.getHttpServer())
|
||||
.get(`/api/v2/event-types?username=${orgUser.username}&orgSlug=${organization.slug}`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||
@@ -636,7 +701,6 @@ describe("Event types Endpoints", () => {
|
||||
.expect(201);
|
||||
|
||||
const createdEventType = createResponse.body.data;
|
||||
console.log("createdEventType: ", createdEventType);
|
||||
|
||||
expect(createdEventType).toMatchObject({
|
||||
id: expect.any(Number),
|
||||
@@ -779,13 +843,20 @@ describe("Event types Endpoints", () => {
|
||||
});
|
||||
|
||||
it("should update event type", async () => {
|
||||
const nameBookingField: NameFieldInput_2024_06_14 = {
|
||||
const nameBookingField: NameDefaultFieldInput_2024_06_14 = {
|
||||
type: "name",
|
||||
label: "Your name sir / madam",
|
||||
placeholder: "john doe",
|
||||
disableOnPrefill: true,
|
||||
};
|
||||
|
||||
const notesBookingField: NotesDefaultFieldInput_2024_06_14 = {
|
||||
slug: "notes",
|
||||
label: "lemme take some notes",
|
||||
placeholder: "write your notes here",
|
||||
required: true,
|
||||
};
|
||||
|
||||
const newTitle = "Coding class in Italian!";
|
||||
|
||||
const body: UpdateEventTypeInput_2024_06_14 = {
|
||||
@@ -804,6 +875,7 @@ describe("Event types Endpoints", () => {
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
notesBookingField,
|
||||
],
|
||||
bookingLimitsCount: {
|
||||
day: 4,
|
||||
@@ -866,21 +938,25 @@ describe("Event types Endpoints", () => {
|
||||
expect(updatedEventType.lengthInMinutes).toEqual(eventType.lengthInMinutes);
|
||||
expect(updatedEventType.locations).toEqual(eventType.locations);
|
||||
|
||||
const requestBookingFields = body.bookingFields || [];
|
||||
const expectedBookingFields = [
|
||||
{ isDefault: true, required: true, slug: "name", ...nameBookingField },
|
||||
{ isDefault: true, required: true, slug: "email", type: "email", disableOnPrefill: false },
|
||||
{ ...defaultResponseBookingFieldName, ...nameBookingField },
|
||||
{ ...defaultResponseBookingFieldEmail },
|
||||
{ ...defaultResponseBookingFieldLocation },
|
||||
{ ...defaultResponseBookingFieldTitle },
|
||||
{ ...defaultResponseBookingFieldGuests },
|
||||
{ ...defaultResponseBookingFieldRescheduleReason },
|
||||
{
|
||||
isDefault: true,
|
||||
required: false,
|
||||
slug: "rescheduleReason",
|
||||
type: "textarea",
|
||||
type: "select",
|
||||
label: "select which language you want to learn",
|
||||
slug: "select-language",
|
||||
required: true,
|
||||
placeholder: "select language",
|
||||
options: ["javascript", "python", "cobol"],
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
isDefault: false,
|
||||
},
|
||||
...requestBookingFields
|
||||
.filter((field) => field.type !== "name" && field.type !== "email")
|
||||
.map((field) => ({ isDefault: false, ...field })),
|
||||
{ ...defaultResponseBookingFieldNotes, ...notesBookingField },
|
||||
];
|
||||
|
||||
expect(updatedEventType.bookingFields).toEqual(expectedBookingFields);
|
||||
@@ -997,7 +1073,9 @@ describe("Event types Endpoints", () => {
|
||||
expect(fetchedEventType?.lengthInMinutes).toEqual(eventType.lengthInMinutes);
|
||||
expect(fetchedEventType?.lengthInMinutesOptions).toEqual(eventType.lengthInMinutesOptions);
|
||||
expect(fetchedEventType?.locations).toEqual(eventType.locations);
|
||||
expect(fetchedEventType?.bookingFields).toEqual(eventType.bookingFields);
|
||||
expect(fetchedEventType?.bookingFields.sort(orderBySlug)).toEqual(
|
||||
eventType.bookingFields.sort(orderBySlug).filter((f) => ("hidden" in f ? !f?.hidden : true))
|
||||
);
|
||||
expect(fetchedEventType?.ownerId).toEqual(user.id);
|
||||
expect(fetchedEventType.bookingLimitsCount).toEqual(eventType.bookingLimitsCount);
|
||||
expect(fetchedEventType.onlyShowFirstAvailableSlot).toEqual(eventType.onlyShowFirstAvailableSlot);
|
||||
@@ -1091,14 +1169,6 @@ describe("Event types Endpoints", () => {
|
||||
placeholder: "",
|
||||
},
|
||||
{ isDefault: true, required: true, slug: "title", type: "text", disableOnPrefill: false, hidden: true },
|
||||
{
|
||||
isDefault: true,
|
||||
required: false,
|
||||
slug: "notes",
|
||||
type: "textarea",
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
required: false,
|
||||
@@ -1116,6 +1186,14 @@ describe("Event types Endpoints", () => {
|
||||
hidden: false,
|
||||
},
|
||||
{ isDefault: true, type: "phone", slug: "attendeePhoneNumber", required: false, hidden: true },
|
||||
{
|
||||
isDefault: true,
|
||||
required: false,
|
||||
slug: "notes",
|
||||
type: "textarea",
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
];
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -1239,15 +1317,6 @@ describe("Event types Endpoints", () => {
|
||||
defaultLabel: "what_is_this_meeting_about",
|
||||
defaultPlaceholder: "",
|
||||
},
|
||||
{
|
||||
name: "notes",
|
||||
type: "textarea",
|
||||
sources: [{ id: "default", type: "default", label: "Default" }],
|
||||
editable: "system-but-optional",
|
||||
required: false,
|
||||
defaultLabel: "additional_notes",
|
||||
defaultPlaceholder: "share_additional_notes",
|
||||
},
|
||||
{
|
||||
name: "guests",
|
||||
type: "multiemail",
|
||||
@@ -1283,6 +1352,15 @@ describe("Event types Endpoints", () => {
|
||||
required: false,
|
||||
defaultLabel: "phone_number",
|
||||
},
|
||||
{
|
||||
name: "notes",
|
||||
type: "textarea",
|
||||
sources: [{ id: "default", type: "default", label: "Default" }],
|
||||
editable: "system-but-optional",
|
||||
required: false,
|
||||
defaultLabel: "additional_notes",
|
||||
defaultPlaceholder: "share_additional_notes",
|
||||
},
|
||||
],
|
||||
};
|
||||
const legacyEventType = await eventTypesRepositoryFixture.create(legacyEventTypeInput, user.id);
|
||||
@@ -1369,15 +1447,6 @@ describe("Event types Endpoints", () => {
|
||||
defaultLabel: "what_is_this_meeting_about",
|
||||
defaultPlaceholder: "",
|
||||
},
|
||||
{
|
||||
name: "notes",
|
||||
type: "textarea",
|
||||
sources: [{ id: "default", type: "default", label: "Default" }],
|
||||
editable: "system-but-optional",
|
||||
required: false,
|
||||
defaultLabel: "additional_notes",
|
||||
defaultPlaceholder: "share_additional_notes",
|
||||
},
|
||||
{
|
||||
name: "guests",
|
||||
type: "multiemail",
|
||||
@@ -1413,6 +1482,15 @@ describe("Event types Endpoints", () => {
|
||||
required: false,
|
||||
defaultLabel: "phone_number",
|
||||
},
|
||||
{
|
||||
name: "notes",
|
||||
type: "textarea",
|
||||
sources: [{ id: "default", type: "default", label: "Default" }],
|
||||
editable: "system-but-optional",
|
||||
required: false,
|
||||
defaultLabel: "additional_notes",
|
||||
defaultPlaceholder: "share_additional_notes",
|
||||
},
|
||||
],
|
||||
};
|
||||
const legacyEventType = await eventTypesRepositoryFixture.create(legacyEventTypeInput, user.id);
|
||||
@@ -1426,19 +1504,21 @@ describe("Event types Endpoints", () => {
|
||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14> = response.body;
|
||||
const fetchedEventType = responseBody.data;
|
||||
|
||||
expect(fetchedEventType.bookingFields).toEqual([
|
||||
...expectedReturnSystemFields,
|
||||
{
|
||||
isDefault: false,
|
||||
type: userDefinedBookingField.type,
|
||||
slug: userDefinedBookingField.name,
|
||||
label: userDefinedBookingField.label,
|
||||
required: userDefinedBookingField.required,
|
||||
placeholder: userDefinedBookingField.placeholder,
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
]);
|
||||
expect(fetchedEventType.bookingFields.sort(orderBySlug)).toEqual(
|
||||
[
|
||||
{
|
||||
isDefault: false,
|
||||
type: userDefinedBookingField.type,
|
||||
slug: userDefinedBookingField.name,
|
||||
label: userDefinedBookingField.label,
|
||||
required: userDefinedBookingField.required,
|
||||
placeholder: userDefinedBookingField.placeholder,
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
...expectedReturnSystemFields,
|
||||
].sort(orderBySlug)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1541,14 +1621,6 @@ describe("Event types Endpoints", () => {
|
||||
disableOnPrefill: false,
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
type: "textarea",
|
||||
slug: "notes",
|
||||
required: false,
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
type: "multiemail",
|
||||
@@ -1565,6 +1637,14 @@ describe("Event types Endpoints", () => {
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
type: "textarea",
|
||||
slug: "notes",
|
||||
required: false,
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
+38
-16
@@ -5,16 +5,19 @@ import { UserWithProfile } from "@/modules/users/users.repository";
|
||||
import { Injectable, BadRequestException } from "@nestjs/common";
|
||||
|
||||
import {
|
||||
transformBookingFieldsApiRequestToInternal,
|
||||
transformBookingFieldsApiToInternal,
|
||||
transformLocationsApiToInternal,
|
||||
transformIntervalLimitsApiToInternal,
|
||||
transformFutureBookingLimitsApiToInternal,
|
||||
transformRecurrenceApiToInternal,
|
||||
systemBeforeFieldName,
|
||||
systemBeforeFieldEmail,
|
||||
systemBeforeFieldLocation,
|
||||
systemAfterFieldTitle,
|
||||
systemAfterFieldNotes,
|
||||
systemAfterFieldGuests,
|
||||
systemAfterFieldRescheduleReason,
|
||||
EventTypeMetaDataSchema,
|
||||
systemBeforeFieldLocation,
|
||||
transformBookerLayoutsApiToInternal,
|
||||
transformConfirmationPolicyApiToInternal,
|
||||
transformEventColorsApiToInternal,
|
||||
@@ -217,27 +220,46 @@ export class InputEventTypesService_2024_06_14 {
|
||||
inputBookingFields: CreateEventTypeInput_2024_06_14["bookingFields"],
|
||||
hasMultipleLocations: boolean
|
||||
) {
|
||||
const customFields: (SystemField | CustomField)[] = inputBookingFields
|
||||
? transformBookingFieldsApiRequestToInternal(inputBookingFields)
|
||||
const internalFields: (SystemField | CustomField)[] = inputBookingFields
|
||||
? transformBookingFieldsApiToInternal(inputBookingFields)
|
||||
: [];
|
||||
const customFieldsWithoutNameEmail = customFields.filter(
|
||||
(field) => field.type !== "name" && field.type !== "email"
|
||||
const systemCustomFields = internalFields.filter((field) => !this.isUserCustomField(field));
|
||||
const userCustomFields = internalFields.filter((field) => this.isUserCustomField(field));
|
||||
|
||||
const systemCustomNameField = systemCustomFields?.find((field) => field.type === "name");
|
||||
const systemCustomEmailField = systemCustomFields?.find((field) => field.type === "email");
|
||||
const systemCustomTitleField = systemCustomFields?.find((field) => field.name === "title");
|
||||
const systemCustomNotesField = systemCustomFields?.find((field) => field.name === "notes");
|
||||
const systemCustomGuestsField = systemCustomFields?.find((field) => field.name === "guests");
|
||||
const systemCustomRescheduleReasonField = systemCustomFields?.find(
|
||||
(field) => field.name === "rescheduleReason"
|
||||
);
|
||||
const customNameField = customFields?.find((field) => field.type === "name");
|
||||
const customEmailField = customFields?.find((field) => field.type === "email");
|
||||
|
||||
const defaultFieldsBefore: (SystemField | CustomField)[] = [
|
||||
customNameField || systemBeforeFieldName,
|
||||
customEmailField || systemBeforeFieldEmail,
|
||||
systemCustomNameField || systemBeforeFieldName,
|
||||
systemCustomEmailField || systemBeforeFieldEmail,
|
||||
systemBeforeFieldLocation,
|
||||
];
|
||||
// note(Lauris): if event type has multiple locations then a radio button booking field has to be displayed to allow booker to pick location
|
||||
if (hasMultipleLocations) {
|
||||
defaultFieldsBefore.push(systemBeforeFieldLocation);
|
||||
}
|
||||
|
||||
const defaultFieldsAfter = [systemAfterFieldRescheduleReason];
|
||||
const defaultFieldsAfter = [
|
||||
systemCustomTitleField || systemAfterFieldTitle,
|
||||
systemCustomNotesField || systemAfterFieldNotes,
|
||||
systemCustomGuestsField || systemAfterFieldGuests,
|
||||
systemCustomRescheduleReasonField || systemAfterFieldRescheduleReason,
|
||||
];
|
||||
|
||||
return [...defaultFieldsBefore, ...customFieldsWithoutNameEmail, ...defaultFieldsAfter];
|
||||
return [...defaultFieldsBefore, ...userCustomFields, ...defaultFieldsAfter];
|
||||
}
|
||||
|
||||
isUserCustomField(field: SystemField | CustomField): field is CustomField {
|
||||
return (
|
||||
field.type !== "name" &&
|
||||
field.type !== "email" &&
|
||||
field.name !== "title" &&
|
||||
field.name !== "notes" &&
|
||||
field.name !== "guests" &&
|
||||
field.name !== "rescheduleReason"
|
||||
);
|
||||
}
|
||||
|
||||
transformInputIntervalLimits(inputBookingFields: CreateEventTypeInput_2024_06_14["bookingLimitsCount"]) {
|
||||
|
||||
+13
-1
@@ -242,7 +242,19 @@ export class OutputEventTypesService_2024_06_14 {
|
||||
}
|
||||
}
|
||||
|
||||
return [...transformBookingFieldsInternalToApi(knownBookingFields), ...unknownBookingFields];
|
||||
const fields = [...transformBookingFieldsInternalToApi(knownBookingFields), ...unknownBookingFields];
|
||||
|
||||
// ensure additional notes are always at the end
|
||||
return fields.sort((a, b) => {
|
||||
if (!a?.slug || !b?.slug) return 0;
|
||||
if (a.slug === "notes" && b.slug !== "notes") {
|
||||
return 1;
|
||||
}
|
||||
if (b.slug === "notes" && a.slug !== "notes") {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
getDefaultBookingFields(isOrgTeamEvent: boolean) {
|
||||
|
||||
+16
-34
@@ -677,27 +677,9 @@ describe("Organizations Event Types Endpoints", () => {
|
||||
const fetchedEventType = responseBody.data;
|
||||
|
||||
expect(fetchedEventType.bookingFields).toEqual([
|
||||
{
|
||||
isDefault: true,
|
||||
type: "name",
|
||||
slug: "name",
|
||||
required: true,
|
||||
disableOnPrefill: false,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
type: "email",
|
||||
slug: "email",
|
||||
required: true,
|
||||
disableOnPrefill: false,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
type: "phone",
|
||||
slug: "attendeePhoneNumber",
|
||||
required: false,
|
||||
hidden: true,
|
||||
},
|
||||
{ isDefault: true, required: true, slug: "name", type: "name", disableOnPrefill: false },
|
||||
{ isDefault: true, required: true, slug: "email", type: "email", disableOnPrefill: false },
|
||||
{ isDefault: true, type: "phone", slug: "attendeePhoneNumber", required: false, hidden: true },
|
||||
{
|
||||
isDefault: true,
|
||||
type: "radioInput",
|
||||
@@ -708,33 +690,33 @@ describe("Organizations Event Types Endpoints", () => {
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
type: "text",
|
||||
slug: "title",
|
||||
required: true,
|
||||
slug: "title",
|
||||
type: "text",
|
||||
disableOnPrefill: false,
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
type: "textarea",
|
||||
slug: "notes",
|
||||
required: false,
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
type: "multiemail",
|
||||
slug: "guests",
|
||||
required: false,
|
||||
type: "multiemail",
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
type: "textarea",
|
||||
slug: "rescheduleReason",
|
||||
required: false,
|
||||
slug: "rescheduleReason",
|
||||
type: "textarea",
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
required: false,
|
||||
slug: "notes",
|
||||
type: "textarea",
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
|
||||
@@ -7741,7 +7741,7 @@
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"NameFieldInput_2024_06_14": {
|
||||
"NameDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
@@ -7766,7 +7766,7 @@
|
||||
"placeholder"
|
||||
]
|
||||
},
|
||||
"EmailFieldInput_2024_06_14": {
|
||||
"EmailDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
@@ -7791,6 +7791,126 @@
|
||||
"placeholder"
|
||||
]
|
||||
},
|
||||
"TitleDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"example": "title",
|
||||
"description": "only allowed value for type is `title`"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both."
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&title=journey`, the title field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"slug"
|
||||
]
|
||||
},
|
||||
"NotesDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"example": "notes",
|
||||
"description": "only allowed value for type is `notes`"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both."
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `¬es=journey`, the notes field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"slug"
|
||||
]
|
||||
},
|
||||
"GuestsDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"example": "guests",
|
||||
"description": "only allowed value for type is `guests`"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both."
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&guests=bob@cal.com`, the guests field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"slug"
|
||||
]
|
||||
},
|
||||
"RescheduleReasonDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"example": "rescheduleReason",
|
||||
"description": "only allowed value for type is `rescheduleReason`"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both."
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&rescheduleReason=travel`, the rescheduleReason field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"slug"
|
||||
]
|
||||
},
|
||||
"BookerLayouts_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7921,10 +8041,22 @@
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NameFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/NameDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/EmailFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/EmailDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/TitleDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/NotesDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GuestsDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RescheduleReasonDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneFieldInput_2024_06_14"
|
||||
@@ -8475,20 +8607,19 @@
|
||||
"RescheduleReasonDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
"description": "This property is always true because it's a default field",
|
||||
"example": true
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"title",
|
||||
"notes",
|
||||
"guests"
|
||||
],
|
||||
"example": "rescheduleReason",
|
||||
"description": "only allowed value for type is `rescheduleReason`",
|
||||
"default": "rescheduleReason"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "textarea"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -8505,33 +8636,39 @@
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&rescheduleReason=busy`, the reschedule reason field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isDefault",
|
||||
"slug",
|
||||
"type",
|
||||
"required",
|
||||
"hidden",
|
||||
"disableOnPrefill"
|
||||
]
|
||||
},
|
||||
"TitleDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
"description": "This property is always true because it's a default field",
|
||||
"example": true
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"default": "title"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "text"
|
||||
"default": "textarea"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"slug",
|
||||
"isDefault",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"TitleDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"title",
|
||||
"notes",
|
||||
"guests"
|
||||
],
|
||||
"example": "title",
|
||||
"description": "only allowed value for type is `title`",
|
||||
"default": "title"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
@@ -8549,33 +8686,39 @@
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&title=masterclass`, the title field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isDefault",
|
||||
"slug",
|
||||
"type",
|
||||
"required",
|
||||
"hidden",
|
||||
"disableOnPrefill"
|
||||
]
|
||||
},
|
||||
"NotesDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
"description": "This property is always true because it's a default field",
|
||||
"example": true
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"default": "notes"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "textarea"
|
||||
"default": "text"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"slug",
|
||||
"isDefault",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"NotesDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"title",
|
||||
"notes",
|
||||
"guests"
|
||||
],
|
||||
"example": "notes",
|
||||
"description": "only allowed value for type is `notes`",
|
||||
"default": "notes"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
@@ -8593,33 +8736,39 @@
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `¬es=hello`, the notes field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isDefault",
|
||||
"slug",
|
||||
"type",
|
||||
"required",
|
||||
"hidden",
|
||||
"disableOnPrefill"
|
||||
]
|
||||
},
|
||||
"GuestsDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
"description": "This property is always true because it's a default field",
|
||||
"example": true
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"default": "guests"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "multiemail"
|
||||
"default": "textarea"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"slug",
|
||||
"isDefault",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"GuestsDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"title",
|
||||
"notes",
|
||||
"guests"
|
||||
],
|
||||
"example": "guests",
|
||||
"description": "only allowed value for type is `guests`",
|
||||
"default": "guests"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
@@ -8637,15 +8786,22 @@
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&guests=lauris@cal.com`, the guests field will be prefilled with this value and disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
"description": "This property is always true because it's a default field",
|
||||
"example": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "multiemail"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isDefault",
|
||||
"slug",
|
||||
"type",
|
||||
"required",
|
||||
"hidden",
|
||||
"disableOnPrefill"
|
||||
"isDefault",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"AddressFieldOutput_2024_06_14": {
|
||||
@@ -9776,10 +9932,22 @@
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NameFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/NameDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/EmailFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/EmailDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/TitleDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/NotesDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GuestsDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RescheduleReasonDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneFieldInput_2024_06_14"
|
||||
@@ -11353,10 +11521,22 @@
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NameFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/NameDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/EmailFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/EmailDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/TitleDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/NotesDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GuestsDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RescheduleReasonDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneFieldInput_2024_06_14"
|
||||
@@ -12178,10 +12358,22 @@
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NameFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/NameDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/EmailFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/EmailDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/TitleDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/NotesDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GuestsDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RescheduleReasonDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneFieldInput_2024_06_14"
|
||||
|
||||
@@ -7173,7 +7173,7 @@
|
||||
},
|
||||
"required": ["type"]
|
||||
},
|
||||
"NameFieldInput_2024_06_14": {
|
||||
"NameDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
@@ -7194,7 +7194,7 @@
|
||||
},
|
||||
"required": ["type", "label", "placeholder"]
|
||||
},
|
||||
"EmailFieldInput_2024_06_14": {
|
||||
"EmailDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
@@ -7215,6 +7215,118 @@
|
||||
},
|
||||
"required": ["type", "label", "placeholder"]
|
||||
},
|
||||
"TitleDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"example": "title",
|
||||
"description": "only allowed value for type is `title`"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both."
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&title=journey`, the title field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": ["slug"]
|
||||
},
|
||||
"NotesDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"example": "notes",
|
||||
"description": "only allowed value for type is `notes`"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both."
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `¬es=journey`, the notes field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": ["slug"]
|
||||
},
|
||||
"GuestsDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"example": "guests",
|
||||
"description": "only allowed value for type is `guests`"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both."
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&guests=bob@cal.com`, the guests field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": ["slug"]
|
||||
},
|
||||
"RescheduleReasonDefaultFieldInput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"example": "rescheduleReason",
|
||||
"description": "only allowed value for type is `rescheduleReason`"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both."
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&rescheduleReason=travel`, the rescheduleReason field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": ["slug"]
|
||||
},
|
||||
"BookerLayouts_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7324,10 +7436,22 @@
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NameFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/NameDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/EmailFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/EmailDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/TitleDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/NotesDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GuestsDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RescheduleReasonDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneFieldInput_2024_06_14"
|
||||
@@ -7837,20 +7961,13 @@
|
||||
"RescheduleReasonDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
"description": "This property is always true because it's a default field",
|
||||
"example": true
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"enum": ["name", "email", "title", "notes", "guests"],
|
||||
"example": "rescheduleReason",
|
||||
"description": "only allowed value for type is `rescheduleReason`",
|
||||
"default": "rescheduleReason"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "textarea"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -7867,26 +7984,29 @@
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&rescheduleReason=busy`, the reschedule reason field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": ["isDefault", "slug", "type", "required", "hidden", "disableOnPrefill"]
|
||||
},
|
||||
"TitleDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
"description": "This property is always true because it's a default field",
|
||||
"example": true
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"default": "title"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "text"
|
||||
"default": "textarea"
|
||||
}
|
||||
},
|
||||
"required": ["slug", "isDefault", "type"]
|
||||
},
|
||||
"TitleDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"enum": ["name", "email", "title", "notes", "guests"],
|
||||
"example": "title",
|
||||
"description": "only allowed value for type is `title`",
|
||||
"default": "title"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
@@ -7904,26 +8024,29 @@
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&title=masterclass`, the title field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": ["isDefault", "slug", "type", "required", "hidden", "disableOnPrefill"]
|
||||
},
|
||||
"NotesDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
"description": "This property is always true because it's a default field",
|
||||
"example": true
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"default": "notes"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "textarea"
|
||||
"default": "text"
|
||||
}
|
||||
},
|
||||
"required": ["slug", "isDefault", "type"]
|
||||
},
|
||||
"NotesDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"enum": ["name", "email", "title", "notes", "guests"],
|
||||
"example": "notes",
|
||||
"description": "only allowed value for type is `notes`",
|
||||
"default": "notes"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
@@ -7941,26 +8064,29 @@
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `¬es=hello`, the notes field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": ["isDefault", "slug", "type", "required", "hidden", "disableOnPrefill"]
|
||||
},
|
||||
"GuestsDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
"description": "This property is always true because it's a default field",
|
||||
"example": true
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"default": "guests"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "multiemail"
|
||||
"default": "textarea"
|
||||
}
|
||||
},
|
||||
"required": ["slug", "isDefault", "type"]
|
||||
},
|
||||
"GuestsDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"enum": ["name", "email", "title", "notes", "guests"],
|
||||
"example": "guests",
|
||||
"description": "only allowed value for type is `guests`",
|
||||
"default": "guests"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
@@ -7978,9 +8104,19 @@
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&guests=lauris@cal.com`, the guests field will be prefilled with this value and disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
"description": "This property is always true because it's a default field",
|
||||
"example": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "multiemail"
|
||||
}
|
||||
},
|
||||
"required": ["isDefault", "slug", "type", "required", "hidden", "disableOnPrefill"]
|
||||
"required": ["slug", "isDefault", "type"]
|
||||
},
|
||||
"AddressFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
@@ -8991,10 +9127,22 @@
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NameFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/NameDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/EmailFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/EmailDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/TitleDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/NotesDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GuestsDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RescheduleReasonDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneFieldInput_2024_06_14"
|
||||
@@ -10334,10 +10482,22 @@
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NameFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/NameDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/EmailFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/EmailDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/TitleDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/NotesDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GuestsDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RescheduleReasonDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneFieldInput_2024_06_14"
|
||||
@@ -11093,10 +11253,22 @@
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NameFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/NameDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/EmailFieldInput_2024_06_14"
|
||||
"$ref": "#/components/schemas/EmailDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/TitleDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/NotesDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GuestsDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RescheduleReasonDefaultFieldInput_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneFieldInput_2024_06_14"
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
} from "../internal-to-api/booking-fields";
|
||||
import {
|
||||
transformLocationsApiToInternal,
|
||||
transformBookingFieldsApiRequestToInternal,
|
||||
transformBookingFieldsApiToInternal,
|
||||
transformSelectOptionsApiToInternal,
|
||||
transformIntervalLimitsApiToInternal,
|
||||
transformFutureBookingLimitsApiToInternal,
|
||||
@@ -151,7 +151,7 @@ describe("transformLocationsApiToInternal", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
describe("transformBookingFieldsApiToInternal", () => {
|
||||
it("should transform name field", () => {
|
||||
const bookingField: InputBookingField_2024_06_14 = {
|
||||
type: "name",
|
||||
@@ -176,7 +176,7 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
|
||||
const expectedOutput: SystemField[] = [expectedField];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -200,7 +200,7 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -237,7 +237,7 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -274,7 +274,7 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -311,7 +311,7 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -348,7 +348,7 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -385,7 +385,7 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -424,7 +424,7 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -455,14 +455,13 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
],
|
||||
editable: "user",
|
||||
required: bookingField.required,
|
||||
placeholder: "",
|
||||
options: transformSelectOptionsApiToInternal(bookingField.options),
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -499,7 +498,7 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -530,14 +529,13 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
],
|
||||
editable: "user",
|
||||
required: bookingField.required,
|
||||
placeholder: "",
|
||||
options: transformSelectOptionsApiToInternal(bookingField.options),
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -568,14 +566,13 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
],
|
||||
editable: "user",
|
||||
required: bookingField.required,
|
||||
placeholder: "",
|
||||
options: transformSelectOptionsApiToInternal(bookingField.options),
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -611,7 +608,7 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
@@ -645,14 +642,13 @@ describe("transformBookingFieldsApiRequestToInternal", () => {
|
||||
],
|
||||
editable: "user",
|
||||
required: bookingField.required,
|
||||
placeholder: "",
|
||||
options: transformSelectOptionsApiToInternal(bookingField.options),
|
||||
disableOnPrefill,
|
||||
hidden: false,
|
||||
},
|
||||
];
|
||||
|
||||
const result = transformBookingFieldsApiRequestToInternal(input);
|
||||
const result = transformBookingFieldsApiToInternal(input);
|
||||
|
||||
expect(result).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
import type { InputBookingField_2024_06_14, OutputBookingField_2024_06_14 } from "@calcom/platform-types";
|
||||
import type {
|
||||
InputBookingField_2024_06_14,
|
||||
LocationDefaultFieldOutput_2024_06_14,
|
||||
PhoneDefaultFieldOutput_2024_06_14,
|
||||
MultiSelectFieldInput_2024_06_14,
|
||||
CheckboxGroupFieldInput_2024_06_14,
|
||||
RadioGroupFieldInput_2024_06_14,
|
||||
NameDefaultFieldInput_2024_06_14,
|
||||
EmailDefaultFieldInput_2024_06_14,
|
||||
TitleDefaultFieldInput_2024_06_14,
|
||||
NotesDefaultFieldInput_2024_06_14,
|
||||
GuestsDefaultFieldInput_2024_06_14,
|
||||
RescheduleReasonDefaultFieldInput_2024_06_14,
|
||||
} from "@calcom/platform-types";
|
||||
|
||||
import {
|
||||
systemBeforeFieldEmail,
|
||||
@@ -13,11 +26,14 @@ import {
|
||||
type SystemField,
|
||||
} from "../internal-to-api";
|
||||
|
||||
export function transformBookingFieldsApiRequestToInternal(
|
||||
inputBookingFields: InputBookingField_2024_06_14[]
|
||||
): (SystemField | CustomField)[] {
|
||||
const customBookingFields = inputBookingFields.map((field) => {
|
||||
const baseProperties = getBasePropertiesRequest(field);
|
||||
type InputBookingField =
|
||||
| InputBookingField_2024_06_14
|
||||
| LocationDefaultFieldOutput_2024_06_14
|
||||
| PhoneDefaultFieldOutput_2024_06_14;
|
||||
|
||||
export function transformBookingFieldsApiToInternal(bookingFields: InputBookingField[]) {
|
||||
const customBookingFields = bookingFields.map((field) => {
|
||||
const baseProperties = getBaseProperties(field);
|
||||
|
||||
const options =
|
||||
"options" in field && field.options ? transformSelectOptionsApiToInternal(field.options) : undefined;
|
||||
@@ -35,42 +51,12 @@ export function transformBookingFieldsApiRequestToInternal(
|
||||
return customBookingFields;
|
||||
}
|
||||
|
||||
function getBasePropertiesRequest(field: InputBookingField_2024_06_14): SystemField | CustomField {
|
||||
if (field.type === "name") {
|
||||
const systemName = { ...systemBeforeFieldName };
|
||||
if (systemName.variantsConfig?.variants?.fullName?.fields?.[0]) {
|
||||
systemName.variantsConfig.variants.fullName.fields[0].label = field.label;
|
||||
}
|
||||
|
||||
if (systemName.variantsConfig?.variants?.fullName?.fields?.[0]) {
|
||||
systemName.variantsConfig.variants.fullName.fields[0].placeholder = field.placeholder;
|
||||
}
|
||||
// note(Lauris): we attach top level label and placeholder for easier access when converting database event type
|
||||
// to v2 response event type even though form builder uses label and placeholder from variantsConfig.
|
||||
systemName.label = field.label;
|
||||
systemName.placeholder = field.placeholder;
|
||||
|
||||
return {
|
||||
...systemName,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
};
|
||||
}
|
||||
|
||||
if (field.type === "email") {
|
||||
return {
|
||||
...systemBeforeFieldEmail,
|
||||
label: field.label,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
};
|
||||
}
|
||||
|
||||
if (field.type === "boolean") {
|
||||
function getBaseProperties(field: InputBookingField): CustomField | SystemField {
|
||||
if (fieldIsSelect(field)) {
|
||||
return {
|
||||
name: field.slug,
|
||||
type: field.type,
|
||||
label: field.label,
|
||||
labelAsSafeHtml: `<p>${field.label}</p>\n`,
|
||||
sources: [
|
||||
{
|
||||
id: "user",
|
||||
@@ -82,88 +68,11 @@ function getBasePropertiesRequest(field: InputBookingField_2024_06_14): SystemFi
|
||||
editable: "user",
|
||||
required: field.required,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
hidden: "hidden" in field ? field.hidden : false,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: field.slug,
|
||||
type: field.type,
|
||||
label: field.label,
|
||||
sources: [
|
||||
{
|
||||
id: "user",
|
||||
type: "user",
|
||||
label: "User",
|
||||
fieldRequired: true,
|
||||
},
|
||||
],
|
||||
editable: "user",
|
||||
required: field.required,
|
||||
placeholder: "placeholder" in field && field.placeholder ? field.placeholder : "",
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
};
|
||||
}
|
||||
|
||||
export function transformSelectOptionsApiToInternal(options: string[]) {
|
||||
return options.map((option) => ({
|
||||
label: option,
|
||||
value: option,
|
||||
}));
|
||||
}
|
||||
|
||||
export function transformBookingFieldsApiResponseToInternal(bookingFields: OutputBookingField_2024_06_14[]) {
|
||||
const customBookingFields = bookingFields.map((field) => {
|
||||
const baseProperties = getBasePropertiesResponse(field);
|
||||
|
||||
const options =
|
||||
"options" in field && field.options ? transformSelectOptionsApiToInternal(field.options) : undefined;
|
||||
|
||||
if (!options) {
|
||||
return baseProperties;
|
||||
}
|
||||
|
||||
return {
|
||||
...baseProperties,
|
||||
options,
|
||||
};
|
||||
});
|
||||
|
||||
return customBookingFields;
|
||||
}
|
||||
|
||||
function getBasePropertiesResponse(field: OutputBookingField_2024_06_14) {
|
||||
if (field.type === "name") {
|
||||
const systemName = { ...systemBeforeFieldName };
|
||||
if (systemName.variantsConfig?.variants?.fullName?.fields?.[0]) {
|
||||
systemName.variantsConfig.variants.fullName.fields[0].label = field.label;
|
||||
}
|
||||
|
||||
if (systemName.variantsConfig?.variants?.fullName?.fields?.[0]) {
|
||||
systemName.variantsConfig.variants.fullName.fields[0].placeholder = field.placeholder;
|
||||
}
|
||||
// note(Lauris): we attach top level label and placeholder for easier access when converting database event type
|
||||
// to v2 response event type even though form builder uses label and placeholder from variantsConfig.
|
||||
systemName.label = field.label;
|
||||
systemName.placeholder = field.placeholder;
|
||||
|
||||
return {
|
||||
...systemName,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
};
|
||||
}
|
||||
|
||||
if (field.type === "email") {
|
||||
return {
|
||||
...systemBeforeFieldEmail,
|
||||
label: field.label,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
};
|
||||
}
|
||||
|
||||
if (field.slug === "location") {
|
||||
if (fieldIsDefaultSystemLocation(field)) {
|
||||
return {
|
||||
...systemBeforeFieldLocation,
|
||||
required: field.required,
|
||||
@@ -171,51 +80,80 @@ function getBasePropertiesResponse(field: OutputBookingField_2024_06_14) {
|
||||
};
|
||||
}
|
||||
|
||||
if (field.slug === "attendeePhoneNumber") {
|
||||
if (fieldIsDefaultAttendeePhone(field)) {
|
||||
return {
|
||||
...systemBeforeFieldPhone,
|
||||
required: field.required,
|
||||
};
|
||||
}
|
||||
|
||||
if (field.slug === "rescheduleReason") {
|
||||
if (fieldIsCustomSystemName(field)) {
|
||||
const systemName = { ...systemBeforeFieldName };
|
||||
if (systemName.variantsConfig?.variants?.fullName?.fields?.[0]) {
|
||||
systemName.variantsConfig.variants.fullName.fields[0].label = field.label;
|
||||
}
|
||||
|
||||
if (systemName.variantsConfig?.variants?.fullName?.fields?.[0]) {
|
||||
systemName.variantsConfig.variants.fullName.fields[0].placeholder = field.placeholder;
|
||||
}
|
||||
// note(Lauris): we attach top level label and placeholder for easier access when converting database event type
|
||||
// to v2 response event type even though form builder uses label and placeholder from variantsConfig.
|
||||
systemName.label = field.label;
|
||||
systemName.placeholder = field.placeholder;
|
||||
|
||||
return {
|
||||
...systemName,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
};
|
||||
}
|
||||
|
||||
if (fieldIsCustomSystemEmail(field)) {
|
||||
return {
|
||||
...systemBeforeFieldEmail,
|
||||
label: field.label,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
};
|
||||
}
|
||||
|
||||
if (fieldIsCustomSystemRescheduleReason(field)) {
|
||||
return {
|
||||
...systemAfterFieldRescheduleReason,
|
||||
required: field.required,
|
||||
hidden: field.hidden,
|
||||
required: !!field.required,
|
||||
hidden: !!field.hidden,
|
||||
label: field.label,
|
||||
placeholder: "placeholder" in field ? field.placeholder : "",
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
};
|
||||
}
|
||||
|
||||
if (field.slug === "title") {
|
||||
if (fieldIsCustomSystemTitle(field)) {
|
||||
return {
|
||||
...systemAfterFieldTitle,
|
||||
required: field.required,
|
||||
hidden: field.hidden,
|
||||
required: !!field.required,
|
||||
hidden: !!field.hidden,
|
||||
label: field.label,
|
||||
placeholder: "placeholder" in field ? field.placeholder : "",
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
};
|
||||
}
|
||||
|
||||
if (field.slug === "notes") {
|
||||
if (fieldIsCustomSystemNotes(field)) {
|
||||
return {
|
||||
...systemAfterFieldNotes,
|
||||
required: field.required,
|
||||
hidden: field.hidden,
|
||||
required: !!field.required,
|
||||
hidden: !!field.hidden,
|
||||
label: field.label,
|
||||
placeholder: "placeholder" in field ? field.placeholder : "",
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
};
|
||||
}
|
||||
|
||||
if (field.slug === "guests") {
|
||||
if (fieldIsCustomSystemGuests(field)) {
|
||||
return {
|
||||
...systemAfterFieldGuests,
|
||||
required: field.required,
|
||||
hidden: field.hidden,
|
||||
required: !!field.required,
|
||||
hidden: !!field.hidden,
|
||||
label: field.label,
|
||||
placeholder: "placeholder" in field ? field.placeholder : "",
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
@@ -237,7 +175,7 @@ function getBasePropertiesResponse(field: OutputBookingField_2024_06_14) {
|
||||
},
|
||||
],
|
||||
editable: "user",
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
};
|
||||
@@ -256,9 +194,63 @@ function getBasePropertiesResponse(field: OutputBookingField_2024_06_14) {
|
||||
},
|
||||
],
|
||||
editable: "user",
|
||||
required: "required" in field ? field.required : false,
|
||||
placeholder: "placeholder" in field && field.placeholder ? field.placeholder : "",
|
||||
disableOnPrefill: "disableOnPrefill" in field ? !!field.disableOnPrefill : false,
|
||||
hidden: "hidden" in field ? field.hidden : undefined,
|
||||
required: !!field.required,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
};
|
||||
}
|
||||
|
||||
function fieldIsCustomSystemName(field: InputBookingField): field is NameDefaultFieldInput_2024_06_14 {
|
||||
return "type" in field && field.type === "name";
|
||||
}
|
||||
|
||||
function fieldIsCustomSystemEmail(field: InputBookingField): field is EmailDefaultFieldInput_2024_06_14 {
|
||||
return "type" in field && field.type === "email";
|
||||
}
|
||||
|
||||
function fieldIsCustomSystemTitle(field: InputBookingField): field is TitleDefaultFieldInput_2024_06_14 {
|
||||
return "slug" in field && field.slug === "title";
|
||||
}
|
||||
|
||||
function fieldIsCustomSystemNotes(field: InputBookingField): field is NotesDefaultFieldInput_2024_06_14 {
|
||||
return "slug" in field && field.slug === "notes";
|
||||
}
|
||||
|
||||
function fieldIsCustomSystemGuests(field: InputBookingField): field is GuestsDefaultFieldInput_2024_06_14 {
|
||||
return "slug" in field && field.slug === "guests";
|
||||
}
|
||||
|
||||
function fieldIsCustomSystemRescheduleReason(
|
||||
field: InputBookingField
|
||||
): field is RescheduleReasonDefaultFieldInput_2024_06_14 {
|
||||
return "slug" in field && field.slug === "rescheduleReason";
|
||||
}
|
||||
|
||||
function fieldIsDefaultAttendeePhone(field: InputBookingField): field is PhoneDefaultFieldOutput_2024_06_14 {
|
||||
return "slug" in field && field.slug === "attendeePhoneNumber";
|
||||
}
|
||||
|
||||
function fieldIsDefaultSystemLocation(
|
||||
field: InputBookingField
|
||||
): field is LocationDefaultFieldOutput_2024_06_14 {
|
||||
return "slug" in field && field.slug === "location";
|
||||
}
|
||||
|
||||
function fieldIsSelect(
|
||||
field: InputBookingField
|
||||
): field is
|
||||
| CheckboxGroupFieldInput_2024_06_14
|
||||
| RadioGroupFieldInput_2024_06_14
|
||||
| MultiSelectFieldInput_2024_06_14 {
|
||||
return (
|
||||
"type" in field && (field.type === "checkbox" || field.type === "radio" || field.type === "multiselect")
|
||||
);
|
||||
}
|
||||
|
||||
export function transformSelectOptionsApiToInternal(options: string[]) {
|
||||
return options.map((option) => ({
|
||||
label: option,
|
||||
value: option,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
isDefault: true,
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
label: field.label,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
@@ -36,7 +36,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
isDefault: true,
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
label: field.label,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
@@ -46,7 +46,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
isDefault: true,
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
hidden: !!field.hidden,
|
||||
label: field.label,
|
||||
placeholder: field.placeholder,
|
||||
@@ -57,7 +57,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
isDefault: true,
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
hidden: !!field.hidden,
|
||||
label: field.label,
|
||||
placeholder: field.placeholder,
|
||||
@@ -68,7 +68,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
isDefault: true,
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
hidden: !!field.hidden,
|
||||
label: field.label,
|
||||
placeholder: field.placeholder,
|
||||
@@ -79,7 +79,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
isDefault: true,
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
hidden: !!field.hidden,
|
||||
label: field.label,
|
||||
placeholder: field.placeholder,
|
||||
@@ -90,7 +90,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
isDefault: true,
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
hidden: !!field.hidden,
|
||||
label: field.label,
|
||||
placeholder: field.placeholder,
|
||||
@@ -101,7 +101,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
isDefault: true,
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
hidden: !!field.hidden,
|
||||
};
|
||||
default:
|
||||
@@ -122,7 +122,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
@@ -133,7 +133,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
@@ -144,7 +144,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
@@ -155,7 +155,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
@@ -166,7 +166,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
@@ -177,7 +177,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
placeholder: field.placeholder,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
@@ -188,7 +188,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
};
|
||||
@@ -198,7 +198,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
placeholder: field.placeholder,
|
||||
options: field.options ? field.options.map((option) => option.value) : [],
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
@@ -210,7 +210,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
options: field.options ? field.options?.map((option) => option.value) : [],
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
@@ -221,7 +221,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
options: field.options ? field.options?.map((option) => option.value) : [],
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
@@ -232,7 +232,7 @@ export function transformBookingFieldsInternalToApi(
|
||||
type: field.type,
|
||||
slug: field.name,
|
||||
label: field.label,
|
||||
required: field.required,
|
||||
required: !!field.required,
|
||||
options: field.options ? field.options?.map((option) => option.value) : [],
|
||||
disableOnPrefill: !!field.disableOnPrefill,
|
||||
hidden: !!field.hidden,
|
||||
@@ -542,7 +542,7 @@ export const systemAfterFieldTitle: TitleSystemField = {
|
||||
name: "title",
|
||||
editable: "system-but-optional",
|
||||
required: true,
|
||||
hidden: false,
|
||||
hidden: true,
|
||||
defaultPlaceholder: "",
|
||||
sources: [
|
||||
{
|
||||
|
||||
+8
-6
@@ -2,7 +2,7 @@ import { defaultEvents } from "@calcom/lib/defaultEvents";
|
||||
import type { SystemField } from "@calcom/lib/event-types/transformers";
|
||||
import {
|
||||
transformLocationsApiToInternal,
|
||||
transformBookingFieldsApiResponseToInternal,
|
||||
transformBookingFieldsApiToInternal,
|
||||
systemBeforeFieldName,
|
||||
systemBeforeFieldEmail,
|
||||
systemBeforeFieldLocation,
|
||||
@@ -16,10 +16,10 @@ import {
|
||||
} from "@calcom/lib/event-types/transformers";
|
||||
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
|
||||
import type {
|
||||
CustomFieldOutput_2024_06_14,
|
||||
EmailDefaultFieldOutput_2024_06_14,
|
||||
EventTypeOutput_2024_06_14,
|
||||
InputLocation_2024_06_14,
|
||||
KnownBookingField_2024_06_14,
|
||||
NameDefaultFieldOutput_2024_06_14,
|
||||
TeamEventTypeOutput_2024_06_14,
|
||||
} from "@calcom/platform-types";
|
||||
@@ -329,7 +329,9 @@ function getBookingFields(
|
||||
bookingFields: EventTypeOutput_2024_06_14["bookingFields"],
|
||||
defaultFormValues: BookerPlatformWrapperAtomProps["defaultFormValues"] | undefined
|
||||
) {
|
||||
const transformedBookingFields = transformBookingFieldsApiResponseToInternal(bookingFields);
|
||||
const transformedBookingFields = transformBookingFieldsApiToInternal(
|
||||
bookingFields.filter((field) => isKnownField(field))
|
||||
);
|
||||
|
||||
const hasNameField = transformedBookingFields.some((field) => field.name === "name");
|
||||
const hasEmailField = transformedBookingFields.some((field) => field.name === "email");
|
||||
@@ -369,10 +371,10 @@ function getBookingFields(
|
||||
return eventTypeBookingFields.brand<"HAS_SYSTEM_FIELDS">().parse(fieldsWithSystem);
|
||||
}
|
||||
|
||||
function isCustomField(
|
||||
function isKnownField(
|
||||
field: EventTypeOutput_2024_06_14["bookingFields"][number]
|
||||
): field is CustomFieldOutput_2024_06_14 {
|
||||
return field.type !== "unknown" && !field.isDefault;
|
||||
): field is KnownBookingField_2024_06_14 {
|
||||
return field.type !== "unknown";
|
||||
}
|
||||
|
||||
function getBookingWindow(inputBookingWindow: EventTypeOutput_2024_06_14["bookingWindow"]) {
|
||||
|
||||
@@ -126,7 +126,7 @@ export { EventTypeMetaDataSchema, userMetadata, bookingMetadataSchema } from "@c
|
||||
|
||||
export {
|
||||
// note(Lauris): Api to internal
|
||||
transformBookingFieldsApiRequestToInternal,
|
||||
transformBookingFieldsApiToInternal,
|
||||
transformLocationsApiToInternal,
|
||||
transformIntervalLimitsApiToInternal,
|
||||
transformFutureBookingLimitsApiToInternal,
|
||||
@@ -155,6 +155,9 @@ export {
|
||||
systemBeforeFieldEmail,
|
||||
systemBeforeFieldLocation,
|
||||
systemAfterFieldRescheduleReason,
|
||||
systemAfterFieldTitle,
|
||||
systemAfterFieldNotes,
|
||||
systemAfterFieldGuests,
|
||||
} from "@calcom/lib/event-types/transformers";
|
||||
|
||||
export type {
|
||||
|
||||
+256
-26
@@ -21,9 +21,14 @@ const inputBookingFieldTypes = [
|
||||
"boolean",
|
||||
] as const;
|
||||
|
||||
export class NameFieldInput_2024_06_14 {
|
||||
const inputBookingFieldSlugs = ["name", "email", "title", "notes", "guests"] as const;
|
||||
|
||||
export class NameDefaultFieldInput_2024_06_14 {
|
||||
@IsIn(inputBookingFieldTypes)
|
||||
@DocsProperty({ example: "name", description: "only allowed value for type is `name`" })
|
||||
@DocsProperty({
|
||||
example: "name",
|
||||
description: "only allowed value for type is `name`",
|
||||
})
|
||||
type!: "name";
|
||||
|
||||
@IsString()
|
||||
@@ -48,9 +53,12 @@ export class NameFieldInput_2024_06_14 {
|
||||
disableOnPrefill?: boolean;
|
||||
}
|
||||
|
||||
export class EmailFieldInput_2024_06_14 {
|
||||
export class EmailDefaultFieldInput_2024_06_14 {
|
||||
@IsIn(inputBookingFieldTypes)
|
||||
@DocsProperty({ example: "email", description: "only allowed value for type is `email`" })
|
||||
@DocsProperty({
|
||||
example: "email",
|
||||
description: "only allowed value for type is `email`",
|
||||
})
|
||||
type!: "email";
|
||||
|
||||
@IsString()
|
||||
@@ -74,6 +82,170 @@ export class EmailFieldInput_2024_06_14 {
|
||||
})
|
||||
disableOnPrefill?: boolean;
|
||||
}
|
||||
|
||||
export class TitleDefaultFieldInput_2024_06_14 {
|
||||
@IsIn(inputBookingFieldSlugs)
|
||||
@DocsProperty({ example: "title", description: "only allowed value for type is `title`" })
|
||||
slug!: "title";
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
required?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional({
|
||||
description:
|
||||
"If true show under event type settings but don't show this booking field in the Booker. If false show in both.",
|
||||
})
|
||||
hidden?: boolean;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
label?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
placeholder?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional({
|
||||
type: Boolean,
|
||||
description:
|
||||
"Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value.\
|
||||
For example, if URL contains query parameter `&title=journey`,\
|
||||
the title field will be prefilled with this value and disabled.",
|
||||
})
|
||||
disableOnPrefill?: boolean;
|
||||
}
|
||||
|
||||
export class NotesDefaultFieldInput_2024_06_14 {
|
||||
@IsIn(inputBookingFieldSlugs)
|
||||
@DocsProperty({ example: "notes", description: "only allowed value for type is `notes`" })
|
||||
slug!: "notes";
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
required?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional({
|
||||
description:
|
||||
"If true show under event type settings but don't show this booking field in the Booker. If false show in both.",
|
||||
})
|
||||
hidden?: boolean;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
label?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
placeholder?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional({
|
||||
type: Boolean,
|
||||
description:
|
||||
"Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value.\
|
||||
For example, if URL contains query parameter `¬es=journey`,\
|
||||
the notes field will be prefilled with this value and disabled.",
|
||||
})
|
||||
disableOnPrefill?: boolean;
|
||||
}
|
||||
|
||||
export class GuestsDefaultFieldInput_2024_06_14 {
|
||||
@IsIn(inputBookingFieldSlugs)
|
||||
@DocsProperty({ example: "guests", description: "only allowed value for type is `guests`" })
|
||||
slug!: "guests";
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
required?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional({
|
||||
description:
|
||||
"If true show under event type settings but don't show this booking field in the Booker. If false show in both.",
|
||||
})
|
||||
hidden?: boolean;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
label?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
placeholder?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional({
|
||||
type: Boolean,
|
||||
description:
|
||||
"Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value.\
|
||||
For example, if URL contains query parameter `&guests=bob@cal.com`,\
|
||||
the guests field will be prefilled with this value and disabled.",
|
||||
})
|
||||
disableOnPrefill?: boolean;
|
||||
}
|
||||
|
||||
export class RescheduleReasonDefaultFieldInput_2024_06_14 {
|
||||
@IsIn(inputBookingFieldSlugs)
|
||||
@DocsProperty({
|
||||
example: "rescheduleReason",
|
||||
description: "only allowed value for type is `rescheduleReason`",
|
||||
})
|
||||
slug!: "rescheduleReason";
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
required?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional({
|
||||
description:
|
||||
"If true show under event type settings but don't show this booking field in the Booker. If false show in both.",
|
||||
})
|
||||
hidden?: boolean;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
label?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional()
|
||||
placeholder?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@DocsPropertyOptional({
|
||||
type: Boolean,
|
||||
description:
|
||||
"Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value.\
|
||||
For example, if URL contains query parameter `&rescheduleReason=travel`,\
|
||||
the rescheduleReason field will be prefilled with this value and disabled.",
|
||||
})
|
||||
disableOnPrefill?: boolean;
|
||||
}
|
||||
|
||||
export class PhoneFieldInput_2024_06_14 {
|
||||
@IsIn(inputBookingFieldTypes)
|
||||
@DocsProperty({ example: "phone", description: "only allowed value for type is `phone`" })
|
||||
@@ -576,9 +748,16 @@ export class BooleanFieldInput_2024_06_14 {
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
type InputDefaultField_2024_06_14 =
|
||||
| NameDefaultFieldInput_2024_06_14
|
||||
| EmailDefaultFieldInput_2024_06_14
|
||||
| TitleDefaultFieldInput_2024_06_14
|
||||
| NotesDefaultFieldInput_2024_06_14
|
||||
| GuestsDefaultFieldInput_2024_06_14
|
||||
| RescheduleReasonDefaultFieldInput_2024_06_14;
|
||||
|
||||
export type InputBookingField_2024_06_14 =
|
||||
| NameFieldInput_2024_06_14
|
||||
| EmailFieldInput_2024_06_14
|
||||
| InputDefaultField_2024_06_14
|
||||
| PhoneFieldInput_2024_06_14
|
||||
| AddressFieldInput_2024_06_14
|
||||
| TextFieldInput_2024_06_14
|
||||
@@ -593,9 +772,13 @@ export type InputBookingField_2024_06_14 =
|
||||
|
||||
@ValidatorConstraint({ async: true })
|
||||
class InputBookingFieldValidator_2024_06_14 implements ValidatorConstraintInterface {
|
||||
private classTypeMap: { [key: string]: new () => InputBookingField_2024_06_14 } = {
|
||||
name: NameFieldInput_2024_06_14,
|
||||
email: EmailFieldInput_2024_06_14,
|
||||
private classMap: { [key: string]: new () => InputBookingField_2024_06_14 } = {
|
||||
name: NameDefaultFieldInput_2024_06_14,
|
||||
email: EmailDefaultFieldInput_2024_06_14,
|
||||
title: TitleDefaultFieldInput_2024_06_14,
|
||||
notes: NotesDefaultFieldInput_2024_06_14,
|
||||
guests: GuestsDefaultFieldInput_2024_06_14,
|
||||
rescheduleReason: RescheduleReasonDefaultFieldInput_2024_06_14,
|
||||
phone: PhoneFieldInput_2024_06_14,
|
||||
address: AddressFieldInput_2024_06_14,
|
||||
text: TextFieldInput_2024_06_14,
|
||||
@@ -609,8 +792,6 @@ class InputBookingFieldValidator_2024_06_14 implements ValidatorConstraintInterf
|
||||
boolean: BooleanFieldInput_2024_06_14,
|
||||
};
|
||||
|
||||
private reservedSystemSlugs = ["location", "rescheduleReason", "attendeePhoneNumber"];
|
||||
|
||||
async validate(bookingFields: { type: string; slug: string }[]) {
|
||||
if (!Array.isArray(bookingFields)) {
|
||||
throw new BadRequestException(`'bookingFields' must be an array.`);
|
||||
@@ -623,20 +804,19 @@ class InputBookingFieldValidator_2024_06_14 implements ValidatorConstraintInterf
|
||||
const slugs: string[] = [];
|
||||
for (const field of bookingFields) {
|
||||
const { type, slug } = field;
|
||||
if (!type) {
|
||||
throw new BadRequestException(`Each booking field must have a 'type' property.`);
|
||||
}
|
||||
const fieldNeedsType =
|
||||
slug !== "title" && slug !== "notes" && slug !== "guests" && slug !== "rescheduleReason";
|
||||
|
||||
const notSystemEditableField = type !== "name" && type !== "email";
|
||||
if (notSystemEditableField && !slug) {
|
||||
throw new BadRequestException(`Each booking field must have a 'slug' property.`);
|
||||
}
|
||||
|
||||
if (this.reservedSystemSlugs.includes(slug)) {
|
||||
if (fieldNeedsType && !type) {
|
||||
throw new BadRequestException(
|
||||
`The slug '${slug}' is reserved and cannot be used, because it is a slug of a default booking field. Reserved slugs are: ${this.reservedSystemSlugs.join(
|
||||
", "
|
||||
)}`
|
||||
`All booking fields except ones with slug equal to title, notes, guests and rescheduleReason must have a 'type' property.`
|
||||
);
|
||||
}
|
||||
|
||||
const fieldNeedsSlug = type !== "name" && type !== "email";
|
||||
if (fieldNeedsSlug && !slug) {
|
||||
throw new BadRequestException(
|
||||
`Each booking field except ones with type equal to name and email must have a 'slug' property.`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -645,13 +825,15 @@ class InputBookingFieldValidator_2024_06_14 implements ValidatorConstraintInterf
|
||||
`Duplicate bookingFields slug '${slug}' found. All bookingFields slugs must be unique.`
|
||||
);
|
||||
}
|
||||
if (notSystemEditableField) {
|
||||
if (fieldNeedsSlug) {
|
||||
slugs.push(slug);
|
||||
}
|
||||
|
||||
const ClassType = this.classTypeMap[type];
|
||||
const ClassType = type ? this.classMap[type] : this.classMap[slug];
|
||||
if (!ClassType) {
|
||||
throw new BadRequestException(`Unsupported booking field type '${type}'.`);
|
||||
throw new BadRequestException(
|
||||
type ? `Unsupported booking field type '${type}'.` : `Unsupported booking field slug '${slug}'.`
|
||||
);
|
||||
}
|
||||
|
||||
const instance = plainToInstance(ClassType, field);
|
||||
@@ -682,3 +864,51 @@ export function ValidateInputBookingFields_2024_06_14(validationOptions?: Valida
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function isDefaultField(field: InputBookingField_2024_06_14): field is InputDefaultField_2024_06_14 {
|
||||
if (
|
||||
isDefaultNameField(field) ||
|
||||
isDefaultEmailField(field) ||
|
||||
isDefaultTitleField(field) ||
|
||||
isDefaultNotesField(field) ||
|
||||
isDefaultGuestsField(field) ||
|
||||
isDefaultRescheduleReasonField(field)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isDefaultNameField(field: InputBookingField_2024_06_14): field is NameDefaultFieldInput_2024_06_14 {
|
||||
return ("type" in field && field.type === "name") || ("slug" in field && field.slug === "name");
|
||||
}
|
||||
|
||||
function isDefaultEmailField(
|
||||
field: InputBookingField_2024_06_14
|
||||
): field is EmailDefaultFieldInput_2024_06_14 {
|
||||
return ("type" in field && field.type === "email") || ("slug" in field && field.slug === "email");
|
||||
}
|
||||
|
||||
function isDefaultTitleField(
|
||||
field: InputBookingField_2024_06_14
|
||||
): field is TitleDefaultFieldInput_2024_06_14 {
|
||||
return "slug" in field && field.slug === "title";
|
||||
}
|
||||
|
||||
function isDefaultNotesField(
|
||||
field: InputBookingField_2024_06_14
|
||||
): field is NotesDefaultFieldInput_2024_06_14 {
|
||||
return "slug" in field && field.slug === "notes";
|
||||
}
|
||||
|
||||
function isDefaultGuestsField(
|
||||
field: InputBookingField_2024_06_14
|
||||
): field is GuestsDefaultFieldInput_2024_06_14 {
|
||||
return "slug" in field && field.slug === "guests";
|
||||
}
|
||||
|
||||
function isDefaultRescheduleReasonField(
|
||||
field: InputBookingField_2024_06_14
|
||||
): field is RescheduleReasonDefaultFieldInput_2024_06_14 {
|
||||
return "slug" in field && field.slug === "rescheduleReason";
|
||||
}
|
||||
|
||||
+18
-6
@@ -26,16 +26,20 @@ import {
|
||||
AddressFieldInput_2024_06_14,
|
||||
BooleanFieldInput_2024_06_14,
|
||||
CheckboxGroupFieldInput_2024_06_14,
|
||||
EmailFieldInput_2024_06_14,
|
||||
EmailDefaultFieldInput_2024_06_14,
|
||||
GuestsDefaultFieldInput_2024_06_14,
|
||||
MultiEmailFieldInput_2024_06_14,
|
||||
MultiSelectFieldInput_2024_06_14,
|
||||
NameFieldInput_2024_06_14,
|
||||
NameDefaultFieldInput_2024_06_14,
|
||||
NotesDefaultFieldInput_2024_06_14,
|
||||
NumberFieldInput_2024_06_14,
|
||||
PhoneFieldInput_2024_06_14,
|
||||
RadioGroupFieldInput_2024_06_14,
|
||||
RescheduleReasonDefaultFieldInput_2024_06_14,
|
||||
SelectFieldInput_2024_06_14,
|
||||
TextAreaFieldInput_2024_06_14,
|
||||
TextFieldInput_2024_06_14,
|
||||
TitleDefaultFieldInput_2024_06_14,
|
||||
} from "./booking-fields.input";
|
||||
import type { InputBookingField_2024_06_14 } from "./booking-fields.input";
|
||||
import { ValidateInputBookingFields_2024_06_14 } from "./booking-fields.input";
|
||||
@@ -106,8 +110,12 @@ export const CREATE_EVENT_SLUG_EXAMPLE = "learn-the-secrets-of-masterchief";
|
||||
InputAttendeeAddressLocation_2024_06_14,
|
||||
InputAttendeePhoneLocation_2024_06_14,
|
||||
InputAttendeeDefinedLocation_2024_06_14,
|
||||
NameFieldInput_2024_06_14,
|
||||
EmailFieldInput_2024_06_14
|
||||
NameDefaultFieldInput_2024_06_14,
|
||||
EmailDefaultFieldInput_2024_06_14,
|
||||
TitleDefaultFieldInput_2024_06_14,
|
||||
NotesDefaultFieldInput_2024_06_14,
|
||||
GuestsDefaultFieldInput_2024_06_14,
|
||||
RescheduleReasonDefaultFieldInput_2024_06_14
|
||||
)
|
||||
export class CreateEventTypeInput_2024_06_14 {
|
||||
@IsInt()
|
||||
@@ -166,8 +174,12 @@ export class CreateEventTypeInput_2024_06_14 {
|
||||
description:
|
||||
"Custom fields that can be added to the booking form when the event is booked by someone. By default booking form has name and email field.",
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(NameFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(EmailFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(NameDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(EmailDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(TitleDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(NotesDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(GuestsDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(RescheduleReasonDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(PhoneFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(AddressFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(TextFieldInput_2024_06_14) },
|
||||
|
||||
+18
-6
@@ -24,16 +24,20 @@ import {
|
||||
AddressFieldInput_2024_06_14,
|
||||
BooleanFieldInput_2024_06_14,
|
||||
CheckboxGroupFieldInput_2024_06_14,
|
||||
EmailFieldInput_2024_06_14,
|
||||
EmailDefaultFieldInput_2024_06_14,
|
||||
GuestsDefaultFieldInput_2024_06_14,
|
||||
MultiEmailFieldInput_2024_06_14,
|
||||
MultiSelectFieldInput_2024_06_14,
|
||||
NameFieldInput_2024_06_14,
|
||||
NameDefaultFieldInput_2024_06_14,
|
||||
NotesDefaultFieldInput_2024_06_14,
|
||||
NumberFieldInput_2024_06_14,
|
||||
PhoneFieldInput_2024_06_14,
|
||||
RadioGroupFieldInput_2024_06_14,
|
||||
RescheduleReasonDefaultFieldInput_2024_06_14,
|
||||
SelectFieldInput_2024_06_14,
|
||||
TextAreaFieldInput_2024_06_14,
|
||||
TextFieldInput_2024_06_14,
|
||||
TitleDefaultFieldInput_2024_06_14,
|
||||
ValidateInputBookingFields_2024_06_14,
|
||||
} from "./booking-fields.input";
|
||||
import type { BookingLimitsCount_2024_06_14 } from "./booking-limits-count.input";
|
||||
@@ -104,8 +108,12 @@ import { Seats_2024_06_14 } from "./seats.input";
|
||||
InputAttendeeAddressLocation_2024_06_14,
|
||||
InputAttendeePhoneLocation_2024_06_14,
|
||||
InputAttendeeDefinedLocation_2024_06_14,
|
||||
NameFieldInput_2024_06_14,
|
||||
EmailFieldInput_2024_06_14
|
||||
NameDefaultFieldInput_2024_06_14,
|
||||
EmailDefaultFieldInput_2024_06_14,
|
||||
TitleDefaultFieldInput_2024_06_14,
|
||||
NotesDefaultFieldInput_2024_06_14,
|
||||
GuestsDefaultFieldInput_2024_06_14,
|
||||
RescheduleReasonDefaultFieldInput_2024_06_14
|
||||
)
|
||||
export class UpdateEventTypeInput_2024_06_14 {
|
||||
@IsOptional()
|
||||
@@ -167,8 +175,12 @@ export class UpdateEventTypeInput_2024_06_14 {
|
||||
description:
|
||||
"Custom fields that can be added to the booking form when the event is booked by someone. By default booking form has name and email field.",
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(NameFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(EmailFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(NameDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(EmailDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(TitleDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(NotesDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(GuestsDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(RescheduleReasonDefaultFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(PhoneFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(AddressFieldInput_2024_06_14) },
|
||||
{ $ref: getSchemaPath(TextFieldInput_2024_06_14) },
|
||||
|
||||
+32
-41
@@ -1,4 +1,4 @@
|
||||
import { BadRequestException } from "@nestjs/common";
|
||||
import { Logger } from "@nestjs/common";
|
||||
import { ApiProperty, ApiProperty as DocsProperty } from "@nestjs/swagger";
|
||||
import { plainToInstance } from "class-transformer";
|
||||
import { IsBoolean, IsOptional, IsString } from "class-validator";
|
||||
@@ -17,11 +17,15 @@ import {
|
||||
CheckboxGroupFieldInput_2024_06_14,
|
||||
RadioGroupFieldInput_2024_06_14,
|
||||
BooleanFieldInput_2024_06_14,
|
||||
NameFieldInput_2024_06_14,
|
||||
EmailFieldInput_2024_06_14,
|
||||
NameDefaultFieldInput_2024_06_14,
|
||||
EmailDefaultFieldInput_2024_06_14,
|
||||
GuestsDefaultFieldInput_2024_06_14,
|
||||
NotesDefaultFieldInput_2024_06_14,
|
||||
RescheduleReasonDefaultFieldInput_2024_06_14,
|
||||
TitleDefaultFieldInput_2024_06_14,
|
||||
} from "../inputs";
|
||||
|
||||
export class NameDefaultFieldOutput_2024_06_14 extends NameFieldInput_2024_06_14 {
|
||||
export class NameDefaultFieldOutput_2024_06_14 extends NameDefaultFieldInput_2024_06_14 {
|
||||
@IsBoolean()
|
||||
@DocsProperty({
|
||||
description: "This property is always true because it's a default field",
|
||||
@@ -47,7 +51,7 @@ export class NameDefaultFieldOutput_2024_06_14 extends NameFieldInput_2024_06_14
|
||||
required!: boolean;
|
||||
}
|
||||
|
||||
export class EmailDefaultFieldOutput_2024_06_14 extends EmailFieldInput_2024_06_14 {
|
||||
export class EmailDefaultFieldOutput_2024_06_14 extends EmailDefaultFieldInput_2024_06_14 {
|
||||
@IsBoolean()
|
||||
@DocsProperty({
|
||||
description: "This property is always true because it's a default field",
|
||||
@@ -108,7 +112,7 @@ export class LocationDefaultFieldOutput_2024_06_14 {
|
||||
hidden!: boolean;
|
||||
}
|
||||
|
||||
export class RescheduleReasonDefaultFieldOutput_2024_06_14 {
|
||||
export class RescheduleReasonDefaultFieldOutput_2024_06_14 extends RescheduleReasonDefaultFieldInput_2024_06_14 {
|
||||
@IsBoolean()
|
||||
@DocsProperty({
|
||||
description: "This property is always true because it's a default field",
|
||||
@@ -161,7 +165,7 @@ export class RescheduleReasonDefaultFieldOutput_2024_06_14 {
|
||||
disableOnPrefill!: boolean;
|
||||
}
|
||||
|
||||
export class TitleDefaultFieldOutput_2024_06_14 {
|
||||
export class TitleDefaultFieldOutput_2024_06_14 extends TitleDefaultFieldInput_2024_06_14 {
|
||||
@IsBoolean()
|
||||
@DocsProperty({
|
||||
description: "This property is always true because it's a default field",
|
||||
@@ -214,7 +218,7 @@ export class TitleDefaultFieldOutput_2024_06_14 {
|
||||
disableOnPrefill!: boolean;
|
||||
}
|
||||
|
||||
export class NotesDefaultFieldOutput_2024_06_14 {
|
||||
export class NotesDefaultFieldOutput_2024_06_14 extends NotesDefaultFieldInput_2024_06_14 {
|
||||
@IsBoolean()
|
||||
@DocsProperty({
|
||||
description: "This property is always true because it's a default field",
|
||||
@@ -267,7 +271,7 @@ export class NotesDefaultFieldOutput_2024_06_14 {
|
||||
disableOnPrefill!: boolean;
|
||||
}
|
||||
|
||||
export class GuestsDefaultFieldOutput_2024_06_14 {
|
||||
export class GuestsDefaultFieldOutput_2024_06_14 extends GuestsDefaultFieldInput_2024_06_14 {
|
||||
@IsBoolean()
|
||||
@DocsProperty({
|
||||
description: "This property is always true because it's a default field",
|
||||
@@ -567,6 +571,8 @@ export type CustomFieldOutput_2024_06_14 =
|
||||
| RadioGroupFieldOutput_2024_06_14
|
||||
| BooleanFieldOutput_2024_06_14;
|
||||
|
||||
export type KnownBookingField_2024_06_14 = DefaultFieldOutput_2024_06_14 | CustomFieldOutput_2024_06_14;
|
||||
|
||||
export type OutputBookingField_2024_06_14 =
|
||||
| DefaultFieldOutput_2024_06_14
|
||||
| CustomFieldOutput_2024_06_14
|
||||
@@ -574,6 +580,8 @@ export type OutputBookingField_2024_06_14 =
|
||||
|
||||
@ValidatorConstraint({ async: true })
|
||||
class OutputBookingFieldValidator_2024_06_14 implements ValidatorConstraintInterface {
|
||||
private readonly logger = new Logger("OutputBookingFieldValidator_2024_06_14");
|
||||
|
||||
private defaultOutputNameMap: { [key: string]: new () => DefaultFieldOutput_2024_06_14 } = {
|
||||
name: NameDefaultFieldOutput_2024_06_14,
|
||||
email: EmailDefaultFieldOutput_2024_06_14,
|
||||
@@ -600,34 +608,7 @@ class OutputBookingFieldValidator_2024_06_14 implements ValidatorConstraintInter
|
||||
};
|
||||
|
||||
async validate(bookingFields: OutputBookingField_2024_06_14[]) {
|
||||
if (!Array.isArray(bookingFields)) {
|
||||
throw new BadRequestException(`'bookingFields' must be an array.`);
|
||||
}
|
||||
|
||||
if (!bookingFields.length) {
|
||||
throw new BadRequestException(`'bookingFields' must contain at least 1 booking field.`);
|
||||
}
|
||||
|
||||
const slugs: string[] = [];
|
||||
for (const field of bookingFields) {
|
||||
const { type, slug } = field;
|
||||
if (!type) {
|
||||
throw new BadRequestException(`Each booking field must have a 'type' property.`);
|
||||
}
|
||||
|
||||
if (!slug) {
|
||||
throw new BadRequestException(`Each booking field must have a 'slug' property.`);
|
||||
}
|
||||
|
||||
if (slugs.includes(slug)) {
|
||||
throw new BadRequestException(
|
||||
`Duplicate bookingFields slug '${slug}' found. All bookingFields slugs must be unique.`
|
||||
);
|
||||
}
|
||||
if (slug !== "unknown") {
|
||||
slugs.push(slug);
|
||||
}
|
||||
|
||||
if (this.isUnknownField(field)) {
|
||||
await this.validateUnknownField(field);
|
||||
} else if (this.isDefaultField(field)) {
|
||||
@@ -653,35 +634,45 @@ class OutputBookingFieldValidator_2024_06_14 implements ValidatorConstraintInter
|
||||
const errors = await validate(instance);
|
||||
if (errors.length > 0) {
|
||||
const message = errors.flatMap((error) => Object.values(error.constraints || {})).join(", ");
|
||||
throw new BadRequestException(`Validation failed for ${field.slug} booking field: ${message}`);
|
||||
this.logger.error(
|
||||
`OutputBookingFieldValidator_2024_06_14: Validation failed for unknown booking field: ${message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async validateDefaultField(field: DefaultFieldOutput_2024_06_14) {
|
||||
const ClassType = this.defaultOutputNameMap[field.slug];
|
||||
if (!ClassType) {
|
||||
throw new BadRequestException(`Unsupported default booking field slug '${field.slug}'.`);
|
||||
this.logger.error(
|
||||
`OutputBookingFieldValidator_2024_06_14: Unsupported default booking field slug '${field.slug}'.`
|
||||
);
|
||||
}
|
||||
|
||||
const instance = plainToInstance(ClassType, field);
|
||||
const errors = await validate(instance);
|
||||
if (errors.length > 0) {
|
||||
const message = errors.flatMap((error) => Object.values(error.constraints || {})).join(", ");
|
||||
throw new BadRequestException(`Validation failed for ${field.slug} booking field: ${message}`);
|
||||
this.logger.error(
|
||||
`OutputBookingFieldValidator_2024_06_14: Validation failed for ${field.slug} booking field: ${message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async validateCustomField(field: CustomFieldOutput_2024_06_14) {
|
||||
const ClassType = this.customOutputTypeMap[field.type];
|
||||
if (!ClassType) {
|
||||
throw new BadRequestException(`Unsupported booking field type '${field.type}'.`);
|
||||
this.logger.error(
|
||||
`OutputBookingFieldValidator_2024_06_14: Unsupported custom booking field type '${field.type}'.`
|
||||
);
|
||||
}
|
||||
|
||||
const instance = plainToInstance(ClassType, field);
|
||||
const errors = await validate(instance);
|
||||
if (errors.length > 0) {
|
||||
const message = errors.flatMap((error) => Object.values(error.constraints || {})).join(", ");
|
||||
throw new BadRequestException(`Validation failed for ${field.type} booking field: ${message}`);
|
||||
this.logger.error(
|
||||
`OutputBookingFieldValidator_2024_06_14: Validation failed for ${field.type} booking field: ${message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import type {
|
||||
transformIntervalLimitsApiToInternal,
|
||||
transformRecurrenceApiToInternal,
|
||||
transformSeatsApiToInternal,
|
||||
transformBookingFieldsApiRequestToInternal,
|
||||
transformBookingFieldsApiToInternal,
|
||||
InternalLocationsSchema,
|
||||
} from "@calcom/lib/event-types/transformers";
|
||||
|
||||
@@ -33,7 +33,7 @@ export type InputEventTransformed_2024_06_14 = Omit<
|
||||
eventName?: string;
|
||||
bookingLimits?: ReturnType<typeof transformIntervalLimitsApiToInternal>;
|
||||
locations?: z.infer<typeof InternalLocationsSchema>;
|
||||
bookingFields?: ReturnType<typeof transformBookingFieldsApiRequestToInternal>;
|
||||
bookingFields?: ReturnType<typeof transformBookingFieldsApiToInternal>;
|
||||
durationLimits?: ReturnType<typeof transformIntervalLimitsApiToInternal>;
|
||||
recurringEvent?: ReturnType<typeof transformRecurrenceApiToInternal>;
|
||||
eventTypeColor?: ReturnType<typeof transformEventColorsApiToInternal>;
|
||||
|
||||
Reference in New Issue
Block a user