refactor: eliminate all @calcom/trpc/react imports and tRPC hooks from @calcom/features (#27728)

* migrate booker layout selector

* move schedule hooks and types

* migrate feature opt in components

* migrate feedback dialog

* update

* migrate schedule hooks

* migrate useSegments

* update

* update import paths

* fix import paths

* mv useTimesForSchedule back to features

* fix

* add useEvent to web

* remove the old file

* update imports

* clean up

* migrate Segment

* update imports

* update imports

* fix name

* fix tests

* fix

* fix

* update imports

* remove trpc/react

* remove trpc dependency from package.json

* rm

* wip

* wip

* fix types

* fix

* migrate PhoneInput

* mv LocationInput

* remove dead file

* update import paths

* rm dead import

* rm useApp hook

* cleanup

* wip

* restore

* wip

* remove dead code

* fix

* simplify

* fix

* fix

* fix

* fix

* fix

* fix

* format

* format

* restore

* remove prop
This commit is contained in:
Benny Joo
2026-02-09 08:39:28 -03:00
committed by GitHub
parent 7b9168e3f2
commit eba0635a07
25 changed files with 288 additions and 349 deletions
@@ -64,9 +64,6 @@ vi.mock("@calcom/web/modules/shell/Shell", () => ({
ShellMain: vi.fn(),
}));
vi.mock("@calcom/lib/hooks/useApp", () => ({
default: vi.fn(),
}));
/**
* Avoids the error due to Formbricks
*/
@@ -15,7 +15,7 @@ import {
OrganizerDefaultConferencingAppType,
} from "@calcom/app-store/locations";
import { Dialog } from "@calcom/features/components/controlled-dialog";
import PhoneInput from "@calcom/features/components/phone-input";
import PhoneInput from "@calcom/web/components/phone-input";
import type { LocationOption } from "@calcom/features/form/components/LocationSelect";
import LocationSelect from "@calcom/features/form/components/LocationSelect";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -8,7 +8,7 @@ import { DefaultEventLocationTypeEnum } from "@calcom/app-store/locations";
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
import getLocationOptionsForSelect from "@calcom/features/bookings/lib/getLocationOptionsForSelect";
import { FormBuilderField } from "@calcom/features/form-builder/FormBuilderField";
import { FormBuilderField } from "@calcom/web/modules/form-builder/components/FormBuilderField";
import { fieldTypesConfigMap } from "@calcom/features/form-builder/fieldTypes";
import { fieldsThatSupportLabelAsSafeHtml } from "@calcom/features/form-builder/fieldsThatSupportLabelAsSafeHtml";
import { SystemField } from "@calcom/lib/bookings/SystemField";
@@ -6,7 +6,7 @@ import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
import { Dialog } from "@calcom/features/components/controlled-dialog";
import PhoneInput from "@calcom/features/components/phone-input";
import PhoneInput from "@calcom/web/components/phone-input";
import { WORKFLOW_ACTIONS } from "@calcom/features/ee/workflows/lib/constants";
import { onlyLettersNumbersSpaces } from "@calcom/features/ee/workflows/lib/schema";
import { SENDER_ID, SENDER_NAME } from "@calcom/lib/constants";
@@ -2,7 +2,7 @@ import { useState } from "react";
import type { UseFormReturn } from "react-hook-form";
import { Dialog } from "@calcom/features/components/controlled-dialog";
import PhoneInput from "@calcom/features/components/phone-input";
import PhoneInput from "@calcom/web/components/phone-input";
import { getEventTypeIdForCalAiTest } from "@calcom/features/ee/workflows/lib/actionHelperFunctions";
import type { FormValues } from "@calcom/features/ee/workflows/lib/types";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -8,7 +8,7 @@ import "react-phone-number-input/style.css";
import type { RetellAgentWithDetails } from "@calcom/features/calAIPhone/providers/retellAI";
import { Dialog } from "@calcom/features/components/controlled-dialog";
import PhoneInput from "@calcom/features/components/phone-input";
import PhoneInput from "@calcom/web/components/phone-input";
import {
isSMSAction,
isWhatsappAction,
@@ -9,7 +9,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import classNames from "@calcom/ui/classNames";
import { TextField } from "@calcom/ui/components/form";
import LocationInput from "@calcom/features/eventtypes/components/locations/LocationInput";
import LocationInput from "./LocationInput";
import LocationOptionContainer from "./LocationSettingsContainer";
const DefaultLocationSettings = ({
@@ -15,7 +15,7 @@ import {
isStaticLocationType,
} from "@calcom/app-store/locations";
import { getAppFromSlug } from "@calcom/app-store/utils";
import PhoneInput from "@calcom/features/components/phone-input";
import PhoneInput from "@calcom/web/components/phone-input";
import invertLogoOnDark from "@calcom/lib/invertLogoOnDark";
import type { LocationOption } from "@calcom/features/form/components/LocationSelect";
import LocationSelect from "@calcom/features/form/components/LocationSelect";
@@ -1,13 +1,13 @@
import { useFormContext, Controller } from "react-hook-form";
import type { EventLocationType } from "@calcom/app-store/locations";
import PhoneInput from "@calcom/features/components/phone-input";
import PhoneInput from "@calcom/web/components/phone-input";
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import classNames from "@calcom/ui/classNames";
import { TextField } from "@calcom/ui/components/form";
import type { LocationInputCustomClassNames } from "./types";
import type { LocationInputCustomClassNames } from "@calcom/features/eventtypes/components/locations/types";
const LocationInput = (props: {
eventLocationType: EventLocationType;
@@ -23,7 +23,7 @@ import { showToast } from "@calcom/ui/components/toast";
import CalVideoSettings from "./CalVideoSettings";
import DefaultLocationSettings from "./DefaultLocationSettings";
import LocationInput from "@calcom/features/eventtypes/components/locations/LocationInput";
import LocationInput from "./LocationInput";
export type TEventTypeLocation = Pick<EventTypeSetupProps["eventType"], "locations" | "calVideoSettings">;
export type TLocationOptions = Pick<EventTypeSetupProps, "locationOptions">["locationOptions"];
@@ -7,10 +7,10 @@ import { z } from "zod";
import { getTemplateFieldsSchema } from "@calcom/features/calAIPhone/getTemplateFieldsSchema";
import { templateFieldsMap } from "@calcom/features/calAIPhone/template-fields-map";
import type { TemplateType } from "@calcom/features/calAIPhone/zod-utils";
import PhoneInput from "@calcom/features/components/phone-input";
import PhoneInput from "@calcom/web/components/phone-input";
import LicenseRequired from "~/ee/common/components/LicenseRequired";
import type { EventTypeSetup, FormValues } from "@calcom/features/eventtypes/lib/types";
import { ComponentForField } from "@calcom/features/form-builder/FormBuilderField";
import { ComponentForField } from "@calcom/web/modules/form-builder/components/FormBuilderField";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import classNames from "@calcom/ui/classNames";
@@ -6,7 +6,7 @@ import type {
TextLikeComponentProps,
} from "@calcom/app-store/routing-forms/components/react-awesome-query-builder/widgets";
import Widgets from "@calcom/app-store/routing-forms/components/react-awesome-query-builder/widgets";
import PhoneInput from "@calcom/features/components/phone-input";
import PhoneInput from "@calcom/web/components/phone-input";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { fieldSchema, variantsConfigSchema, FieldType } from "@calcom/prisma/zod-utils";
import { AddressInput } from "@calcom/ui/components/address";
@@ -18,8 +18,8 @@ import { RadioGroup, RadioField } from "@calcom/ui/components/radio";
import { Tooltip } from "@calcom/ui/components/tooltip";
import { ComponentForField } from "./FormBuilderField";
import { propsTypes } from "./propsTypes";
import { preprocessNameFieldDataWithVariant } from "./utils";
import { propsTypes } from "@calcom/features/form-builder/propsTypes";
import { preprocessNameFieldDataWithVariant } from "@calcom/features/form-builder/utils";
export const isValidValueProp: Record<Component["propsType"], (val: unknown) => boolean> = {
boolean: (val) => typeof val === "boolean",
@@ -59,7 +59,7 @@ describe("FormBuilderField", () => {
});
const component = {
getFieldInput: ({ label }) => screen.getByRole("textbox", { name: label }) as HTMLInputElement,
getFieldInput: ({ label }: { label: string }) => screen.getByRole("textbox", { name: label }) as HTMLInputElement,
fillFieldInput: ({ label, value }: { label: string; value: string }) => {
fireEvent.change(component.getFieldInput({ label }), { target: { value } });
},
@@ -11,14 +11,14 @@ import { Label } from "@calcom/ui/components/form";
import { Icon } from "@calcom/ui/components/icon";
import { Components, isValidValueProp } from "./Components";
import { fieldTypesConfigMap } from "./fieldTypes";
import { fieldsThatSupportLabelAsSafeHtml } from "./fieldsThatSupportLabelAsSafeHtml";
import type { fieldsSchema } from "./schema";
import { fieldTypesConfigMap } from "@calcom/features/form-builder/fieldTypes";
import { fieldsThatSupportLabelAsSafeHtml } from "@calcom/features/form-builder/fieldsThatSupportLabelAsSafeHtml";
import type { fieldsSchema } from "@calcom/features/form-builder/schema";
import {
useShouldBeDisabledDueToPrefill,
getFieldNameFromErrorMessage,
} from "./useShouldBeDisabledDueToPrefill";
import { getTranslatedConfig as getTranslatedVariantsConfig } from "./utils/variantsConfig";
} from "@calcom/features/form-builder/useShouldBeDisabledDueToPrefill";
import { getTranslatedConfig as getTranslatedVariantsConfig } from "@calcom/features/form-builder/utils/variantsConfig";
// helper to render markdown label safely
const renderLabel = (field: Partial<RhfFormField>) => {
@@ -115,7 +115,6 @@ export default function AppCard({
) : (
<OmniInstallAppButton
className="ml-auto flex items-center"
appId={app.slug}
app={app}
returnTo={returnTo}
teamId={teamId}
@@ -1,12 +1,10 @@
import useApp from "@calcom/features/apps/hooks/useApp";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import classNames from "@calcom/ui/classNames";
import { Button } from "@calcom/ui/components/button";
import { showToast } from "@calcom/ui/components/toast";
import useAddAppMutation from "../_utils/useAddAppMutation";
import { InstallAppButton } from "../InstallAppButton";
import type { AppCardApp } from "../types";
import useAddAppMutation from "../_utils/useAddAppMutation";
type AppData = Pick<AppCardApp, "type" | "variant" | "slug" | "teamsPlanRequired">;
@@ -14,28 +12,23 @@ type AppData = Pick<AppCardApp, "type" | "variant" | "slug" | "teamsPlanRequired
* Use this component to allow installing an app from anywhere on the app.
* Use of this component requires you to remove custom InstallAppButtonComponent so that it can manage the redirection itself.
*
* When `app` prop is provided, it will use that data directly instead of fetching via useApp.
* This is useful when the app data is already available (e.g., from the integrations query)
* to avoid redundant API calls that can cause URL length issues when batched.
* The `app` prop must be provided with the necessary app data.
* This avoids redundant API calls and prevents the app-store package from depending on the features package.
*/
export default function OmniInstallAppButton({
appId,
app: appProp,
app,
className,
returnTo,
teamId,
onAppInstallSuccess,
}: {
appId: string;
app?: AppData;
app: AppData;
className: string;
onAppInstallSuccess: () => void;
returnTo?: string;
teamId?: number;
}) {
const { t } = useLocale();
const { data: fetchedApp } = useApp(appId, { enabled: !appProp });
const app = appProp ?? fetchedApp;
const mutation = useAddAppMutation(null, {
returnTo,
@@ -49,10 +42,6 @@ export default function OmniInstallAppButton({
},
});
if (!app) {
return null;
}
return (
<InstallAppButton
type={app.type}
-14
View File
@@ -1,14 +0,0 @@
import { useSession } from "next-auth/react";
import { trpc } from "@calcom/trpc/react";
export default function useApp(appId: string, options?: { enabled?: boolean }) {
const { status } = useSession();
return trpc.viewer.apps.appById.useQuery(
{ appId },
{
enabled: status === "authenticated" && (options?.enabled ?? true),
}
);
}
+2 -3
View File
@@ -2,13 +2,12 @@ import type { ErrorOption, FieldPath } from "react-hook-form";
import type { RegularBookingCreateResult } from "@calcom/features/bookings/lib/dto/types";
import type { Slots } from "@calcom/features/calendars/lib/types";
import type { PublicEventType } from "@calcom/features/eventtypes/lib/getPublicEvent";
import type { SchedulingType } from "@calcom/prisma/enums";
import type { RouterOutputs } from "@calcom/trpc/react";
import type { AppsStatus } from "@calcom/types/Calendar";
import type { BookingCreateBody } from "./lib/bookingCreateBodySchema";
export type PublicEvent = NonNullable<RouterOutputs["viewer"]["public"]["event"]>;
export type PublicEvent = NonNullable<PublicEventType>;
export type BookerEventQuery = {
isSuccess: boolean;
+244 -24
View File
@@ -1,21 +1,25 @@
import { z } from "zod";
import type { ConnectedApps } from "@calcom/app-store/_utils/getConnectedApps";
import type { EventLocationType } from "@calcom/app-store/locations";
import type { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
import type { TemplateType } from "@calcom/features/calAIPhone/zod-utils";
import type { ChildrenEventType } from "@calcom/features/eventtypes/lib/childrenEventType";
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
import type { AttributesQueryValue } from "@calcom/lib/raqb/types";
import type { EventTypeTranslation } from "@calcom/prisma/client";
import { type PeriodType, SchedulingType } from "@calcom/prisma/enums";
import type { BookerLayoutSettings } from "@calcom/prisma/zod-utils";
import type { customInputSchema } from "@calcom/prisma/zod-utils";
import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
import type { eventTypeColor } from "@calcom/prisma/zod-utils";
import type { RouterOutputs, RouterInputs } from "@calcom/trpc/react";
import type { MembershipRole, PeriodType, SchedulingType } from "@calcom/prisma/enums";
import type {
BookerLayoutSettings,
CustomInputSchema,
customInputSchema,
EventTypeLocation,
EventTypeMetadata,
eventTypeBookingFields,
eventTypeColor,
} from "@calcom/prisma/zod-utils";
import type { RecurringEvent } from "@calcom/types/Calendar";
import { MembershipRole } from "@calcom/prisma/enums";
import type { UserProfile } from "@calcom/types/UserProfile";
import type { z } from "zod";
import type { EventType } from "./getEventTypeById";
export type CustomInputParsed = typeof customInputSchema._output;
export type AvailabilityOption = {
@@ -24,9 +28,9 @@ export type AvailabilityOption = {
isDefault: boolean;
isManaged?: boolean;
};
export type EventTypeSetupProps = RouterOutputs["viewer"]["eventTypes"]["get"];
export type EventTypeSetup = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"];
export type EventTypeApps = RouterOutputs["viewer"]["apps"]["integrations"];
export type EventTypeSetupProps = EventType;
export type EventTypeSetup = EventType["eventType"];
export type EventTypeApps = ConnectedApps;
export type HostLocation = {
id?: string;
userId: number;
@@ -223,7 +227,223 @@ export type EventTypeHosts = {
weight: number | null;
groupId: string | null;
}[];
export type EventTypeUpdateInput = RouterInputs["viewer"]["eventTypesHeavy"]["update"];
// ============================================================================
// EVENT TYPE UPDATE INPUT TYPES
// ============================================================================
// These types define the shape of event type update operations and should be
// consumed by both the features package and tRPC routers.
// ============================================================================
export type HashedLinkInput = {
link: string;
expiresAt?: Date | null;
maxUsageCount?: number | null;
usageCount?: number | null;
};
export type AiPhoneCallConfig = {
generalPrompt: string;
enabled: boolean;
beginMessage: string | null;
yourPhoneNumber: string;
numberToCall: string;
guestName?: string | null;
guestEmail?: string | null;
guestCompany?: string | null;
templateType: TemplateType;
};
export type HostLocationInput = {
id?: string;
userId: number;
eventTypeId: number;
type: string;
credentialId?: number | null;
link?: string | null;
address?: string | null;
phoneNumber?: string | null;
};
export type HostInput = {
userId: number;
profileId?: number | null;
isFixed?: boolean;
priority?: number | null;
weight?: number | null;
scheduleId?: number | null;
groupId?: string | null;
location?: HostLocationInput | null;
};
export type HostGroupInput = {
id: string;
name: string;
};
export type ChildInput = {
owner: {
id: number;
name: string;
email: string;
eventTypeSlugs: string[];
};
hidden: boolean;
};
export type DestinationCalendarInput = {
integration: string;
externalId: string;
} | null;
export type RecurringEventInput = {
dtstart?: Date;
interval: number;
count: number;
freq: number;
until?: Date;
tzid?: string;
} | null;
export type EventTypeColorInput = {
lightEventTypeColor: string;
darkEventTypeColor: string;
} | null;
/**
* Booking field type - minimal type definition for fields that need to be accessed.
* Only includes properties that are actually read in server code.
* Does NOT use an index signature to maintain compatibility with API v2 DTO classes.
*/
export type BookingFieldInput = {
name: string;
hidden?: boolean;
required?: boolean;
type?: string;
};
/**
* RR Segment query value - using index signature for complex RAQB structure.
* The values need to be indexable (string keys) for downstream usage.
*/
export type RRSegmentQueryValueInput = {
[key: string]: unknown;
} | null;
/**
* Explicit type definition for event type update input.
*
* This type is defined explicitly rather than using z.infer<> on a complex
* schema chain to significantly reduce TypeScript type-checking time.
* The schema still validates all fields at runtime.
*
* All fields are optional (from .partial()) except `id` which is required.
*/
export type EventTypeUpdateInput = {
// Required field
id: number;
// Fields from EventTypeSchema (all optional due to .partial())
periodType?: PeriodType;
schedulingType?: SchedulingType | null;
title?: string;
slug?: string;
description?: string | null;
interfaceLanguage?: string | null;
position?: number;
locations?: EventTypeLocation[] | null;
length?: number;
offsetStart?: number;
hidden?: boolean;
userId?: number | null;
profileId?: number | null;
teamId?: number | null;
useEventLevelSelectedCalendars?: boolean;
eventName?: string | null;
parentId?: number | null;
bookingFields?: BookingFieldInput[] | null;
timeZone?: string | null;
periodStartDate?: Date | null;
periodEndDate?: Date | null;
periodDays?: number | null;
periodCountCalendarDays?: boolean | null;
lockTimeZoneToggleOnBookingPage?: boolean;
lockedTimeZone?: string | null;
requiresConfirmation?: boolean;
requiresConfirmationWillBlockSlot?: boolean;
requiresConfirmationForFreeEmail?: boolean;
requiresBookerEmailVerification?: boolean;
canSendCalVideoTranscriptionEmails?: boolean;
autoTranslateDescriptionEnabled?: boolean;
autoTranslateInstantMeetingTitleEnabled?: boolean;
recurringEvent?: RecurringEventInput;
disableGuests?: boolean;
hideCalendarNotes?: boolean;
hideCalendarEventDetails?: boolean;
minimumBookingNotice?: number;
beforeEventBuffer?: number;
afterEventBuffer?: number;
seatsPerTimeSlot?: number | null;
onlyShowFirstAvailableSlot?: boolean;
showOptimizedSlots?: boolean | null;
disableCancelling?: boolean | null;
disableRescheduling?: boolean | null;
minimumRescheduleNotice?: number | null;
seatsShowAttendees?: boolean | null;
seatsShowAvailabilityCount?: boolean | null;
scheduleId?: number | null;
allowReschedulingCancelledBookings?: boolean | null;
price?: number;
currency?: string;
slotInterval?: number | null;
metadata?: EventTypeMetadata;
successRedirectUrl?: string | null;
forwardParamsSuccessRedirect?: boolean | null;
redirectUrlOnNoRoutingFormResponse?: string | null;
bookingLimits?: IntervalLimit | null;
durationLimits?: IntervalLimit | null;
isInstantEvent?: boolean;
instantMeetingExpiryTimeOffsetInSeconds?: number;
instantMeetingScheduleId?: number | null;
instantMeetingParameters?: string[];
assignAllTeamMembers?: boolean;
assignRRMembersUsingSegment?: boolean;
rrSegmentQueryValue?: RRSegmentQueryValueInput;
useEventTypeDestinationCalendarEmail?: boolean;
isRRWeightsEnabled?: boolean;
maxLeadThreshold?: number | null;
includeNoShowInRRCalculation?: boolean;
allowReschedulingPastBookings?: boolean;
hideOrganizerEmail?: boolean;
maxActiveBookingsPerBooker?: number | null;
maxActiveBookingPerBookerOfferReschedule?: boolean;
customReplyToEmail?: string | null;
eventTypeColor?: EventTypeColorInput;
rescheduleWithSameRoundRobinHost?: boolean;
secondaryEmailId?: number | null;
useBookerTimezone?: boolean;
restrictionScheduleId?: number | null;
bookingRequiresAuthentication?: boolean;
rrHostSubsetEnabled?: boolean;
createdAt?: Date | null;
updatedAt?: Date | null;
// Extended fields (all optional due to .partial())
aiPhoneCallConfig?: AiPhoneCallConfig;
calVideoSettings?: CalVideoSettings;
calAiPhoneScript?: string;
customInputs?: CustomInputSchema[];
destinationCalendar?: DestinationCalendarInput;
users?: number[];
children?: ChildInput[];
hosts?: HostInput[];
schedule?: number | null;
instantMeetingSchedule?: number | null;
multiplePrivateLinks?: (string | HashedLinkInput)[];
hostGroups?: HostGroupInput[];
enablePerHostLocations?: boolean;
};
export type TabMap = {
advanced: React.ReactNode;
ai?: React.ReactNode;
@@ -286,13 +506,13 @@ export interface EventTypePlatformWrapperRef {
handleFormSubmit: (callbacks?: { onSuccess?: () => void; onError?: (error: Error) => void }) => void;
}
export interface CalVideoSettings {
disableRecordingForOrganizer?: boolean;
disableRecordingForGuests?: boolean;
enableAutomaticTranscription?: boolean;
enableAutomaticRecordingForOrganizer?: boolean;
disableTranscriptionForGuests?: boolean;
disableTranscriptionForOrganizer?: boolean;
redirectUrlOnExit?: string;
requireEmailForGuests?: boolean;
}
export type CalVideoSettings = {
disableRecordingForGuests?: boolean | null;
disableRecordingForOrganizer?: boolean | null;
enableAutomaticTranscription?: boolean | null;
enableAutomaticRecordingForOrganizer?: boolean | null;
disableTranscriptionForGuests?: boolean | null;
disableTranscriptionForOrganizer?: boolean | null;
redirectUrlOnExit?: string | null;
requireEmailForGuests?: boolean | null;
} | null;
-4
View File
@@ -1,4 +0,0 @@
// TODO: FormBuilder makes more sense in @calcom/ui but it has an additional thing that other components don't have
// It has zod schema associated with it and I currently can't import zod in there.
// Move it later there maybe? @sean
export { FormBuilderField } from "./FormBuilderField";
@@ -1,20 +0,0 @@
export function PermissionProvider({ children }: { children: React.ReactNode }) {
// const { data: permissions, isLoading } = trpc.viewer.pbac.getUserPermissions.useQuery(undefined, {
// // Since permissions don't change often, we can cache them for a while
// staleTime: 5 * 60 * 1000, // 5 minutes
// });
// const setTeamPermissions = usePermissionStore((state) => state.setTeamPermissions);
// useEffect(() => {
// if (permissions) {
// setTeamPermissions(permissions);
// }
// }, [permissions, setTeamPermissions]);
// if (true) {
// return null;
// }
return <>{children}</>;
}
@@ -18,7 +18,6 @@ import dayjs from "@calcom/dayjs";
import { defaultDayRange as DEFAULT_DAY_RANGE } from "@calcom/lib/availability";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { weekdayNames } from "@calcom/lib/weekday";
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
import type { TimeRange } from "@calcom/types/schedule";
import cn from "@calcom/ui/classNames";
import { Button } from "@calcom/ui/components/button";
@@ -1,253 +1,27 @@
import { z } from "zod";
import type { TemplateType } from "@calcom/features/calAIPhone/zod-utils";
import { templateTypeEnum } from "@calcom/features/calAIPhone/zod-utils";
import { MAX_SEATS_PER_TIME_SLOT } from "@calcom/lib/constants";
import type { PeriodType, SchedulingType } from "@calcom/prisma/enums";
import type {
CustomInputSchema,
EventTypeMetadata,
IntervalLimit,
EventTypeLocation,
} from "@calcom/prisma/zod-utils";
AiPhoneCallConfig,
CalVideoSettings,
ChildInput,
DestinationCalendarInput,
EventTypeUpdateInput,
HashedLinkInput,
HostGroupInput,
HostInput,
} from "@calcom/features/eventtypes/lib/types";
import { MAX_SEATS_PER_TIME_SLOT } from "@calcom/lib/constants";
import {
customInputSchema,
EventTypeMetaDataSchema,
rrSegmentQueryValueSchema,
eventTypeLocations,
recurringEventType,
intervalLimitsType,
eventTypeBookingFields,
eventTypeColor,
eventTypeLocations,
intervalLimitsType,
recurringEventType,
rrSegmentQueryValueSchema,
} from "@calcom/prisma/zod-utils";
// ============================================================================
// EXPLICIT TYPE DEFINITIONS
// ============================================================================
// These explicit types prevent TypeScript from having to infer complex types
// through chains of .extend(), .partial(), .merge() etc. This significantly
// reduces type-checking time by giving TypeScript the answer directly.
// ============================================================================
type HashedLinkInput = {
link: string;
expiresAt?: Date | null;
maxUsageCount?: number | null;
usageCount?: number | null;
};
type AiPhoneCallConfig = {
generalPrompt: string;
enabled: boolean;
beginMessage: string | null;
yourPhoneNumber: string;
numberToCall: string;
guestName?: string | null;
guestEmail?: string | null;
guestCompany?: string | null;
templateType: TemplateType;
};
type CalVideoSettings = {
disableRecordingForGuests?: boolean | null;
disableRecordingForOrganizer?: boolean | null;
enableAutomaticTranscription?: boolean | null;
enableAutomaticRecordingForOrganizer?: boolean | null;
disableTranscriptionForGuests?: boolean | null;
disableTranscriptionForOrganizer?: boolean | null;
redirectUrlOnExit?: string | null;
requireEmailForGuests?: boolean | null;
} | null;
type HostLocationInput = {
id?: string;
userId: number;
eventTypeId: number;
type: string;
credentialId?: number | null;
link?: string | null;
address?: string | null;
phoneNumber?: string | null;
};
type HostInput = {
userId: number;
profileId?: number | null;
isFixed?: boolean;
priority?: number | null;
weight?: number | null;
scheduleId?: number | null;
groupId?: string | null;
location?: HostLocationInput | null;
};
type HostGroupInput = {
id: string;
name: string;
};
type ChildInput = {
owner: {
id: number;
name: string;
email: string;
eventTypeSlugs: string[];
};
hidden: boolean;
};
type DestinationCalendarInput = {
integration: string;
externalId: string;
} | null;
type RecurringEventInput = {
dtstart?: Date;
interval: number;
count: number;
freq: number;
until?: Date;
tzid?: string;
} | null;
type EventTypeColorInput = {
lightEventTypeColor: string;
darkEventTypeColor: string;
} | null;
/**
* Booking field type - minimal type definition for fields that need to be accessed.
* Only includes properties that are actually read in server code.
* Does NOT use an index signature to maintain compatibility with API v2 DTO classes.
*/
type BookingFieldInput = {
name: string;
hidden?: boolean;
required?: boolean;
type?: string;
};
/**
* RR Segment query value - using index signature for complex RAQB structure.
* The values need to be indexable (string keys) for downstream usage.
*/
type RRSegmentQueryValueInput = {
[key: string]: unknown;
} | null;
/**
* Explicit type definition for event type update input.
*
* This type is defined explicitly rather than using z.infer<> on a complex
* schema chain to significantly reduce TypeScript type-checking time.
* The schema still validates all fields at runtime.
*
* All fields are optional (from .partial()) except `id` which is required.
*/
export type TUpdateInputSchema = {
// Required field
id: number;
// Fields from EventTypeSchema (all optional due to .partial())
periodType?: PeriodType;
schedulingType?: SchedulingType | null;
title?: string;
slug?: string;
description?: string | null;
interfaceLanguage?: string | null;
position?: number;
locations?: EventTypeLocation[] | null;
length?: number;
offsetStart?: number;
hidden?: boolean;
userId?: number | null;
profileId?: number | null;
teamId?: number | null;
useEventLevelSelectedCalendars?: boolean;
eventName?: string | null;
parentId?: number | null;
bookingFields?: BookingFieldInput[] | null;
timeZone?: string | null;
periodStartDate?: Date | null;
periodEndDate?: Date | null;
periodDays?: number | null;
periodCountCalendarDays?: boolean | null;
lockTimeZoneToggleOnBookingPage?: boolean;
lockedTimeZone?: string | null;
requiresConfirmation?: boolean;
requiresConfirmationWillBlockSlot?: boolean;
requiresConfirmationForFreeEmail?: boolean;
requiresBookerEmailVerification?: boolean;
canSendCalVideoTranscriptionEmails?: boolean;
autoTranslateDescriptionEnabled?: boolean;
autoTranslateInstantMeetingTitleEnabled?: boolean;
recurringEvent?: RecurringEventInput;
disableGuests?: boolean;
hideCalendarNotes?: boolean;
hideCalendarEventDetails?: boolean;
minimumBookingNotice?: number;
beforeEventBuffer?: number;
afterEventBuffer?: number;
seatsPerTimeSlot?: number | null;
onlyShowFirstAvailableSlot?: boolean;
showOptimizedSlots?: boolean | null;
disableCancelling?: boolean | null;
disableRescheduling?: boolean | null;
minimumRescheduleNotice?: number | null;
seatsShowAttendees?: boolean | null;
seatsShowAvailabilityCount?: boolean | null;
scheduleId?: number | null;
allowReschedulingCancelledBookings?: boolean | null;
price?: number;
currency?: string;
slotInterval?: number | null;
metadata?: EventTypeMetadata;
successRedirectUrl?: string | null;
forwardParamsSuccessRedirect?: boolean | null;
redirectUrlOnNoRoutingFormResponse?: string | null;
bookingLimits?: IntervalLimit | null;
durationLimits?: IntervalLimit | null;
isInstantEvent?: boolean;
instantMeetingExpiryTimeOffsetInSeconds?: number;
instantMeetingScheduleId?: number | null;
instantMeetingParameters?: string[];
assignAllTeamMembers?: boolean;
assignRRMembersUsingSegment?: boolean;
rrSegmentQueryValue?: RRSegmentQueryValueInput;
useEventTypeDestinationCalendarEmail?: boolean;
isRRWeightsEnabled?: boolean;
maxLeadThreshold?: number | null;
includeNoShowInRRCalculation?: boolean;
allowReschedulingPastBookings?: boolean;
hideOrganizerEmail?: boolean;
maxActiveBookingsPerBooker?: number | null;
maxActiveBookingPerBookerOfferReschedule?: boolean;
customReplyToEmail?: string | null;
eventTypeColor?: EventTypeColorInput;
rescheduleWithSameRoundRobinHost?: boolean;
secondaryEmailId?: number | null;
useBookerTimezone?: boolean;
restrictionScheduleId?: number | null;
bookingRequiresAuthentication?: boolean;
rrHostSubsetEnabled?: boolean;
createdAt?: Date | null;
updatedAt?: Date | null;
// Extended fields (all optional due to .partial())
aiPhoneCallConfig?: AiPhoneCallConfig;
calVideoSettings?: CalVideoSettings;
calAiPhoneScript?: string;
customInputs?: CustomInputSchema[];
destinationCalendar?: DestinationCalendarInput;
users?: number[];
children?: ChildInput[];
hosts?: HostInput[];
schedule?: number | null;
instantMeetingSchedule?: number | null;
multiplePrivateLinks?: (string | HashedLinkInput)[];
hostGroups?: HostGroupInput[];
enablePerHostLocations?: boolean;
};
import { z } from "zod";
export type TUpdateInputSchema = EventTypeUpdateInput;
// ============================================================================
// ZOD SCHEMAS