diff --git a/lib/eventTypeInput.ts b/lib/eventTypeInput.ts deleted file mode 100644 index e8c76e428c..0000000000 --- a/lib/eventTypeInput.ts +++ /dev/null @@ -1,13 +0,0 @@ -export enum EventTypeCustomInputType { - Text = 'text', - TextLong = 'textLong', - Number = 'number', - Bool = 'bool', -} - -export interface EventTypeCustomInput { - id?: number; - type: EventTypeCustomInputType; - label: string; - required: boolean; -} diff --git a/pages/[user]/book.tsx b/pages/[user]/book.tsx index df9b19c53d..feb622870f 100644 --- a/pages/[user]/book.tsx +++ b/pages/[user]/book.tsx @@ -3,6 +3,7 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { CalendarIcon, ClockIcon, ExclamationIcon, LocationMarkerIcon } from "@heroicons/react/solid"; import prisma, { whereAndSelect } from "../../lib/prisma"; +import { EventTypeCustomInputType } from "@prisma/client"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "../../lib/telemetry"; import { useEffect, useState } from "react"; import dayjs from "dayjs"; @@ -13,7 +14,6 @@ import PhoneInput from "react-phone-number-input"; import { LocationType } from "../../lib/location"; import Avatar from "../../components/Avatar"; import Button from "../../components/ui/Button"; -import { EventTypeCustomInputType } from "../../lib/eventTypeInput"; import Theme from "@components/Theme"; import { ReactMultiEmail } from "react-multi-email"; @@ -70,7 +70,7 @@ export default function Book(props: any): JSX.Element { .map((input) => { const data = event.target["custom_" + input.id]; if (data) { - if (input.type === EventTypeCustomInputType.Bool) { + if (input.type === EventTypeCustomInputType.BOOL) { return input.label + "\n" + (data.checked ? "Yes" : "No"); } else { return input.label + "\n" + data.value; @@ -272,14 +272,14 @@ export default function Book(props: any): JSX.Element { .sort((a, b) => a.id - b.id) .map((input) => (
- {input.type !== EventTypeCustomInputType.Bool && ( + {input.type !== EventTypeCustomInputType.BOOL && ( )} - {input.type === EventTypeCustomInputType.TextLong && ( + {input.type === EventTypeCustomInputType.TEXTLONG && ( -
- -
- -
- -
-
-
- - -
-
-
-
- -
-
- -

- Hide the event type from your page, so it can only be booked through its URL. -

-
-
-
-
-
-
- -
-
- -

- The booking needs to be confirmed, before it is pushed to the integrations and a - confirmation mail is sent. -

-
-
-
- -
- When can people book this event? -
- -
- -
- minutes -
-
-
-
-
-
- {/* */} - Invitees can schedule... -
- - Date Range -
- {PERIOD_TYPES.map((period) => ( - - classnames( - checked ? "bg-indigo-50 border-indigo-200 z-10" : "border-gray-200", - "relative py-4 px-2 lg:p-4 min-h-20 lg:flex items-center cursor-pointer focus:outline-none" - ) - }> - {({ active, checked }) => ( - <> - - ))} -
-
-
-
-
-
- -
- -
- minutes -
-
-
-
-
-

- How do you want to offer your availability for this event type? -

- -
- - Cancel - - -
-
-
-
- - - -
-
-

Delete this event type

-
-

Once you delete this event type, it will be permanently removed.

-
-
- -
-
-
- - - {showLocationModal && ( -
-
- - - - -
-
-
- -
-
- -
-
-
- -
-
- -
- -
-
-
- - -
- -
- - -
- -
-
- - )} - - - ); -} - -export const getServerSideProps: GetServerSideProps = async ({ req, query }) => { - const session = await getSession({ req }); - if (!session) { - return { - redirect: { - permanent: false, - destination: "/auth/login", - }, - }; - } - - const user: User = await prisma.user.findFirst({ - where: { - email: session.user.email, - }, - select: { - username: true, - timeZone: true, - startTime: true, - endTime: true, - availability: true, - }, - }); - - const eventType: EventType | null = await prisma.eventType.findUnique({ - where: { - id: parseInt(query.type as string), - }, - select: { - id: true, - title: true, - slug: true, - description: true, - length: true, - hidden: true, - locations: true, - eventName: true, - availability: true, - customInputs: true, - timeZone: true, - periodType: true, - periodDays: true, - periodStartDate: true, - periodEndDate: true, - periodCountCalendarDays: true, - requiresConfirmation: true, - minimumBookingNotice: true, - }, - }); - - if (!eventType) { - return { - notFound: true, - }; - } - - const credentials = await prisma.credential.findMany({ - where: { - userId: user.id, - }, - select: { - id: true, - type: true, - key: true, - }, - }); - - const integrations = [ - { - installed: !!(process.env.GOOGLE_API_CREDENTIALS && validJson(process.env.GOOGLE_API_CREDENTIALS)), - enabled: credentials.find((integration) => integration.type === "google_calendar") != null, - type: "google_calendar", - title: "Google Calendar", - imageSrc: "integrations/google-calendar.svg", - description: "For personal and business accounts", - }, - { - installed: !!(process.env.MS_GRAPH_CLIENT_ID && process.env.MS_GRAPH_CLIENT_SECRET), - type: "office365_calendar", - enabled: credentials.find((integration) => integration.type === "office365_calendar") != null, - title: "Office 365 / Outlook.com Calendar", - imageSrc: "integrations/outlook.svg", - description: "For personal and business accounts", - }, - ]; - - const locationOptions: OptionBase[] = [ - { value: LocationType.InPerson, label: "In-person meeting" }, - { value: LocationType.Phone, label: "Phone call" }, - { value: LocationType.Zoom, label: "Zoom Video" }, - ]; - - const hasGoogleCalendarIntegration = integrations.find( - (i) => i.type === "google_calendar" && i.installed === true && i.enabled - ); - if (hasGoogleCalendarIntegration) { - locationOptions.push({ value: LocationType.GoogleMeet, label: "Google Meet" }); - } - - const hasOfficeIntegration = integrations.find( - (i) => i.type === "office365_calendar" && i.installed === true && i.enabled - ); - if (hasOfficeIntegration) { - // TODO: Add default meeting option of the office integration. - // Assuming it's Microsoft Teams. - } - - const getAvailability = (providesAvailability) => - providesAvailability.availability && providesAvailability.availability.length - ? providesAvailability.availability - : null; - - const availability: Availability[] = getAvailability(eventType) || - getAvailability(user) || [ - { - days: [0, 1, 2, 3, 4, 5, 6], - startTime: user.startTime, - endTime: user.endTime, - }, - ]; - - availability.sort((a, b) => a.startTime - b.startTime); - - const eventTypeObject = Object.assign({}, eventType, { - periodStartDate: eventType.periodStartDate?.toString() ?? null, - periodEndDate: eventType.periodEndDate?.toString() ?? null, - }); - - return { - props: { - user, - eventType: eventTypeObject, - locationOptions, - availability, - }, - }; -}; diff --git a/pages/event-types/[type].tsx b/pages/event-types/[type].tsx index de360693cd..81071a84f1 100644 --- a/pages/event-types/[type].tsx +++ b/pages/event-types/[type].tsx @@ -5,13 +5,13 @@ import { useRouter } from "next/router"; import React, { useEffect, useRef, useState } from "react"; import Select, { OptionBase } from "react-select"; import prisma from "@lib/prisma"; +import { EventTypeCustomInput, EventTypeCustomInputType } from "@prisma/client"; import { LocationType } from "@lib/location"; import Shell from "@components/Shell"; import { getSession } from "next-auth/client"; import { Scheduler } from "@components/ui/Scheduler"; import { Disclosure, RadioGroup } from "@headlessui/react"; import { PhoneIcon, XIcon } from "@heroicons/react/outline"; -import { EventTypeCustomInput, EventTypeCustomInputType } from "@lib/eventTypeInput"; import { LocationMarkerIcon, LinkIcon, @@ -106,10 +106,10 @@ export default function EventTypePage({ const router = useRouter(); const inputOptions: OptionBase[] = [ - { value: EventTypeCustomInputType.Text, label: "Text" }, - { value: EventTypeCustomInputType.TextLong, label: "Multiline Text" }, - { value: EventTypeCustomInputType.Number, label: "Number" }, - { value: EventTypeCustomInputType.Bool, label: "Checkbox" }, + { value: EventTypeCustomInputType.TEXT, label: "Text" }, + { value: EventTypeCustomInputType.TEXTLONG, label: "Multiline Text" }, + { value: EventTypeCustomInputType.NUMBER, label: "Number" }, + { value: EventTypeCustomInputType.BOOL, label: "Checkbox" }, ]; const [DATE_PICKER_ORIENTATION, setDatePickerOrientation] = useState("horizontal"); @@ -342,28 +342,19 @@ export default function EventTypePage({ type: e.target.type.value, }; - if (e.target.id?.value) { - const index = customInputs.findIndex((inp) => inp.id === +e.target.id?.value); - if (index >= 0) { - const input = customInputs[index]; - input.label = customInput.label; - input.required = customInput.required; - input.type = customInput.type; - setCustomInputs(customInputs); - } + if (selectedCustomInput) { + selectedCustomInput.label = customInput.label; + selectedCustomInput.required = customInput.required; + selectedCustomInput.type = customInput.type; } else { setCustomInputs(customInputs.concat(customInput)); } closeAddCustomModal(); }; - const removeCustom = (customInput, e) => { - e.preventDefault(); - const index = customInputs.findIndex((inp) => inp.id === customInput.id); - if (index >= 0) { - customInputs.splice(index, 1); - setCustomInputs([...customInputs]); - } + const removeCustom = (index: number) => { + customInputs.splice(index, 1); + setCustomInputs([...customInputs]); }; return ( @@ -638,8 +629,8 @@ export default function EventTypePage({
    - {customInputs.map((customInput) => ( -
  • + {customInputs.map((customInput: EventTypeCustomInput, idx: number) => ( +
  • @@ -661,7 +652,7 @@ export default function EventTypePage({ className="mr-2 text-sm text-primary-600"> Edit -
    diff --git a/prisma/migrations/20210814175645_custom_inputs_type_enum/migration.sql b/prisma/migrations/20210814175645_custom_inputs_type_enum/migration.sql new file mode 100644 index 0000000000..614c2c30d5 --- /dev/null +++ b/prisma/migrations/20210814175645_custom_inputs_type_enum/migration.sql @@ -0,0 +1,14 @@ + +-- CreateEnum +CREATE TYPE "EventTypeCustomInputType" AS ENUM ('text', 'textLong', 'number', 'bool'); + +--- AlterTable +ALTER TABLE "EventTypeCustomInput" RENAME COLUMN "type" TO "type_old"; +ALTER TABLE "EventTypeCustomInput" ADD COLUMN "type" "EventTypeCustomInputType"; + +-- UpdateTable +UPDATE "EventTypeCustomInput" SET "type" = CAST( "type_old" AS "EventTypeCustomInputType" ); + +-- AlterTable +ALTER TABLE "EventTypeCustomInput" ALTER COLUMN "type" SET NOT NULL; +ALTER TABLE "EventTypeCustomInput" DROP COLUMN "type_old"; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 02bc0bc6e6..ca51aa8158 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -163,12 +163,19 @@ model SelectedCalendar { @@id([userId,integration,externalId]) } +enum EventTypeCustomInputType { + TEXT @map("text") + TEXTLONG @map("textLong") + NUMBER @map("number") + BOOL @map("bool") +} + model EventTypeCustomInput { id Int @id @default(autoincrement()) eventTypeId Int eventType EventType @relation(fields: [eventTypeId], references: [id]) label String - type String + type EventTypeCustomInputType required Boolean }