// TODO: replace headlessui with radix-ui import { Menu, Transition } from "@headlessui/react"; import { ChevronDownIcon, DotsHorizontalIcon, ExternalLinkIcon, LinkIcon, PlusIcon, UsersIcon, } from "@heroicons/react/solid"; import { SchedulingType } from "@prisma/client"; import { Prisma } from "@prisma/client"; import dayjs from "dayjs"; import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; import React, { Fragment, useRef } from "react"; import { useMutation } from "react-query"; import { asStringOrNull } from "@lib/asStringOrNull"; import { getSession } from "@lib/auth"; import classNames from "@lib/classNames"; import { HttpError } from "@lib/core/http/error"; import { extractLocaleInfo } from "@lib/core/i18n/i18n.utils"; import { ONBOARDING_INTRODUCED_AT } from "@lib/getting-started"; import { useLocale } from "@lib/hooks/useLocale"; import { useToggleQuery } from "@lib/hooks/useToggleQuery"; import createEventType from "@lib/mutations/event-types/create-event-type"; import showToast from "@lib/notification"; import prisma from "@lib/prisma"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import { Dialog, DialogClose, DialogContent } from "@components/Dialog"; import Shell from "@components/Shell"; import { Tooltip } from "@components/Tooltip"; import EventTypeDescription from "@components/eventtype/EventTypeDescription"; import { Alert } from "@components/ui/Alert"; import Avatar from "@components/ui/Avatar"; import AvatarGroup from "@components/ui/AvatarGroup"; import Badge from "@components/ui/Badge"; import { Button } from "@components/ui/Button"; import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@components/ui/Dropdown"; import * as RadioArea from "@components/ui/form/radio-area"; import UserCalendarIllustration from "@components/ui/svg/UserCalendarIllustration"; type PageProps = inferSSRProps; type EventType = PageProps["eventTypes"][number]; type Profile = PageProps["profiles"][number]; type MembershipCount = EventType["metadata"]["membershipCount"]; const EventTypesPage = (props: PageProps) => { const { locale } = useLocale({ localeProp: props.localeProp, namespaces: "event-types-page", }); const CreateFirstEventTypeView = () => (

Create your first event type

Event types enable you to share links that show available times on your calendar and allow people to make bookings with you.

); const EventTypeListHeading = ({ profile, membershipCount, }: { profile?: Profile; membershipCount: MembershipCount; }) => (
{profile?.name || ""} {membershipCount && ( {membershipCount} )} {profile?.slug && ( {`${process.env.NEXT_PUBLIC_APP_URL?.replace( "https://", "" )}/${profile.slug}`} )}
); const EventTypeList = ({ readOnly, types, profile, }: { profile: PageProps["profiles"][number]; readOnly: boolean; types: EventType["eventTypes"]; }) => (
); return (
Event Types | Cal.com ) }> {props.user.plan === "FREE" && !props.canAddEvents && ( You need to upgrade your plan to have more than one active event type.} message={ <> To upgrade go to{" "} {"https://cal.com/upgrade"} } className="my-4" /> )} {props.eventTypes && props.eventTypes.map((input) => ( {/* hide list heading when there is only one (current user) */} {(props.eventTypes.length !== 1 || input.teamId) && ( )} ))} {props.eventTypes.length === 0 && }
); }; const CreateNewEventDialog = ({ profiles, canAddEvents, localeProp, }: { profiles: Profile[]; canAddEvents: boolean; localeProp: string; }) => { const router = useRouter(); const teamId: number | null = Number(router.query.teamId) || null; const modalOpen = useToggleQuery("new"); const { t } = useLocale({ localeProp }); const createMutation = useMutation(createEventType, { onSuccess: async ({ eventType }) => { await router.push("/event-types/" + eventType.id); showToast(`${eventType.title} event type created successfully`, "success"); }, onError: (err: HttpError) => { const message = `${err.statusCode}: ${err.message}`; showToast(message, "error"); }, }); const slugRef = useRef(null); return ( { router.push(isOpen ? modalOpen.hrefOn : modalOpen.hrefOff); }}> {!profiles.filter((profile) => profile.teamId).length && ( )} {profiles.filter((profile) => profile.teamId).length > 0 && ( Create an event type under your name or a team. {profiles.map((profile) => ( router.push({ pathname: router.pathname, query: { ...router.query, new: "1", eventPage: profile.slug, ...(profile.teamId ? { teamId: profile.teamId, } : {}), }, }) }> {profile.name ? profile.name : profile.slug} ))} )}

Create a new event type for people to book times with.

{ e.preventDefault(); const target = e.target as unknown as Record< "title" | "slug" | "description" | "length" | "schedulingType", { value: string } >; const payload = { title: target.title.value, slug: target.slug.value, description: target.description.value, length: parseInt(target.length.value), }; if (router.query.teamId) { payload.teamId = parseInt(asStringOrNull(router.query.teamId), 10); payload.schedulingType = target.schedulingType.value; } createMutation.mutate(payload); }}>
{ if (!slugRef.current) { return; } slugRef.current.value = e.target.value.replace(/\s+/g, "-").toLowerCase(); }} type="text" name="title" id="title" required className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-neutral-900 focus:border-neutral-900 sm:text-sm" placeholder="Quick Chat" />
{process.env.NEXT_PUBLIC_APP_URL}/{router.query.eventPage || profiles[0].slug}/