) => {
seatsPerTimeSlot,
seatsShowAttendees,
metadata,
+ customInputs,
});
}}>
@@ -495,6 +499,8 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
// const parsedMetaData = _EventTypeModel.parse(newMetadata);
const parsedMetaData = newMetadata;
+ const parsedCustomInputs = (rawEventType.customInputs || []).map((input) => customInputSchema.parse(input));
+
const eventType = {
...restEventType,
schedule: rawEventType.schedule?.id || rawEventType.users[0]?.defaultScheduleId || null,
@@ -502,6 +508,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
bookingLimits: parseBookingLimit(restEventType.bookingLimits),
locations: locations as unknown as LocationObject[],
metadata: parsedMetaData,
+ customInputs: parsedCustomInputs,
};
// backwards compat
diff --git a/apps/web/pages/team/[slug]/book.tsx b/apps/web/pages/team/[slug]/book.tsx
index cdeed9c326..e225b75724 100644
--- a/apps/web/pages/team/[slug]/book.tsx
+++ b/apps/web/pages/team/[slug]/book.tsx
@@ -4,7 +4,7 @@ import { JSONObject } from "superjson/dist/types";
import { LocationObject, privacyFilteredLocations } from "@calcom/app-store/locations";
import { parseRecurringEvent } from "@calcom/lib";
import prisma from "@calcom/prisma";
-import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
+import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { asStringOrNull, asStringOrThrow } from "@lib/asStringOrNull";
import getBooking, { GetBookingType } from "@lib/getBooking";
@@ -96,6 +96,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}),
periodStartDate: e.periodStartDate?.toString() ?? null,
periodEndDate: e.periodEndDate?.toString() ?? null,
+ customInputs: customInputSchema.array().parse(e.customInputs || []),
users: eventType.users.map((u) => ({
id: u.id,
name: u.name,
diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index 517dd3c31a..5051a76f6e 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -1388,5 +1388,9 @@
"test_preview_description": "Test your routing form without submitting any data",
"test_routing": "Test Routing",
"booking_limit_reached":"Booking Limit for this event type has been reached",
- "fill_this_field": "Please fill in this field"
+ "fill_this_field": "Please fill in this field",
+ "options": "Options",
+ "enter_option": "Enter Option {{index}}",
+ "add_an_option": "Add an option",
+ "radio": "Radio"
}
diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts
index 0d7032f306..6951dc36e6 100644
--- a/packages/features/bookings/lib/handleNewBooking.ts
+++ b/packages/features/bookings/lib/handleNewBooking.ts
@@ -42,7 +42,11 @@ import { checkBookingLimits, getLuckyUser } from "@calcom/lib/server";
import { getTranslation } from "@calcom/lib/server/i18n";
import { updateWebUser as syncServicesUpdateWebUser } from "@calcom/lib/sync/SyncServiceManager";
import prisma, { userSelect } from "@calcom/prisma";
-import { EventTypeMetaDataSchema, extendedBookingCreateBody } from "@calcom/prisma/zod-utils";
+import {
+ customInputSchema,
+ EventTypeMetaDataSchema,
+ extendedBookingCreateBody,
+} from "@calcom/prisma/zod-utils";
import type { BufferedBusyTime } from "@calcom/types/BufferedBusyTime";
import type { AdditionalInformation, AppsStatus, CalendarEvent } from "@calcom/types/Calendar";
import type { EventResult, PartialReference } from "@calcom/types/EventManager";
@@ -203,6 +207,7 @@ const getEventTypesFromDB = async (eventTypeId: number) => {
...eventType,
metadata: EventTypeMetaDataSchema.parse(eventType.metadata),
recurringEvent: parseRecurringEvent(eventType.recurringEvent),
+ customInputs: customInputSchema.array().parse(eventType.customInputs),
locations: (eventType.locations ?? []) as LocationObject[],
};
};
@@ -310,7 +315,7 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) {
const stripeAppData = getStripeAppData(eventType);
// Check if required custom inputs exist
- handleCustomInputs(eventType.customInputs, reqBody.customInputs);
+ handleCustomInputs(eventType.customInputs as EventTypeCustomInput[], reqBody.customInputs);
let timeOutOfBounds = false;
try {
diff --git a/packages/lib/defaultEvents.ts b/packages/lib/defaultEvents.ts
index 1968bc2c4c..8d33a1a375 100644
--- a/packages/lib/defaultEvents.ts
+++ b/packages/lib/defaultEvents.ts
@@ -1,10 +1,8 @@
-import type { EventTypeCustomInput } from "@prisma/client";
import { PeriodType, Prisma, SchedulingType, UserPlan } from "@prisma/client";
import { DailyLocationType } from "@calcom/app-store/locations";
import { userSelect } from "@calcom/prisma/selects";
-import { _EventTypeModel } from "@calcom/prisma/zod";
-import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
+import { CustomInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
type User = Prisma.UserGetPayload;
@@ -50,7 +48,7 @@ const user: User = {
allowDynamicBooking: true,
};
-const customInputs: EventTypeCustomInput[] = [];
+const customInputs: CustomInputSchema[] = [];
const commons = {
isDynamic: true,
diff --git a/packages/prisma/migrations/20221129112344_adding_radio_custom_input/migration.sql b/packages/prisma/migrations/20221129112344_adding_radio_custom_input/migration.sql
new file mode 100644
index 0000000000..d1d3b7c3ed
--- /dev/null
+++ b/packages/prisma/migrations/20221129112344_adding_radio_custom_input/migration.sql
@@ -0,0 +1,5 @@
+-- AlterEnum
+ALTER TYPE "EventTypeCustomInputType" ADD VALUE 'radio';
+
+-- AlterTable
+ALTER TABLE "EventTypeCustomInput" ADD COLUMN "options" JSONB;
diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma
index 44b6cedbe4..261f11391b 100644
--- a/packages/prisma/schema.prisma
+++ b/packages/prisma/schema.prisma
@@ -347,6 +347,7 @@ enum EventTypeCustomInputType {
TEXTLONG @map("textLong")
NUMBER @map("number")
BOOL @map("bool")
+ RADIO @map("radio")
}
model EventTypeCustomInput {
@@ -355,6 +356,8 @@ model EventTypeCustomInput {
eventType EventType @relation(fields: [eventTypeId], references: [id], onDelete: Cascade)
label String
type EventTypeCustomInputType
+ // @zod.custom(imports.customInputOptionSchema)
+ options Json?
required Boolean
placeholder String @default("")
}
diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts
index 4841a31f41..d1470c4690 100644
--- a/packages/prisma/zod-utils.ts
+++ b/packages/prisma/zod-utils.ts
@@ -1,3 +1,4 @@
+import { EventTypeCustomInputType } from "@prisma/client";
import z, { ZodNullable, ZodObject, ZodOptional } from "zod";
/* eslint-disable no-underscore-dangle */
@@ -212,6 +213,25 @@ export const teamMetadataSchema = z
.partial()
.nullable();
+export const customInputOptionSchema = z.array(
+ z.object({
+ label: z.string(),
+ type: z.string(),
+ })
+);
+
+export const customInputSchema = z.object({
+ id: z.number(),
+ eventTypeId: z.number(),
+ label: z.string(),
+ type: z.nativeEnum(EventTypeCustomInputType),
+ options: customInputOptionSchema.optional().nullable(),
+ required: z.boolean(),
+ placeholder: z.string(),
+});
+
+export type CustomInputSchema = z.infer;
+
/**
* Ensures that it is a valid HTTP URL
* It automatically avoids
diff --git a/packages/trpc/server/routers/viewer/eventTypes.tsx b/packages/trpc/server/routers/viewer/eventTypes.tsx
index 9c27ba1aa6..d5640eef47 100644
--- a/packages/trpc/server/routers/viewer/eventTypes.tsx
+++ b/packages/trpc/server/routers/viewer/eventTypes.tsx
@@ -11,7 +11,12 @@ import { validateBookingLimitOrder } from "@calcom/lib";
import { CAL_URL } from "@calcom/lib/constants";
import { baseEventTypeSelect, baseUserSelect } from "@calcom/prisma";
import { _DestinationCalendarModel, _EventTypeCustomInputModel, _EventTypeModel } from "@calcom/prisma/zod";
-import { EventTypeMetaDataSchema, stringOrNumber } from "@calcom/prisma/zod-utils";
+import {
+ customInputSchema,
+ EventTypeMetaDataSchema,
+ stringOrNumber,
+ CustomInputSchema,
+} from "@calcom/prisma/zod-utils";
import { createEventTypeInput } from "@calcom/prisma/zod/custom/eventtype";
import { TRPCError } from "@trpc/server";
@@ -30,7 +35,7 @@ function handlePeriodType(periodType: string | undefined): PeriodType | undefine
return PeriodType[passedPeriodType];
}
-function handleCustomInputs(customInputs: EventTypeCustomInput[], eventTypeId: number) {
+function handleCustomInputs(customInputs: CustomInputSchema[], eventTypeId: number) {
const cInputsIdsToDelete = customInputs.filter((input) => input.id > 0).map((e) => e.id);
const cInputsToCreate = customInputs
.filter((input) => input.id < 0)
@@ -39,6 +44,7 @@ function handleCustomInputs(customInputs: EventTypeCustomInput[], eventTypeId: n
label: input.label,
required: input.required,
placeholder: input.placeholder,
+ options: input.options || undefined,
}));
const cInputsToUpdate = customInputs
.filter((input) => input.id > 0)
@@ -48,6 +54,7 @@ function handleCustomInputs(customInputs: EventTypeCustomInput[], eventTypeId: n
label: input.label,
required: input.required,
placeholder: input.placeholder,
+ options: input.options || undefined,
},
where: {
id: input.id,
@@ -71,7 +78,7 @@ function handleCustomInputs(customInputs: EventTypeCustomInput[], eventTypeId: n
const EventTypeUpdateInput = _EventTypeModel
/** Optional fields */
.extend({
- customInputs: z.array(_EventTypeCustomInputModel),
+ customInputs: z.array(customInputSchema).optional(),
destinationCalendar: _DestinationCalendarModel.pick({
integration: true,
externalId: true,
diff --git a/packages/ui/index.tsx b/packages/ui/index.tsx
index 8e1662a407..6830c765da 100644
--- a/packages/ui/index.tsx
+++ b/packages/ui/index.tsx
@@ -86,7 +86,7 @@ export {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "./v2/core/Dropdown";
-export { RadioGroup } from "./v2/core/form";
+export { RadioGroup, Radio, Group, RadioField } from "./v2/core/form";
export { BooleanToggleGroupField } from "./v2/core/form/BooleanToggleGroup";
export { DateRangePickerLazy as DateRangePicker } from "./v2/core/form/date-range-picker";
export { default as DatePickerField } from "./v2/core/form/DatePicker";
diff --git a/packages/ui/v2/core/form/index.ts b/packages/ui/v2/core/form/index.ts
index 93e21c7c8d..81bdd04ac4 100644
--- a/packages/ui/v2/core/form/index.ts
+++ b/packages/ui/v2/core/form/index.ts
@@ -1,4 +1,10 @@
-export { RadioGroup, /* TODO: solve this conflict -> Select, */ Radio } from "./radio-area";
+export {
+ RadioGroup,
+ /* TODO: solve this conflict -> Select, */
+ Radio,
+ Group,
+ RadioField,
+} from "./radio-area";
export { default as Checkbox } from "../../../components/form/checkbox/Checkbox";
export { default as DatePicker } from "./DatePicker";
export { default as Select, SelectWithValidation, SelectField, getReactSelectProps } from "./select";
diff --git a/packages/ui/v2/core/form/radio-area/Radio.tsx b/packages/ui/v2/core/form/radio-area/Radio.tsx
index 4f7351f9ce..8b892c900b 100644
--- a/packages/ui/v2/core/form/radio-area/Radio.tsx
+++ b/packages/ui/v2/core/form/radio-area/Radio.tsx
@@ -29,7 +29,7 @@ export const Label = (props: JSX.IntrinsicElements["label"] & { disabled?: boole
diff --git a/packages/ui/v2/core/form/radio-area/index.ts b/packages/ui/v2/core/form/radio-area/index.ts
index 9067dc8c88..b6a8b4b6cc 100644
--- a/packages/ui/v2/core/form/radio-area/index.ts
+++ b/packages/ui/v2/core/form/radio-area/index.ts
@@ -1,3 +1,3 @@
export * as RadioGroup from "./RadioAreaGroup";
export { default as Select } from "./Select";
-export * as Radio from "./Radio";
+export { Group, Indicator, Label, Radio, RadioField } from "./Radio";