From bf6dd665f0df561ec63c64337d8ea765d6e77427 Mon Sep 17 00:00:00 2001 From: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:27:30 -0400 Subject: [PATCH 01/23] fix: response size scheduleEmailReminder (#12057) Co-authored-by: CarinaWolli --- .../ee/workflows/api/scheduleEmailReminders.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/features/ee/workflows/api/scheduleEmailReminders.ts b/packages/features/ee/workflows/api/scheduleEmailReminders.ts index 3ce801fb0f..5755d7149e 100644 --- a/packages/features/ee/workflows/api/scheduleEmailReminders.ts +++ b/packages/features/ee/workflows/api/scheduleEmailReminders.ts @@ -14,6 +14,7 @@ import { parseRecurringEvent } from "@calcom/lib"; import { defaultHandler } from "@calcom/lib/server"; import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat"; import prisma from "@calcom/prisma"; +import type { User } from "@calcom/prisma/client"; import { WorkflowActions, WorkflowMethods, WorkflowTemplates } from "@calcom/prisma/enums"; import { bookingMetadataSchema } from "@calcom/prisma/zod-utils"; @@ -29,14 +30,14 @@ sgMail.setApiKey(sendgridAPIKey); type Booking = Prisma.BookingGetPayload<{ include: { eventType: true; - user: true; attendees: true; }; }>; function getiCalEventAsString( - booking: Pick & { + booking: Pick & { eventType: { recurringEvent?: Prisma.JsonValue; title?: string } | null; + user: Partial | null; } ) { let recurrenceRule: string | undefined = undefined; @@ -234,7 +235,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { endTime: true, location: true, description: true, - user: true, + user: { + select: { + email: true, + name: true, + timeZone: true, + locale: true, + username: true, + timeFormat: true, + hideBranding: true, + }, + }, metadata: true, uid: true, customInputs: true, From 687669ce179fbc44d4749fe8a9b85752fd846b3e Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Tue, 24 Oct 2023 15:31:04 +0000 Subject: [PATCH 02/23] New Crowdin translations by Github Action --- apps/web/public/static/locales/fr/common.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/public/static/locales/fr/common.json b/apps/web/public/static/locales/fr/common.json index 9d7cb5be31..6aa814498a 100644 --- a/apps/web/public/static/locales/fr/common.json +++ b/apps/web/public/static/locales/fr/common.json @@ -1600,6 +1600,7 @@ "options": "Options", "enter_option": "Entrer l'option {{index}}", "add_an_option": "Ajouter une option", + "location_already_exists": "Ce lieu existe déjà. Veuillez en sélectionner un nouveau.", "radio": "Radio", "google_meet_warning": "Pour utiliser Google Meet, vous devez définir votre calendrier de destination sur un calendrier Google", "individual": "Particulier", From a8c03262c2db4d4e35d198a765b69191ab0f5d0c Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Tue, 24 Oct 2023 20:15:17 +0100 Subject: [PATCH 03/23] fix: re-render on booker (#12058) --- .../bookings/Booker/components/EventMeta.tsx | 8 -------- .../bookings/components/AvailableTimes.tsx | 14 ++++++++------ .../bookings/lib/useCheckOverlapWithOverlay.tsx | 10 +++++++++- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/features/bookings/Booker/components/EventMeta.tsx b/packages/features/bookings/Booker/components/EventMeta.tsx index 6a6fb5ee78..83bcfc6312 100644 --- a/packages/features/bookings/Booker/components/EventMeta.tsx +++ b/packages/features/bookings/Booker/components/EventMeta.tsx @@ -1,6 +1,5 @@ import { m } from "framer-motion"; import dynamic from "next/dynamic"; -import { useEffect } from "react"; import { shallow } from "zustand/shallow"; import { useEmbedUiConfig, useIsEmbed } from "@calcom/embed-core/embed-iframe"; @@ -38,13 +37,6 @@ export const EventMeta = () => { const isEmbed = useIsEmbed(); const hideEventTypeDetails = isEmbed ? embedUiConfig.hideEventTypeDetails : false; - useEffect(() => { - if (!selectedDuration && event?.length) { - setSelectedDuration(event.length); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [event?.length, selectedDuration]); - if (hideEventTypeDetails) { return null; } diff --git a/packages/features/bookings/components/AvailableTimes.tsx b/packages/features/bookings/components/AvailableTimes.tsx index 509056d5a3..1daa55e284 100644 --- a/packages/features/bookings/components/AvailableTimes.tsx +++ b/packages/features/bookings/components/AvailableTimes.tsx @@ -11,6 +11,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Button, SkeletonText } from "@calcom/ui"; import { useBookerStore } from "../Booker/store"; +import { useEvent } from "../Booker/utils/event"; import { getQueryParam } from "../Booker/utils/query-param"; import { useTimePreferences } from "../lib"; import { useCheckOverlapWithOverlay } from "../lib/useCheckOverlapWithOverlay"; @@ -51,9 +52,9 @@ const SlotItem = ({ const overlayCalendarToggled = getQueryParam("overlayCalendar") === "true" || localStorage.getItem("overlayCalendarSwitchDefault"); const [timeFormat, timezone] = useTimePreferences((state) => [state.timeFormat, state.timezone]); - const selectedDuration = useBookerStore((state) => state.selectedDuration); const bookingData = useBookerStore((state) => state.bookingData); const layout = useBookerStore((state) => state.layout); + const { data: event } = useEvent(); const hasTimeSlots = !!seatsPerTimeSlot; const computedDateWithUsersTimezone = dayjs.utc(slot.time).tz(timezone); @@ -67,11 +68,12 @@ const SlotItem = ({ const offset = (usersTimezoneDate.utcOffset() - nowDate.utcOffset()) / 60; - const { isOverlapping, overlappingTimeEnd, overlappingTimeStart } = useCheckOverlapWithOverlay( - computedDateWithUsersTimezone, - selectedDuration, - offset - ); + const { isOverlapping, overlappingTimeEnd, overlappingTimeStart } = useCheckOverlapWithOverlay({ + start: computedDateWithUsersTimezone, + selectedDuration: event?.length ?? 0, + offset, + }); + const [overlapConfirm, setOverlapConfirm] = useState(false); const onButtonClick = useCallback(() => { diff --git a/packages/features/bookings/lib/useCheckOverlapWithOverlay.tsx b/packages/features/bookings/lib/useCheckOverlapWithOverlay.tsx index a1a3020da8..ba994ee7f7 100644 --- a/packages/features/bookings/lib/useCheckOverlapWithOverlay.tsx +++ b/packages/features/bookings/lib/useCheckOverlapWithOverlay.tsx @@ -9,7 +9,15 @@ function getCurrentTime(date: Date) { return `${hours}:${minutes}`; } -export function useCheckOverlapWithOverlay(start: Dayjs, selectedDuration: number | null, offset: number) { +export function useCheckOverlapWithOverlay({ + start, + selectedDuration, + offset, +}: { + start: Dayjs; + selectedDuration: number | null; + offset: number; +}) { const overlayBusyDates = useOverlayCalendarStore((state) => state.overlayBusyDates); let overlappingTimeStart: string | null = null; From 0ae6506bc13bd720ea3583504f9cfb89237aa8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20L=C3=B3pez?= Date: Tue, 24 Oct 2023 12:59:15 -0700 Subject: [PATCH 04/23] fix: prevents prisma idle connections (#12068) --- packages/prisma/index.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/prisma/index.ts b/packages/prisma/index.ts index 071f6fdbbe..b045ff8fb6 100644 --- a/packages/prisma/index.ts +++ b/packages/prisma/index.ts @@ -6,9 +6,16 @@ import { bookingReferenceMiddleware } from "./middleware"; const prismaOptions: Prisma.PrismaClientOptions = {}; +const globalForPrisma = global as unknown as { + prismaWithoutClientExtensions: PrismaClientWithoutExtension; + prismaWithClientExtensions: PrismaClientWithExtensions; +}; + if (!!process.env.NEXT_PUBLIC_DEBUG) prismaOptions.log = ["query", "error", "warn"]; -const prismaWithoutClientExtensions = new PrismaClientWithoutExtension(prismaOptions); +// Prevents flooding with idle connections +const prismaWithoutClientExtensions = + globalForPrisma.prismaWithoutClientExtensions || new PrismaClientWithoutExtension(prismaOptions); export const customPrisma = (options?: Prisma.PrismaClientOptions) => new PrismaClientWithoutExtension({ ...prismaOptions, ...options }).$extends(withAccelerate()); @@ -50,16 +57,15 @@ const prismaWithClientExtensions = prismaWithoutClientExtensions // }, // }) -// const prismaWithClientExtensions = prismaWithoutClientExtensions; - -export const prisma = - ((globalThis as any).prisma as typeof prismaWithClientExtensions) || prismaWithClientExtensions; +export const prisma = globalForPrisma.prismaWithClientExtensions || prismaWithClientExtensions; if (process.env.NODE_ENV !== "production") { - (globalThis as any).prisma = prisma; + globalForPrisma.prismaWithoutClientExtensions = prismaWithoutClientExtensions; + globalForPrisma.prismaWithClientExtensions = prisma; } -export type PrismaClient = typeof prismaWithClientExtensions; +type PrismaClientWithExtensions = typeof prismaWithClientExtensions; +export type PrismaClient = PrismaClientWithExtensions; export default prisma; export * from "./selects"; From a9535d3fd4590959599bfc7565092ebec0cf5879 Mon Sep 17 00:00:00 2001 From: Greg Pabian <35925521+grzpab@users.noreply.github.com> Date: Tue, 24 Oct 2023 22:52:59 +0200 Subject: [PATCH 05/23] chore: [app dir bootstrapping 4.1] check nullability of navigation hook return values part 2 (#12065) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Omar López --- .../OverlayCalendar/OverlayCalendarSettingsModal.tsx | 2 +- packages/features/ee/payments/components/Payment.tsx | 2 +- packages/features/embed/Embed.tsx | 6 +++--- packages/lib/payment/handlePayment.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSettingsModal.tsx b/packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSettingsModal.tsx index 118265be69..f5731fd17d 100644 --- a/packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSettingsModal.tsx +++ b/packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSettingsModal.tsx @@ -47,7 +47,7 @@ export function OverlayCalendarSettingsModal(props: IOverlayCalendarContinueModa const searchParams = useSearchParams(); const setOverlayBusyDates = useOverlayCalendarStore((state) => state.setOverlayBusyDates); const { data, isLoading } = trpc.viewer.connectedCalendars.useQuery(undefined, { - enabled: !!props.open || !!searchParams.get("overlayCalendar"), + enabled: !!props.open || Boolean(searchParams?.get("overlayCalendar")), }); const { toggleValue, hasItem, set } = useLocalSet<{ credentialId: number; diff --git a/packages/features/ee/payments/components/Payment.tsx b/packages/features/ee/payments/components/Payment.tsx index 4311e9036c..dc575e6320 100644 --- a/packages/features/ee/payments/components/Payment.tsx +++ b/packages/features/ee/payments/components/Payment.tsx @@ -94,7 +94,7 @@ const PaymentForm = (props: Props) => { location?: string; } = { uid: props.booking.uid, - email: searchParams.get("email"), + email: searchParams?.get("email"), }; if (paymentOption === "HOLD" && "setupIntent" in props.payment.data) { payload = await stripe.confirmSetup({ diff --git a/packages/features/embed/Embed.tsx b/packages/features/embed/Embed.tsx index 69df6baaf6..2a39590dc9 100644 --- a/packages/features/embed/Embed.tsx +++ b/packages/features/embed/Embed.tsx @@ -61,7 +61,7 @@ function useRouterHelpers() { const pathname = usePathname(); const goto = (newSearchParams: Record) => { - const newQuery = new URLSearchParams(searchParams); + const newQuery = new URLSearchParams(searchParams ?? undefined); Object.keys(newSearchParams).forEach((key) => { newQuery.set(key, newSearchParams[key]); }); @@ -70,7 +70,7 @@ function useRouterHelpers() { }; const removeQueryParams = (queryParams: string[]) => { - const params = new URLSearchParams(searchParams); + const params = new URLSearchParams(searchParams ?? undefined); queryParams.forEach((param) => { params.delete(param); @@ -529,7 +529,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({ ); const s = (href: string) => { - const _searchParams = new URLSearchParams(searchParams); + const _searchParams = new URLSearchParams(searchParams ?? undefined); const [a, b] = href.split("="); _searchParams.set(a, b); return `${pathname?.split("?")[0] ?? ""}?${_searchParams.toString()}`; diff --git a/packages/lib/payment/handlePayment.ts b/packages/lib/payment/handlePayment.ts index 9b3aa85be2..7f096a1e30 100644 --- a/packages/lib/payment/handlePayment.ts +++ b/packages/lib/payment/handlePayment.ts @@ -2,13 +2,13 @@ import type { AppCategories, Prisma } from "@prisma/client"; import appStore from "@calcom/app-store"; import type { EventTypeAppsList } from "@calcom/app-store/utils"; -import type { EventTypeModel } from "@calcom/prisma/zod"; +import type { CompleteEventType } from "@calcom/prisma/zod"; import type { CalendarEvent } from "@calcom/types/Calendar"; import type { IAbstractPaymentService, PaymentApp } from "@calcom/types/PaymentService"; const handlePayment = async ( evt: CalendarEvent, - selectedEventType: Pick, "metadata" | "title">, + selectedEventType: Pick, paymentAppCredentials: { key: Prisma.JsonValue; appId: EventTypeAppsList; From 79c1aa60a2676ce5d1d96df1c98a7c6a30932cde Mon Sep 17 00:00:00 2001 From: Morgan <33722304+ThyMinimalDev@users.noreply.github.com> Date: Wed, 25 Oct 2023 00:34:27 +0300 Subject: [PATCH 06/23] perf: database index on booking_status_starttime_endtime (#12066) --- .../migration.sql | 2 ++ packages/prisma/schema.prisma | 1 + 2 files changed, 3 insertions(+) create mode 100644 packages/prisma/migrations/20231024173642_idx_booking_status_starttime_endtime/migration.sql diff --git a/packages/prisma/migrations/20231024173642_idx_booking_status_starttime_endtime/migration.sql b/packages/prisma/migrations/20231024173642_idx_booking_status_starttime_endtime/migration.sql new file mode 100644 index 0000000000..8ac1699440 --- /dev/null +++ b/packages/prisma/migrations/20231024173642_idx_booking_status_starttime_endtime/migration.sql @@ -0,0 +1,2 @@ +-- CreateIndex +CREATE INDEX "Booking_startTime_endTime_status_idx" ON "Booking"("startTime", "endTime", "status"); diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 78c0ce069c..7eee884084 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -423,6 +423,7 @@ model Booking { @@index([recurringEventId]) @@index([uid]) @@index([status]) + @@index([startTime, endTime, status]) } model Schedule { From af801df421ed210987a577c954ecf8cffbc9bf97 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Wed, 25 Oct 2023 15:57:29 +0530 Subject: [PATCH 07/23] Fixes in teams and avatar across org (#12070) --- apps/web/pages/event-types/index.tsx | 17 ++------ packages/lib/getEventTypeById.ts | 14 ++----- .../viewer/eventTypes/getByViewer.handler.ts | 1 + .../routers/viewer/teams/list.handler.ts | 39 ++----------------- 4 files changed, 12 insertions(+), 59 deletions(-) diff --git a/apps/web/pages/event-types/index.tsx b/apps/web/pages/event-types/index.tsx index bd87521314..4d68795e92 100644 --- a/apps/web/pages/event-types/index.tsx +++ b/apps/web/pages/event-types/index.tsx @@ -72,6 +72,7 @@ import useMeQuery from "@lib/hooks/useMeQuery"; import PageWrapper from "@components/PageWrapper"; import SkeletonLoader from "@components/eventtype/SkeletonLoader"; +import { UserAvatarGroup } from "@components/ui/avatar/UserAvatarGroup"; type EventTypeGroups = RouterOutputs["viewer"]["eventTypes"]["getByViewer"]["eventTypeGroups"]; type EventTypeGroupProfile = EventTypeGroups[number]["profile"]; @@ -398,23 +399,11 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
{type.team && !isManagedEventType && ( - ({ - alt: organizer.name || "", - image: `${orgBranding?.fullDomain ?? WEBAPP_URL}/${ - organizer.username - }/avatar.png`, - title: organizer.name || "", - }) - ) - : [] - } + users={type?.users ?? []} /> )} {isManagedEventType && type?.children && type.children?.length > 0 && ( diff --git a/packages/lib/getEventTypeById.ts b/packages/lib/getEventTypeById.ts index 7637446d80..7ce86719fd 100644 --- a/packages/lib/getEventTypeById.ts +++ b/packages/lib/getEventTypeById.ts @@ -4,10 +4,9 @@ import { getLocationGroupedOptions } from "@calcom/app-store/server"; import type { StripeData } from "@calcom/app-store/stripepayment/lib/server"; import { getEventTypeAppData } from "@calcom/app-store/utils"; import type { LocationObject } from "@calcom/core/location"; -import { getOrgFullOrigin } from "@calcom/ee/organizations/lib/orgDomains"; import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields"; import { parseBookingLimit, parseDurationLimit, parseRecurringEvent } from "@calcom/lib"; -import { CAL_URL } from "@calcom/lib/constants"; +import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl"; import { getTranslation } from "@calcom/lib/server/i18n"; import type { PrismaClient } from "@calcom/prisma"; import type { Credential } from "@calcom/prisma/client"; @@ -36,6 +35,7 @@ export default async function getEventTypeById({ username: true, id: true, email: true, + organizationId: true, locale: true, defaultScheduleId: true, }); @@ -298,9 +298,7 @@ export default async function getEventTypeById({ const eventTypeUsers: ((typeof eventType.users)[number] & { avatar: string })[] = eventType.users.map( (user) => ({ ...user, - avatar: `${eventType.team?.parent?.slug ? getOrgFullOrigin(eventType.team?.parent?.slug) : CAL_URL}/${ - user.username - }/avatar.png`, + avatar: getUserAvatarUrl(user), }) ); @@ -346,11 +344,7 @@ export default async function getEventTypeById({ .map((member) => { const user: typeof member.user & { avatar: string } = { ...member.user, - avatar: `${ - eventTypeObject.team?.parent?.slug - ? getOrgFullOrigin(eventTypeObject.team?.parent?.slug) - : CAL_URL - }/${member.user.username}/avatar.png`, + avatar: getUserAvatarUrl(member.user), }; return { ...user, diff --git a/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts b/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts index 6c154cdb30..83f8089c88 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts @@ -30,6 +30,7 @@ const userSelect = Prisma.validator()({ id: true, username: true, name: true, + organizationId: true, }); const userEventTypeSelect = Prisma.validator()({ diff --git a/packages/trpc/server/routers/viewer/teams/list.handler.ts b/packages/trpc/server/routers/viewer/teams/list.handler.ts index 4aa8947e19..44220804aa 100644 --- a/packages/trpc/server/routers/viewer/teams/list.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/list.handler.ts @@ -1,4 +1,3 @@ -import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations"; import { prisma } from "@calcom/prisma"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; @@ -11,42 +10,12 @@ type ListOptions = { }; export const listHandler = async ({ ctx }: ListOptions) => { - if (ctx.user?.organization?.id) { - const membershipsWithoutParent = await prisma.membership.findMany({ - where: { - userId: ctx.user.id, - team: { - parent: { - is: { - id: ctx.user?.organization?.id, - }, - }, - }, - }, - include: { - team: { - include: { - inviteTokens: true, - }, - }, - }, - orderBy: { role: "desc" }, - }); - - const isOrgAdmin = !!(await isOrganisationAdmin(ctx.user.id, ctx.user.organization.id)); // Org id exists here as we're inside a conditional TS complaining for some reason - - return membershipsWithoutParent.map(({ team: { inviteTokens, ..._team }, ...membership }) => ({ - role: membership.role, - accepted: membership.accepted, - isOrgAdmin, - ..._team, - /** To prevent breaking we only return non-email attached token here, if we have one */ - inviteToken: inviteTokens.find((token) => token.identifier === `invite-link-for-teamId-${_team.id}`), - })); - } - const memberships = await prisma.membership.findMany({ where: { + // Show all the teams this user belongs to regardless of the team being part of the user's org or not + // We don't want to restrict in the listing here. If we need to restrict a situation where a user is part of the org along with being part of a non-org team, we should do that instead of filtering out from here + // This became necessary when we started migrating user to Org, without migrating some teams of the user to the org + // Also, we would allow a user to be part of multiple orgs, then also it would be necessary. userId: ctx.user.id, }, include: { From 327159c2ae450eb8090e19d42ba9d057937a44e4 Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Wed, 25 Oct 2023 11:41:25 +0100 Subject: [PATCH 08/23] fix/profile-dont-wait-for-avatar (#12080) * fix/profile-dont-wait-for-avatar * Update apps/web/pages/settings/my-account/profile.tsx * Update apps/web/pages/settings/my-account/profile.tsx --------- Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> --- apps/web/pages/settings/my-account/profile.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/web/pages/settings/my-account/profile.tsx b/apps/web/pages/settings/my-account/profile.tsx index bfe82cdc27..7b1d873dd8 100644 --- a/apps/web/pages/settings/my-account/profile.tsx +++ b/apps/web/pages/settings/my-account/profile.tsx @@ -78,8 +78,8 @@ type FormValues = { bio: string; }; -const checkIfItFallbackImage = (fetchedImgSrc: string) => { - return fetchedImgSrc.endsWith(AVATAR_FALLBACK); +const checkIfItFallbackImage = (fetchedImgSrc?: string) => { + return !fetchedImgSrc || fetchedImgSrc.endsWith(AVATAR_FALLBACK); }; const ProfileView = () => { @@ -226,10 +226,11 @@ const ProfileView = () => { [ErrorCode.ThirdPartyIdentityProviderEnabled]: t("account_created_with_identity_provider"), }; - if (isLoading || !user || fetchedImgSrc === undefined) + if (isLoading || !user) { return ( ); + } const defaultValues = { username: user.username || "", From efc7be0b6bb01f5ca3bd68b7bfd4230a4be510bd Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 25 Oct 2023 14:18:25 +0100 Subject: [PATCH 09/23] fix: Infinite loop in timezones on the negative side of UTC (#12063) * fix: Infinite loop in timezones on the negative side of UTC * Update packages/features/calendars/lib/getAvailableDatesInMonth.test.ts * Revert back to real system time after test * Handle all dates as local time, given this all happens in the browser --- .github/workflows/test.yml | 2 ++ ...getAvailableDatesInMonth.timezone.test.ts} | 32 +++++++++++++++++-- .../calendars/lib/getAvailableDatesInMonth.ts | 8 +++-- vitest.workspace.ts | 21 ++++++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) rename packages/features/calendars/lib/{getAvailableDatesInMonth.test.ts => getAvailableDatesInMonth.timezone.test.ts} (53%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fcc7c6ed1f..d8ca18d282 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,3 +15,5 @@ jobs: - uses: ./.github/actions/yarn-install # Should be an 8GB machine as per https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners - run: yarn test + # We could add different timezones here that we need to run our tests in + - run: TZ=America/Los_Angeles yarn test -- --timeZoneDependentTestsOnly diff --git a/packages/features/calendars/lib/getAvailableDatesInMonth.test.ts b/packages/features/calendars/lib/getAvailableDatesInMonth.timezone.test.ts similarity index 53% rename from packages/features/calendars/lib/getAvailableDatesInMonth.test.ts rename to packages/features/calendars/lib/getAvailableDatesInMonth.timezone.test.ts index 10e8fdc147..c8475e3311 100644 --- a/packages/features/calendars/lib/getAvailableDatesInMonth.test.ts +++ b/packages/features/calendars/lib/getAvailableDatesInMonth.timezone.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from "vitest"; +import { describe, expect, test, vi } from "vitest"; import { getAvailableDatesInMonth } from "@calcom/features/calendars/lib/getAvailableDatesInMonth"; import { daysInMonth, yyyymmdd } from "@calcom/lib/date-fns"; @@ -8,7 +8,7 @@ describe("Test Suite: Date Picker", () => { // *) Use right amount of days in given month. (28, 30, 31) test("it returns the right amount of days in a given month", () => { const currentDate = new Date(); - const nextMonthDate = new Date(Date.UTC(currentDate.getFullYear(), currentDate.getMonth() + 1)); + const nextMonthDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1); const result = getAvailableDatesInMonth({ browsingDate: nextMonthDate, @@ -35,5 +35,33 @@ describe("Test Suite: Date Picker", () => { expect(result).toHaveLength(1); }); + + test("it translates correctly regardless of system time", () => { + { + // test a date in negative UTC offset + vi.useFakeTimers().setSystemTime(new Date("2023-10-24T13:27:00.000-07:00")); + + const currentDate = new Date(); + const result = getAvailableDatesInMonth({ + browsingDate: currentDate, + }); + + expect(result).toHaveLength(daysInMonth(currentDate) - currentDate.getDate() + 1); + } + { + // test a date in positive UTC offset + vi.useFakeTimers().setSystemTime(new Date("2023-10-24T13:27:00.000+07:00")); + + const currentDate = new Date(); + const result = getAvailableDatesInMonth({ + browsingDate: currentDate, + }); + + expect(result).toHaveLength(daysInMonth(currentDate) - currentDate.getDate() + 1); + } + // Undo the forced time we applied earlier, reset to system default. + vi.setSystemTime(vi.getRealSystemTime()); + vi.useRealTimers(); + }); }); }); diff --git a/packages/features/calendars/lib/getAvailableDatesInMonth.ts b/packages/features/calendars/lib/getAvailableDatesInMonth.ts index 8fbace876b..8e50ef9793 100644 --- a/packages/features/calendars/lib/getAvailableDatesInMonth.ts +++ b/packages/features/calendars/lib/getAvailableDatesInMonth.ts @@ -5,7 +5,7 @@ import { daysInMonth, yyyymmdd } from "@calcom/lib/date-fns"; // *) Dates in the past are not available. // *) Use right amount of days in given month. (28, 30, 31) export function getAvailableDatesInMonth({ - browsingDate, // pass as UTC + browsingDate, minDate = new Date(), includedDates, }: { @@ -15,12 +15,14 @@ export function getAvailableDatesInMonth({ }) { const dates = []; const lastDateOfMonth = new Date( - Date.UTC(browsingDate.getFullYear(), browsingDate.getMonth(), daysInMonth(browsingDate)) + browsingDate.getFullYear(), + browsingDate.getMonth(), + daysInMonth(browsingDate) ); for ( let date = browsingDate > minDate ? browsingDate : minDate; date <= lastDateOfMonth; - date = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 1)) + date = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1) ) { // intersect included dates if (includedDates && !includedDates.includes(yyyymmdd(date))) { diff --git a/vitest.workspace.ts b/vitest.workspace.ts index 587aeb8cbf..20d12799fb 100644 --- a/vitest.workspace.ts +++ b/vitest.workspace.ts @@ -1,6 +1,13 @@ import { defineWorkspace } from "vitest/config"; const packagedEmbedTestsOnly = process.argv.includes("--packaged-embed-tests-only"); +const timeZoneDependentTestsOnly = process.argv.includes("--timeZoneDependentTestsOnly"); +// eslint-disable-next-line turbo/no-undeclared-env-vars +const envTZ = process.env.TZ; +if (timeZoneDependentTestsOnly && !envTZ) { + throw new Error("TZ environment variable is not set"); +} + // defineWorkspace provides a nice type hinting DX const workspaces = packagedEmbedTestsOnly ? [ @@ -11,6 +18,19 @@ const workspaces = packagedEmbedTestsOnly }, }, ] + : // It doesn't seem to be possible to fake timezone per test, so we rerun the entire suite with different TZ. See https://github.com/vitest-dev/vitest/issues/1575#issuecomment-1439286286 + timeZoneDependentTestsOnly + ? [ + { + test: { + name: `TimezoneDependentTests:${envTZ}`, + include: ["packages/**/*.timezone.test.ts", "apps/**/*.timezone.test.ts"], + // TODO: Ignore the api until tests are fixed + exclude: ["**/node_modules/**/*", "packages/embeds/**/*"], + setupFiles: ["setupVitest.ts"], + }, + }, + ] : [ { test: { @@ -20,6 +40,7 @@ const workspaces = packagedEmbedTestsOnly setupFiles: ["setupVitest.ts"], }, }, + { test: { name: "@calcom/closecom", From 1929b23ea83b81ee102c016dfa5a4b554025d496 Mon Sep 17 00:00:00 2001 From: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Date: Wed, 25 Oct 2023 17:21:14 +0400 Subject: [PATCH 10/23] Update handleNewBooking.ts (#12081) --- packages/features/bookings/lib/handleNewBooking.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index 83886bab35..fa810f1bf8 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -2378,6 +2378,7 @@ async function handler( ...eventTypeInfo, bookingId: booking?.id, rescheduleUid, + oldBookingId: originalRescheduledBooking?.id || undefined, rescheduleStartTime: originalRescheduledBooking?.startTime ? dayjs(originalRescheduledBooking?.startTime).utc().format() : undefined, From 93640552837a0b3fd96cd087b09b29bb9f514f2d Mon Sep 17 00:00:00 2001 From: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Date: Wed, 25 Oct 2023 21:26:22 +0400 Subject: [PATCH 11/23] fix: typefix for webhook and rename oldBookingid to rescheduleId (#12084) * add rescheduleId to type * update oldBookingId to rescheduleId * Remove old remnant --- packages/features/bookings/lib/handleNewBooking.ts | 3 ++- packages/features/webhooks/lib/sendPayload.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index fa810f1bf8..3488442086 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -1851,6 +1851,7 @@ async function handler( ...eventTypeInfo, uid: resultBooking?.uid || uid, bookingId: booking?.id, + rescheduleId: originalRescheduledBooking?.id || undefined, rescheduleUid, rescheduleStartTime: originalRescheduledBooking?.startTime ? dayjs(originalRescheduledBooking?.startTime).utc().format() @@ -2377,8 +2378,8 @@ async function handler( ...evt, ...eventTypeInfo, bookingId: booking?.id, + rescheduleId: originalRescheduledBooking?.id || undefined, rescheduleUid, - oldBookingId: originalRescheduledBooking?.id || undefined, rescheduleStartTime: originalRescheduledBooking?.startTime ? dayjs(originalRescheduledBooking?.startTime).utc().format() : undefined, diff --git a/packages/features/webhooks/lib/sendPayload.ts b/packages/features/webhooks/lib/sendPayload.ts index e0211a6a71..7ff22d9577 100644 --- a/packages/features/webhooks/lib/sendPayload.ts +++ b/packages/features/webhooks/lib/sendPayload.ts @@ -22,6 +22,7 @@ export type WebhookDataType = CalendarEvent & bookingId?: number; status?: string; smsReminderNumber?: string; + rescheduleId?: number; rescheduleUid?: string; rescheduleStartTime?: string; rescheduleEndTime?: string; From 0fb75b715dabc337d12fc603a293d51f1f10129b Mon Sep 17 00:00:00 2001 From: Aldrin <53973174+Dhoni77@users.noreply.github.com> Date: Wed, 25 Oct 2023 22:59:41 +0530 Subject: [PATCH 12/23] fix: event type invalidation (#12077) --- .../features/eventtypes/components/CreateEventTypeDialog.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/features/eventtypes/components/CreateEventTypeDialog.tsx b/packages/features/eventtypes/components/CreateEventTypeDialog.tsx index 7032079c13..6fd6483d65 100644 --- a/packages/features/eventtypes/components/CreateEventTypeDialog.tsx +++ b/packages/features/eventtypes/components/CreateEventTypeDialog.tsx @@ -79,6 +79,7 @@ export default function CreateEventTypeDialog({ membershipRole: MembershipRole | null | undefined; }[]; }) { + const utils = trpc.useContext(); const { t } = useLocale(); const router = useRouter(); const [firstRender, setFirstRender] = useState(true); @@ -116,6 +117,7 @@ export default function CreateEventTypeDialog({ const createMutation = trpc.viewer.eventTypes.create.useMutation({ onSuccess: async ({ eventType }) => { + await utils.viewer.eventTypes.getByViewer.invalidate(); await router.replace(`/event-types/${eventType.id}`); showToast( t("event_type_created_successfully", { From 1c65f5c150a964da4d32106dd77bbbf21e96fa65 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 25 Oct 2023 19:00:14 +0100 Subject: [PATCH 13/23] v3.4.4 --- apps/web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/package.json b/apps/web/package.json index 829f47e542..429b56feb3 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@calcom/web", - "version": "3.4.3", + "version": "3.4.4", "private": true, "scripts": { "analyze": "ANALYZE=true next build", From f9ad99e5728e317739c0b744696f6925c0b0ed75 Mon Sep 17 00:00:00 2001 From: Siddharth Movaliya Date: Wed, 25 Oct 2023 23:46:01 +0530 Subject: [PATCH 14/23] feat: Lock timezone on booking page (#11891) Co-authored-by: CarinaWolli --- .../components/eventtype/EventAdvancedTab.tsx | 17 ++++++++ apps/web/pages/[user].tsx | 1 + apps/web/pages/event-types/[type]/index.tsx | 1 + apps/web/public/static/locales/en/common.json | 2 + .../test/lib/handleChildrenEventTypes.test.ts | 41 +++++++++++++------ .../bookings/Booker/components/EventMeta.tsx | 7 +++- .../features/bookings/lib/handleNewBooking.ts | 2 + .../features/eventtypes/lib/getPublicEvent.ts | 1 + packages/lib/defaultEvents.ts | 1 + packages/lib/getEventTypeById.ts | 1 + packages/lib/test/builder.ts | 1 + .../migration.sql | 2 + packages/prisma/schema.prisma | 1 + packages/prisma/selects/event-types.ts | 2 + 14 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 packages/prisma/migrations/20231020090443_add_lock_timezone_toggle/migration.sql diff --git a/apps/web/components/eventtype/EventAdvancedTab.tsx b/apps/web/components/eventtype/EventAdvancedTab.tsx index 563250a671..6ddba9b78b 100644 --- a/apps/web/components/eventtype/EventAdvancedTab.tsx +++ b/apps/web/components/eventtype/EventAdvancedTab.tsx @@ -433,6 +433,23 @@ export const EventAdvancedTab = ({ eventType, team }: Pick )} /> + ( + onChange(e)} + /> + )} + /> {allowDisablingAttendeeConfirmationEmails(workflows) && ( <> { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore // eslint-disable-next-line - const { schedulingType, id, teamId, timeZone, users, requiresBookerEmailVerification, ...evType } = - mockFindFirstEventType({ - id: 123, - metadata: { managedEventConfig: {} }, - locations: [], - }); + const { + schedulingType, + id, + teamId, + timeZone, + requiresBookerEmailVerification, + lockTimeZoneToggleOnBookingPage, + ...evType + } = mockFindFirstEventType({ + id: 123, + metadata: { managedEventConfig: {} }, + locations: [], + }); const result = await updateChildrenEventTypes({ eventTypeId: 1, oldEventType: { children: [], team: { name: "" } }, @@ -145,6 +152,7 @@ describe("handleChildrenEventTypes", () => { userId, scheduleId, requiresBookerEmailVerification, + lockTimeZoneToggleOnBookingPage, ...evType } = mockFindFirstEventType({ metadata: { managedEventConfig: {} }, @@ -230,12 +238,19 @@ describe("handleChildrenEventTypes", () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore // eslint-disable-next-line - const { schedulingType, id, teamId, timeZone, users, requiresBookerEmailVerification, ...evType } = - mockFindFirstEventType({ - id: 123, - metadata: { managedEventConfig: {} }, - locations: [], - }); + const { + schedulingType, + id, + teamId, + timeZone, + requiresBookerEmailVerification, + lockTimeZoneToggleOnBookingPage, + ...evType + } = mockFindFirstEventType({ + id: 123, + metadata: { managedEventConfig: {} }, + locations: [], + }); prismaMock.eventType.deleteMany.mockResolvedValue([123] as unknown as Prisma.BatchPayload); const result = await updateChildrenEventTypes({ eventTypeId: 1, @@ -277,6 +292,7 @@ describe("handleChildrenEventTypes", () => { parentId, userId, requiresBookerEmailVerification, + lockTimeZoneToggleOnBookingPage, ...evType } = mockFindFirstEventType({ metadata: { managedEventConfig: {} }, @@ -327,6 +343,7 @@ describe("handleChildrenEventTypes", () => { userId: _userId, // eslint-disable-next-line @typescript-eslint/no-unused-vars requiresBookerEmailVerification, + lockTimeZoneToggleOnBookingPage, ...evType } = mockFindFirstEventType({ metadata: { managedEventConfig: {} }, diff --git a/packages/features/bookings/Booker/components/EventMeta.tsx b/packages/features/bookings/Booker/components/EventMeta.tsx index 83bcfc6312..f282e48d5e 100644 --- a/packages/features/bookings/Booker/components/EventMeta.tsx +++ b/packages/features/bookings/Booker/components/EventMeta.tsx @@ -105,6 +105,7 @@ export const EventMeta = () => { )} + { {bookerState === "booking" ? ( <>{timezone} ) : ( - + { }} value={timezone} onChange={(tz) => setTimezone(tz.value)} + isDisabled={event.lockTimeZoneToggleOnBookingPage} /> )} diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index 3488442086..997b318bf4 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -276,6 +276,7 @@ const getEventTypesFromDB = async (eventTypeId: number) => { periodEndDate: true, periodDays: true, periodCountCalendarDays: true, + lockTimeZoneToggleOnBookingPage: true, requiresConfirmation: true, requiresBookerEmailVerification: true, userId: true, @@ -2686,6 +2687,7 @@ const findBookingQuery = async (bookingId: number) => { description: true, currency: true, length: true, + lockTimeZoneToggleOnBookingPage: true, requiresConfirmation: true, requiresBookerEmailVerification: true, price: true, diff --git a/packages/features/eventtypes/lib/getPublicEvent.ts b/packages/features/eventtypes/lib/getPublicEvent.ts index 3b6d8f704f..85bab2e26f 100644 --- a/packages/features/eventtypes/lib/getPublicEvent.ts +++ b/packages/features/eventtypes/lib/getPublicEvent.ts @@ -33,6 +33,7 @@ const publicEventSelect = Prisma.validator()({ customInputs: true, disableGuests: true, metadata: true, + lockTimeZoneToggleOnBookingPage: true, requiresConfirmation: true, requiresBookerEmailVerification: true, recurringEvent: true, diff --git a/packages/lib/defaultEvents.ts b/packages/lib/defaultEvents.ts index 5aba14feaa..5243b63ebd 100644 --- a/packages/lib/defaultEvents.ts +++ b/packages/lib/defaultEvents.ts @@ -86,6 +86,7 @@ const commons = { recurringEvent: null, destinationCalendar: null, team: null, + lockTimeZoneToggleOnBookingPage: false, requiresConfirmation: false, requiresBookerEmailVerification: false, bookingLimits: null, diff --git a/packages/lib/getEventTypeById.ts b/packages/lib/getEventTypeById.ts index 7ce86719fd..be26508d9f 100644 --- a/packages/lib/getEventTypeById.ts +++ b/packages/lib/getEventTypeById.ts @@ -89,6 +89,7 @@ export default async function getEventTypeById({ periodStartDate: true, periodEndDate: true, periodCountCalendarDays: true, + lockTimeZoneToggleOnBookingPage: true, requiresConfirmation: true, requiresBookerEmailVerification: true, recurringEvent: true, diff --git a/packages/lib/test/builder.ts b/packages/lib/test/builder.ts index 7769023a59..bd9e9fc516 100644 --- a/packages/lib/test/builder.ts +++ b/packages/lib/test/builder.ts @@ -85,6 +85,7 @@ export const buildEventType = (eventType?: Partial): EventType => { periodDays: null, periodCountCalendarDays: null, recurringEvent: null, + lockTimeZoneToggleOnBookingPage: false, requiresConfirmation: false, disableGuests: false, hideCalendarNotes: false, diff --git a/packages/prisma/migrations/20231020090443_add_lock_timezone_toggle/migration.sql b/packages/prisma/migrations/20231020090443_add_lock_timezone_toggle/migration.sql new file mode 100644 index 0000000000..c97b6146c5 --- /dev/null +++ b/packages/prisma/migrations/20231020090443_add_lock_timezone_toggle/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "EventType" ADD COLUMN "lockTimeZoneToggleOnBookingPage" BOOLEAN NOT NULL DEFAULT false; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 7eee884084..6356442fa9 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -86,6 +86,7 @@ model EventType { periodEndDate DateTime? periodDays Int? periodCountCalendarDays Boolean? + lockTimeZoneToggleOnBookingPage Boolean @default(false) requiresConfirmation Boolean @default(false) requiresBookerEmailVerification Boolean @default(false) /// @zod.custom(imports.recurringEventType) diff --git a/packages/prisma/selects/event-types.ts b/packages/prisma/selects/event-types.ts index c47e148dde..35733ede28 100644 --- a/packages/prisma/selects/event-types.ts +++ b/packages/prisma/selects/event-types.ts @@ -11,6 +11,7 @@ export const baseEventTypeSelect = Prisma.validator()({ hidden: true, price: true, currency: true, + lockTimeZoneToggleOnBookingPage: true, requiresConfirmation: true, requiresBookerEmailVerification: true, }); @@ -28,6 +29,7 @@ export const bookEventTypeSelect = Prisma.validator()({ periodStartDate: true, periodEndDate: true, recurringEvent: true, + lockTimeZoneToggleOnBookingPage: true, requiresConfirmation: true, requiresBookerEmailVerification: true, metadata: true, From 158da51a5d9b3de3dcaa7210f1a77f495515ad06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20L=C3=B3pez?= Date: Wed, 25 Oct 2023 12:33:22 -0700 Subject: [PATCH 15/23] fix: embed rewrites post dotted usernames (#12087) --- apps/web/next.config.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/web/next.config.js b/apps/web/next.config.js index 22da1946e5..dc4cbdafa2 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -226,6 +226,14 @@ const nextConfig = { }, async rewrites() { const beforeFiles = [ + { + /** + * Needed due to the introduction of dotted usernames + * @see https://github.com/calcom/cal.com/pull/11706 + */ + source: "/embed.js", + destination: "/embed/embed.js", + }, { source: "/login", destination: "/auth/login", From 139a7c8249b82fe86f92d9a1bf2c27f26ee59516 Mon Sep 17 00:00:00 2001 From: DmytroHryshyn <125881252+DmytroHryshyn@users.noreply.github.com> Date: Wed, 25 Oct 2023 23:58:08 +0300 Subject: [PATCH 16/23] chore: [app dir bootstrapping 5] add RootLayout (#11982) Co-authored-by: zomars Co-authored-by: Greg Pabian <35925521+grzpab@users.noreply.github.com> --- ...honenumber-js-npm-1.10.12-51c84f8bf1.patch | 15 + apps/web/app/layout.tsx | 109 +++++++ apps/web/components/PageWrapperAppDir.tsx | 88 ++++++ apps/web/lib/app-providers-app-dir.tsx | 291 ++++++++++++++++++ package.json | 3 +- yarn.lock | 15 +- 6 files changed, 516 insertions(+), 5 deletions(-) create mode 100644 .yarn/patches/libphonenumber-js-npm-1.10.12-51c84f8bf1.patch create mode 100644 apps/web/app/layout.tsx create mode 100644 apps/web/components/PageWrapperAppDir.tsx create mode 100644 apps/web/lib/app-providers-app-dir.tsx diff --git a/.yarn/patches/libphonenumber-js-npm-1.10.12-51c84f8bf1.patch b/.yarn/patches/libphonenumber-js-npm-1.10.12-51c84f8bf1.patch new file mode 100644 index 0000000000..7cfa242404 --- /dev/null +++ b/.yarn/patches/libphonenumber-js-npm-1.10.12-51c84f8bf1.patch @@ -0,0 +1,15 @@ +diff --git a/index.cjs b/index.cjs +index b645707a3549fc298508726e404243499bbed499..f34b0891e99b275a9218e253f303f43d31ef3f73 100644 +--- a/index.cjs ++++ b/index.cjs +@@ -13,8 +13,8 @@ function withMetadataArgument(func, _arguments) { + // https://github.com/babel/babel/issues/2212#issuecomment-131827986 + // An alternative approach: + // https://www.npmjs.com/package/babel-plugin-add-module-exports +-exports = module.exports = min.parsePhoneNumberFromString +-exports['default'] = min.parsePhoneNumberFromString ++// exports = module.exports = min.parsePhoneNumberFromString ++// exports['default'] = min.parsePhoneNumberFromString + + // `parsePhoneNumberFromString()` named export is now considered legacy: + // it has been promoted to a default export due to being too verbose. diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx new file mode 100644 index 0000000000..23d224b6e1 --- /dev/null +++ b/apps/web/app/layout.tsx @@ -0,0 +1,109 @@ +import type { Metadata } from "next"; +import { headers as nextHeaders, cookies as nextCookies } from "next/headers"; +import Script from "next/script"; +import React from "react"; + +import { getLocale } from "@calcom/features/auth/lib/getLocale"; +import { IS_PRODUCTION } from "@calcom/lib/constants"; + +import "../styles/globals.css"; + +export const metadata: Metadata = { + icons: { + icon: [ + { + sizes: "32x32", + url: "/api/logo?type=favicon-32", + }, + { + sizes: "16x16", + url: "/api/logo?type=favicon-16", + }, + ], + apple: { + sizes: "180x180", + url: "/api/logo?type=apple-touch-icon", + }, + other: [ + { + url: "/safari-pinned-tab.svg", + rel: "mask-icon", + }, + ], + }, + manifest: "/site.webmanifest", + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "#f9fafb" }, + { media: "(prefers-color-scheme: dark)", color: "#1C1C1C" }, + ], + other: { + "msapplication-TileColor": "#000000", + }, +}; + +const getInitialProps = async ( + url: string, + headers: ReturnType, + cookies: ReturnType +) => { + const { pathname, searchParams } = new URL(url); + + const isEmbed = pathname.endsWith("/embed") || (searchParams?.get("embedType") ?? null) !== null; + const embedColorScheme = searchParams?.get("ui.color-scheme"); + + // @ts-expect-error we cannot access ctx.req in app dir, however headers and cookies are only properties needed to extract the locale + const newLocale = await getLocale({ headers, cookies }); + let direction = "ltr"; + + try { + const intlLocale = new Intl.Locale(newLocale); + // @ts-expect-error INFO: Typescript does not know about the Intl.Locale textInfo attribute + direction = intlLocale.textInfo?.direction; + } catch (e) { + console.error(e); + } + + return { isEmbed, embedColorScheme, locale: newLocale, direction }; +}; + +export default async function RootLayout({ children }: { children: React.ReactNode }) { + const headers = nextHeaders(); + const cookies = nextCookies(); + + const fullUrl = headers.get("x-url") ?? ""; + const nonce = headers.get("x-csp") ?? ""; + + const { locale, direction, isEmbed, embedColorScheme } = await getInitialProps(fullUrl, headers, cookies); + return ( + + + {!IS_PRODUCTION && process.env.VERCEL_ENV === "preview" && ( + // eslint-disable-next-line @next/next/no-sync-scripts +