diff --git a/apps/web/server/lib/[user]/getServerSideProps.ts b/apps/web/server/lib/[user]/getServerSideProps.ts index 9b39ecdf69..4b1a8b8c5a 100644 --- a/apps/web/server/lib/[user]/getServerSideProps.ts +++ b/apps/web/server/lib/[user]/getServerSideProps.ts @@ -59,6 +59,7 @@ type UserPageProps = { | "length" | "hidden" | "lockTimeZoneToggleOnBookingPage" + | "lockedTimeZone" | "requiresConfirmation" | "canSendCalVideoTranscriptionEmails" | "requiresBookerEmailVerification" diff --git a/apps/web/test/lib/handleChildrenEventTypes.test.ts b/apps/web/test/lib/handleChildrenEventTypes.test.ts index 281a37d8db..82021c88ad 100644 --- a/apps/web/test/lib/handleChildrenEventTypes.test.ts +++ b/apps/web/test/lib/handleChildrenEventTypes.test.ts @@ -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: {}, diff --git a/docs/platform/guides/teams-setup.mdx b/docs/platform/guides/teams-setup.mdx index 877396dcd7..2fb88f6836 100644 --- a/docs/platform/guides/teams-setup.mdx +++ b/docs/platform/guides/teams-setup.mdx @@ -245,4 +245,4 @@ export default function TeamEvent(){ - \ No newline at end of file + diff --git a/packages/features/bookings/Booker/components/EventMeta.tsx b/packages/features/bookings/Booker/components/EventMeta.tsx index c4e2c4d9dd..31e7e82d7f 100644 --- a/packages/features/bookings/Booker/components/EventMeta.tsx +++ b/packages/features/bookings/Booker/components/EventMeta.tsx @@ -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); diff --git a/packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts b/packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts index 277fc2451f..4f33094694 100644 --- a/packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts +++ b/packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts @@ -58,6 +58,7 @@ export const getEventTypesFromDB = async (eventTypeId: number) => { periodDays: true, periodCountCalendarDays: true, lockTimeZoneToggleOnBookingPage: true, + lockedTimeZone: true, requiresConfirmation: true, requiresConfirmationForFreeEmail: true, requiresBookerEmailVerification: true, diff --git a/packages/features/bookings/types.ts b/packages/features/bookings/types.ts index 464b454582..2490b6c8f9 100644 --- a/packages/features/bookings/types.ts +++ b/packages/features/bookings/types.ts @@ -46,6 +46,7 @@ export type BookerEvent = Pick< | "price" | "currency" | "lockTimeZoneToggleOnBookingPage" + | "lockedTimeZone" | "schedule" | "seatsPerTimeSlot" | "title" diff --git a/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx b/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx index be706ca4a0..86dd902570 100644 --- a/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx +++ b/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx @@ -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"> +
+
+ ( + <> + + { + if (event) + formMethods.setValue("lockedTimeZone", event.value, { shouldDirty: true }); + }} + /> + + )} + /> +
+
+ )} /> { disableGuests: true, metadata: true, lockTimeZoneToggleOnBookingPage: true, + lockedTimeZone: true, requiresConfirmation: true, autoTranslateDescriptionEnabled: true, fieldTranslations: { diff --git a/packages/features/eventtypes/lib/types.ts b/packages/features/eventtypes/lib/types.ts index 5f98411b6b..181a070669 100644 --- a/packages/features/eventtypes/lib/types.ts +++ b/packages/features/eventtypes/lib/types.ts @@ -80,6 +80,7 @@ export type FormValues = { description: string; disableGuests: boolean; lockTimeZoneToggleOnBookingPage: boolean; + lockedTimeZone: string; requiresConfirmation: boolean; requiresConfirmationWillBlockSlot: boolean; requiresConfirmationForFreeEmail: boolean; diff --git a/packages/lib/defaultEvents.ts b/packages/lib/defaultEvents.ts index 2201f054fe..f0b9e9a0c7 100644 --- a/packages/lib/defaultEvents.ts +++ b/packages/lib/defaultEvents.ts @@ -100,6 +100,7 @@ const commons = { destinationCalendar: null, team: null, lockTimeZoneToggleOnBookingPage: false, + lockedTimeZone: "Europe/London", requiresConfirmation: false, requiresConfirmationForFreeEmail: false, requiresBookerEmailVerification: false, diff --git a/packages/lib/event-types/getEventTypesPublic.ts b/packages/lib/event-types/getEventTypesPublic.ts index 34f462d2e2..a884d21abe 100644 --- a/packages/lib/event-types/getEventTypesPublic.ts +++ b/packages/lib/event-types/getEventTypesPublic.ts @@ -35,14 +35,14 @@ const getEventTypesWithHiddenFromDB = async (userId: number) => { const eventTypes = await prisma.$queryRaw` 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" diff --git a/packages/lib/server/eventTypeSelect.ts b/packages/lib/server/eventTypeSelect.ts index 53ca0cfad2..38b8ad6e18 100644 --- a/packages/lib/server/eventTypeSelect.ts +++ b/packages/lib/server/eventTypeSelect.ts @@ -28,6 +28,7 @@ export const eventTypeSelect = Prisma.validator()({ periodDays: true, periodCountCalendarDays: true, lockTimeZoneToggleOnBookingPage: true, + lockedTimeZone: true, requiresBookerEmailVerification: true, disableGuests: true, disableCancelling: true, diff --git a/packages/lib/server/repository/eventType.ts b/packages/lib/server/repository/eventType.ts index 120574d138..53a816c54f 100644 --- a/packages/lib/server/repository/eventType.ts +++ b/packages/lib/server/repository/eventType.ts @@ -487,6 +487,7 @@ export class EventTypeRepository { periodEndDate: true, periodCountCalendarDays: true, lockTimeZoneToggleOnBookingPage: true, + lockedTimeZone: true, requiresConfirmation: true, requiresConfirmationForFreeEmail: true, canSendCalVideoTranscriptionEmails: true, diff --git a/packages/lib/test/builder.ts b/packages/lib/test/builder.ts index 7640d0eba6..cbd6e5552b 100644 --- a/packages/lib/test/builder.ts +++ b/packages/lib/test/builder.ts @@ -111,6 +111,7 @@ export const buildEventType = (eventType?: Partial): EventType => { periodCountCalendarDays: null, recurringEvent: null, lockTimeZoneToggleOnBookingPage: false, + lockedTimeZone: "Europe/London", requiresConfirmation: false, requiresConfirmationForFreeEmail: false, requiresConfirmationWillBlockSlot: false, diff --git a/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts b/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts index a8fe9f9669..7fbbf50ff7 100644 --- a/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts +++ b/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts @@ -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, diff --git a/packages/prisma/migrations/20250328085029_event_locked_timezone/migration.sql b/packages/prisma/migrations/20250328085029_event_locked_timezone/migration.sql new file mode 100644 index 0000000000..d7436146e5 --- /dev/null +++ b/packages/prisma/migrations/20250328085029_event_locked_timezone/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "EventType" ADD COLUMN "lockedTimeZone" TEXT NOT NULL DEFAULT 'Europe/London'; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index f202ec8cd4..20588024f2 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -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) diff --git a/packages/prisma/selects/event-types.ts b/packages/prisma/selects/event-types.ts index 75c2dd83fa..e5c79bea22 100644 --- a/packages/prisma/selects/event-types.ts +++ b/packages/prisma/selects/event-types.ts @@ -12,6 +12,7 @@ export const baseEventTypeSelect = Prisma.validator()({ price: true, currency: true, lockTimeZoneToggleOnBookingPage: true, + lockedTimeZone: true, requiresConfirmation: true, requiresBookerEmailVerification: true, canSendCalVideoTranscriptionEmails: true, @@ -31,6 +32,7 @@ export const bookEventTypeSelect = Prisma.validator()({ periodEndDate: true, recurringEvent: true, lockTimeZoneToggleOnBookingPage: true, + lockedTimeZone: true, requiresConfirmation: true, canSendCalVideoTranscriptionEmails: true, requiresBookerEmailVerification: true, diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index 6fbaafa8dc..0c0753af7d 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -660,6 +660,7 @@ export const allManagedEventTypeProps: { [k in keyof Omit