feat: Ability to set a locked timezone for event type (#20425)

* feat: ability to set locked timezone

* update

* Update update-event-type.input.ts

* Update get-event-type-public.output.ts

* Update documentation.json

* revert

* nit
This commit is contained in:
Anik Dhabal Babu authored and GitHub committed 2025-05-01 11:09:57 +05:30
1 parent 7ee451ca02
commit 03240709dc
19 files changed
+65 -11

No files matched your search

@@ -59,6 +59,7 @@ type UserPageProps = {
| "length"
| "hidden"
| "lockTimeZoneToggleOnBookingPage"
| "lockedTimeZone"
| "requiresConfirmation"
| "canSendCalVideoTranscriptionEmails"
| "requiresBookerEmailVerification"
@@ -111,6 +111,7 @@ describe("handleChildrenEventTypes", () => {
timeZone,
requiresBookerEmailVerification,
lockTimeZoneToggleOnBookingPage,
lockedTimeZone,
useEventTypeDestinationCalendarEmail,
secondaryEmailId,
autoTranslateDescriptionEnabled,
@@ -136,6 +137,7 @@ describe("handleChildrenEventTypes", () => {
parentId: 1,
users: { connect: [{ id: 4 }] },
lockTimeZoneToggleOnBookingPage: false,
lockedTimeZone: "Europe/London",
requiresBookerEmailVerification: false,
instantMeetingScheduleId: undefined,
bookingLimits: undefined,
@@ -168,6 +170,7 @@ describe("handleChildrenEventTypes", () => {
scheduleId,
requiresBookerEmailVerification,
lockTimeZoneToggleOnBookingPage,
lockedTimeZone,
useEventTypeDestinationCalendarEmail,
secondaryEmailId,
...evType
@@ -198,6 +201,7 @@ describe("handleChildrenEventTypes", () => {
locations: [],
scheduleId: null,
lockTimeZoneToggleOnBookingPage: false,
lockedTimeZone: "Europe/London",
requiresBookerEmailVerification: false,
hashedLink: {
deleteMany: {},
@@ -272,6 +276,7 @@ describe("handleChildrenEventTypes", () => {
timeZone,
requiresBookerEmailVerification,
lockTimeZoneToggleOnBookingPage,
lockedTimeZone,
useEventTypeDestinationCalendarEmail,
secondaryEmailId,
autoTranslateDescriptionEnabled,
@@ -304,6 +309,7 @@ describe("handleChildrenEventTypes", () => {
customReplyToEmail: null,
instantMeetingScheduleId: undefined,
lockTimeZoneToggleOnBookingPage: false,
lockedTimeZone: "Europe/London",
requiresBookerEmailVerification: false,
userId: 4,
workflows: undefined,
@@ -330,6 +336,7 @@ describe("handleChildrenEventTypes", () => {
userId,
requiresBookerEmailVerification,
lockTimeZoneToggleOnBookingPage,
lockedTimeZone,
useEventTypeDestinationCalendarEmail,
secondaryEmailId,
...evType
@@ -363,6 +370,7 @@ describe("handleChildrenEventTypes", () => {
deleteMany: {},
},
lockTimeZoneToggleOnBookingPage: false,
lockedTimeZone: "Europe/London",
requiresBookerEmailVerification: false,
instantMeetingScheduleId: undefined,
},
@@ -393,6 +401,7 @@ describe("handleChildrenEventTypes", () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
requiresBookerEmailVerification,
lockTimeZoneToggleOnBookingPage,
lockedTimeZone,
useEventTypeDestinationCalendarEmail,
secondaryEmailId,
autoTranslateDescriptionEnabled,
@@ -418,6 +427,7 @@ describe("handleChildrenEventTypes", () => {
schedulingType: SchedulingType.MANAGED,
requiresBookerEmailVerification: false,
lockTimeZoneToggleOnBookingPage: false,
lockedTimeZone: "Europe/London",
useEventTypeDestinationCalendarEmail: false,
workflows: [],
parentId: 1,
@@ -452,6 +462,7 @@ describe("handleChildrenEventTypes", () => {
instantMeetingScheduleId: undefined,
locations: [],
lockTimeZoneToggleOnBookingPage: false,
lockedTimeZone: "Europe/London",
requiresBookerEmailVerification: false,
parentId: 1,
userId: 5,
@@ -478,6 +489,7 @@ describe("handleChildrenEventTypes", () => {
customReplyToEmail: null,
rrSegmentQueryValue: undefined,
lockTimeZoneToggleOnBookingPage: false,
lockedTimeZone: "Europe/London",
requiresBookerEmailVerification: false,
hashedLink: {
deleteMany: {},
+1 -1
View File
@@ -245,4 +245,4 @@ export default function TeamEvent(){
</Step>
</Steps>
</Steps>
@@ -13,7 +13,6 @@ import { useTimePreferences } from "@calcom/features/bookings/lib";
import type { BookerEvent } from "@calcom/features/bookings/types";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { markdownToSafeHTMLClient } from "@calcom/lib/markdownToSafeHTMLClient";
import { CURRENT_TIMEZONE } from "@calcom/lib/timezoneConstants";
import type { EventTypeTranslation } from "@calcom/prisma/client";
import { EventTypeAutoTranslatedField } from "@calcom/prisma/enums";
@@ -57,6 +56,7 @@ export const EventMeta = ({
event?: Pick<
BookerEvent,
| "lockTimeZoneToggleOnBookingPage"
| "lockedTimeZone"
| "schedule"
| "seatsPerTimeSlot"
| "subsetOfUsers"
@@ -109,9 +109,9 @@ export const EventMeta = ({
);
useEffect(() => {
//In case the event has lockTimeZone enabled ,set the timezone to event's attached availability timezone
if (event && event?.lockTimeZoneToggleOnBookingPage && event?.schedule?.timeZone) {
setTimezone(event.schedule?.timeZone);
//In case the event has lockTimeZone enabled ,set the timezone to event's locked timezone
if (event && event?.lockTimeZoneToggleOnBookingPage && event?.lockedTimeZone) {
setTimezone(event?.lockedTimeZone);
}
}, [event, setTimezone]);
@@ -225,7 +225,7 @@ export const EventMeta = ({
indicatorsContainer: () => "ml-auto",
container: () => "max-w-full",
}}
value={event.lockTimeZoneToggleOnBookingPage ? CURRENT_TIMEZONE : timezone}
value={event.lockTimeZoneToggleOnBookingPage ? event.lockedTimeZone : timezone}
onChange={({ value }) => {
setTimezone(value);
setBookerStoreTimezone(value);
@@ -58,6 +58,7 @@ export const getEventTypesFromDB = async (eventTypeId: number) => {
periodDays: true,
periodCountCalendarDays: true,
lockTimeZoneToggleOnBookingPage: true,
lockedTimeZone: true,
requiresConfirmation: true,
requiresConfirmationForFreeEmail: true,
requiresBookerEmailVerification: true,
+1
View File
@@ -46,6 +46,7 @@ export type BookerEvent = Pick<
| "price"
| "currency"
| "lockTimeZoneToggleOnBookingPage"
| "lockedTimeZone"
| "schedule"
| "seatsPerTimeSlot"
| "title"
@@ -12,6 +12,7 @@ import {
} from "@calcom/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper";
import getLocationsOptionsForSelect from "@calcom/features/bookings/lib/getLocationOptionsForSelect";
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
import { TimezoneSelect } from "@calcom/features/components/timezone-select";
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
import {
allowDisablingAttendeeConfirmationEmails,
@@ -1002,7 +1003,8 @@ export const EventAdvancedTab = ({
toggleSwitchAtTheEnd={true}
switchContainerClassName={classNames(
"border-subtle rounded-lg border py-6 px-4 sm:px-6",
customClassNames?.timezoneLock?.container
customClassNames?.timezoneLock?.container,
formMethods.getValues("lockTimeZoneToggleOnBookingPage") && "rounded-b-none"
)}
title={t("lock_timezone_toggle_on_booking_page")}
{...lockTimeZoneToggleOnBookingPageLocked}
@@ -1010,7 +1012,31 @@ export const EventAdvancedTab = ({
checked={value}
onCheckedChange={(e) => onChange(e)}
data-testid="lock-timezone-toggle"
/>
childrenClassName="lg:ml-0">
<div className="border-subtle flex flex-col gap-6 rounded-b-lg border border-t-0 p-6">
<div>
<Controller
name="lockedTimeZone"
control={formMethods.control}
render={({ field: { value } }) => (
<>
<Label className="text-default mb-2 block text-sm font-medium">
<>{t("timezone")}</>
</Label>
<TimezoneSelect
id="lockedTimeZone"
value={value}
onChange={(event) => {
if (event)
formMethods.setValue("lockedTimeZone", event.value, { shouldDirty: true });
}}
/>
</>
)}
/>
</div>
</div>
</SettingsToggle>
)}
/>
<Controller
@@ -67,6 +67,7 @@ const getPublicEventSelect = (fetchAllUsers: boolean) => {
disableGuests: true,
metadata: true,
lockTimeZoneToggleOnBookingPage: true,
lockedTimeZone: true,
requiresConfirmation: true,
autoTranslateDescriptionEnabled: true,
fieldTranslations: {
@@ -80,6 +80,7 @@ export type FormValues = {
description: string;
disableGuests: boolean;
lockTimeZoneToggleOnBookingPage: boolean;
lockedTimeZone: string;
requiresConfirmation: boolean;
requiresConfirmationWillBlockSlot: boolean;
requiresConfirmationForFreeEmail: boolean;
+1
View File
@@ -100,6 +100,7 @@ const commons = {
destinationCalendar: null,
team: null,
lockTimeZoneToggleOnBookingPage: false,
lockedTimeZone: "Europe/London",
requiresConfirmation: false,
requiresConfirmationForFreeEmail: false,
requiresBookerEmailVerification: false,
@@ -35,14 +35,14 @@ const getEventTypesWithHiddenFromDB = async (userId: number) => {
const eventTypes = await prisma.$queryRaw<RawEventType[]>`
SELECT data."id", data."title", data."description", data."length", data."schedulingType"::text,
data."recurringEvent", data."slug", data."hidden", data."price", data."currency",
data."lockTimeZoneToggleOnBookingPage", data."requiresConfirmation", data."requiresBookerEmailVerification",
data."lockTimeZoneToggleOnBookingPage", data."lockedTimeZone", data."requiresConfirmation", data."requiresBookerEmailVerification",
data."metadata", data."canSendCalVideoTranscriptionEmails"
FROM (
SELECT "EventType"."id", "EventType"."title", "EventType"."description",
"EventType"."position", "EventType"."length", "EventType"."schedulingType"::text,
"EventType"."recurringEvent", "EventType"."slug", "EventType"."hidden",
"EventType"."price", "EventType"."currency",
"EventType"."lockTimeZoneToggleOnBookingPage", "EventType"."requiresConfirmation",
"EventType"."lockTimeZoneToggleOnBookingPage", "EventType"."lockedTimeZone", "EventType"."requiresConfirmation",
"EventType"."requiresBookerEmailVerification", "EventType"."metadata",
"EventType"."canSendCalVideoTranscriptionEmails"
FROM "EventType"
@@ -52,7 +52,7 @@ const getEventTypesWithHiddenFromDB = async (userId: number) => {
"EventType"."position", "EventType"."length", "EventType"."schedulingType"::text,
"EventType"."recurringEvent", "EventType"."slug", "EventType"."hidden",
"EventType"."price", "EventType"."currency",
"EventType"."lockTimeZoneToggleOnBookingPage", "EventType"."requiresConfirmation",
"EventType"."lockTimeZoneToggleOnBookingPage", "EventType"."lockedTimeZone", "EventType"."requiresConfirmation",
"EventType"."requiresBookerEmailVerification", "EventType"."metadata",
"EventType"."canSendCalVideoTranscriptionEmails"
FROM "EventType"
+1
View File
@@ -28,6 +28,7 @@ export const eventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
periodDays: true,
periodCountCalendarDays: true,
lockTimeZoneToggleOnBookingPage: true,
lockedTimeZone: true,
requiresBookerEmailVerification: true,
disableGuests: true,
disableCancelling: true,
@@ -487,6 +487,7 @@ export class EventTypeRepository {
periodEndDate: true,
periodCountCalendarDays: true,
lockTimeZoneToggleOnBookingPage: true,
lockedTimeZone: true,
requiresConfirmation: true,
requiresConfirmationForFreeEmail: true,
canSendCalVideoTranscriptionEmails: true,
+1
View File
@@ -111,6 +111,7 @@ export const buildEventType = (eventType?: Partial<EventType>): EventType => {
periodCountCalendarDays: null,
recurringEvent: null,
lockTimeZoneToggleOnBookingPage: false,
lockedTimeZone: "Europe/London",
requiresConfirmation: false,
requiresConfirmationForFreeEmail: false,
requiresConfirmationWillBlockSlot: false,
@@ -53,6 +53,7 @@ export const useEventTypeForm = ({
seatsShowAttendees: eventType.seatsShowAttendees,
seatsShowAvailabilityCount: eventType.seatsShowAvailabilityCount,
lockTimeZoneToggleOnBookingPage: eventType.lockTimeZoneToggleOnBookingPage,
lockedTimeZone: eventType.lockedTimeZone,
locations: eventType.locations || [],
destinationCalendar: eventType.destinationCalendar,
recurringEvent: eventType.recurringEvent || null,
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "EventType" ADD COLUMN "lockedTimeZone" TEXT NOT NULL DEFAULT 'Europe/London';
+1
View File
@@ -108,6 +108,7 @@ model EventType {
periodDays Int?
periodCountCalendarDays Boolean?
lockTimeZoneToggleOnBookingPage Boolean @default(false)
lockedTimeZone String @default("Europe/London")
requiresConfirmation Boolean @default(false)
requiresConfirmationWillBlockSlot Boolean @default(false)
requiresConfirmationForFreeEmail Boolean @default(false)
+2
View File
@@ -12,6 +12,7 @@ export const baseEventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
price: true,
currency: true,
lockTimeZoneToggleOnBookingPage: true,
lockedTimeZone: true,
requiresConfirmation: true,
requiresBookerEmailVerification: true,
canSendCalVideoTranscriptionEmails: true,
@@ -31,6 +32,7 @@ export const bookEventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
periodEndDate: true,
recurringEvent: true,
lockTimeZoneToggleOnBookingPage: true,
lockedTimeZone: true,
requiresConfirmation: true,
canSendCalVideoTranscriptionEmails: true,
requiresBookerEmailVerification: true,
+1
View File
@@ -660,6 +660,7 @@ export const allManagedEventTypeProps: { [k in keyof Omit<Prisma.EventTypeSelect
bookingFields: true,
durationLimits: true,
lockTimeZoneToggleOnBookingPage: true,
lockedTimeZone: true,
requiresBookerEmailVerification: true,
assignAllTeamMembers: true,
isRRWeightsEnabled: true,