import Link from "next/link"; import type { CustomInputParsed, EventTypeSetupProps, FormValues } from "pages/event-types/[type]"; import { useEffect, useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import short from "short-uuid"; import { v5 as uuidv5 } from "uuid"; import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector"; import CustomInputItem from "@calcom/features/eventtypes/components/CustomInputItem"; import { APP_NAME, CAL_URL, IS_SELF_HOSTED } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; import { Badge, Button, Checkbox, Dialog, DialogClose, DialogContent, DialogFooter, Label, SettingsToggle, showToast, TextField, Tooltip, } from "@calcom/ui"; import { FiEdit, FiCopy, FiPlus } from "@calcom/ui/components/icon"; import CustomInputTypeForm from "@components/eventtype/CustomInputTypeForm"; import RequiresConfirmationController from "./RequiresConfirmationController"; const generateHashedLink = (id: number) => { const translator = short(); const seed = `${id}:${new Date().getTime()}`; const uid = translator.fromUUID(uuidv5(seed, uuidv5.URL)); return uid; }; const getRandomId = (length = 8) => { return ( -1 * parseInt( Math.ceil(Math.random() * Date.now()) .toPrecision(length) .toString() .replace(".", "") ) ); }; export const EventAdvancedTab = ({ eventType, team }: Pick) => { const connectedCalendarsQuery = trpc.viewer.connectedCalendars.useQuery(); const formMethods = useFormContext(); const { t } = useLocale(); const [showEventNameTip, setShowEventNameTip] = useState(false); const [hashedLinkVisible, setHashedLinkVisible] = useState(!!eventType.hashedLink); const [redirectUrlVisible, setRedirectUrlVisible] = useState(!!eventType.successRedirectUrl); const [hashedUrl, setHashedUrl] = useState(eventType.hashedLink?.link); const [customInputs, setCustomInputs] = useState( eventType.customInputs.sort((a, b) => a.id - b.id) || [] ); const [selectedCustomInput, setSelectedCustomInput] = useState(undefined); const [selectedCustomInputModalOpen, setSelectedCustomInputModalOpen] = useState(false); const [requiresConfirmation, setRequiresConfirmation] = useState(eventType.requiresConfirmation); const placeholderHashedLink = `${CAL_URL}/d/${hashedUrl}/${eventType.slug}`; const seatsEnabled = formMethods.getValues("seatsPerTimeSlotEnabled"); const removeCustom = (index: number) => { formMethods.getValues("customInputs").splice(index, 1); customInputs.splice(index, 1); setCustomInputs([...customInputs]); }; useEffect(() => { !hashedUrl && setHashedUrl(generateHashedLink(eventType.users[0]?.id ?? team?.id)); }, [eventType.users, hashedUrl, team?.id]); useEffect(() => { if (eventType.customInputs) { setCustomInputs(eventType.customInputs.sort((a, b) => a.id - b.id)); } }, [eventType.customInputs]); return (
{/** * Only display calendar selector if user has connected calendars AND if it's not * a team event. Since we don't have logic to handle each attendee calendar (for now). * This will fallback to each user selected destination calendar. */} {!!connectedCalendarsQuery.data?.connectedCalendars.length && !team && (
{t("add_another_calendar")}
( )} />

{t("select_which_cal")}

)}
setShowEventNameTip((old) => !old)} /> } />

0} onCheckedChange={(e) => { if (e && customInputs.length === 0) { // Push a placeholders setSelectedCustomInputModalOpen(true); } else if (!e) { formMethods.setValue("customInputs", []); } }}>
    {customInputs.map((customInput, idx) => ( { setSelectedCustomInput(customInput); setSelectedCustomInputModalOpen(true); }} deleteOnClick={() => removeCustom(idx)} /> ))}
{customInputs.length > 0 && ( )}


( onChange(e)} disabled={seatsEnabled} /> )} />
( onChange(e)} /> )} />
(
onChange(e)} />
)} />
( <> { setRedirectUrlVisible(e); onChange(e ? value : ""); }}> {/* Textfield has some margin by default we remove that so we can keep consitant aligment */}
{/*TODO: Extract it out into a component when used more than once*/} {!IS_SELF_HOSTED && ( Platform Only )}
)} />
{ formMethods.setValue("hashedLink", e ? hashedUrl : undefined); setHashedLinkVisible(e); }}> {/* Textfield has some margin by default we remove that so we can keep consitant aligment */}
} />

( { // Enabling seats will disable guests and requiring confirmation until fully supported if (e) { formMethods.setValue("disableGuests", true); formMethods.setValue("requiresConfirmation", false); setRequiresConfirmation(false); formMethods.setValue("seatsPerTimeSlot", 2); } else { formMethods.setValue("seatsPerTimeSlot", null); formMethods.setValue("disableGuests", false); } onChange(e); }}> (
{t("seats")}} onChange={(e) => { onChange(Math.abs(Number(e.target.value))); }} />
formMethods.setValue("seatsShowAttendees", e.target.checked)} defaultChecked={!!eventType.seatsShowAttendees} />
)} />
)} /> {showEventNameTip && (

{`{HOST} = ${t("your_name")}`}

{`{ATTENDEE} = ${t("attendee_name")}`}

{`{HOST/ATTENDEE} = ${t("dynamically_display_attendee_or_organizer")}`}

{`{LOCATION} = ${t("event_location")}`}

formMethods.setValue("eventName", eventType.eventName ?? "")} />
)} ( { const customInput: CustomInputParsed = { id: getRandomId(), eventTypeId: -1, label: values.label, placeholder: values.placeholder, required: values.required, type: values.type, options: values.options, hasToBeCreated: true, }; if (selectedCustomInput) { selectedCustomInput.label = customInput.label; selectedCustomInput.placeholder = customInput.placeholder; selectedCustomInput.required = customInput.required; selectedCustomInput.type = customInput.type; selectedCustomInput.options = customInput.options || undefined; selectedCustomInput.hasToBeCreated = false; // Update by id const inputIndex = customInputs.findIndex((input) => input.id === values.id); customInputs[inputIndex] = selectedCustomInput; setCustomInputs(customInputs); formMethods.setValue("customInputs", customInputs); } else { const concatted = customInputs.concat({ ...customInput, options: customInput.options, }); console.log(concatted); setCustomInputs(concatted); formMethods.setValue("customInputs", concatted); } setSelectedCustomInputModalOpen(false); }} onCancel={() => { setSelectedCustomInputModalOpen(false); }} /> )} />
); };