From 4aa0b4cc65993b988d8c5ba71557f3c0c80d2b98 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Mon, 8 Jan 2024 20:55:48 +0530 Subject: [PATCH 1/9] fix: Team URL and booker URLs (#13050) Co-authored-by: Bailey Pumfleet Co-authored-by: Peer Richelsen --- apps/web/lib/orgMigration.test.ts | 4 +++- apps/web/test/utils/bookingScenario/test.ts | 4 +++- .../ee/organizations/lib/orgDomains.ts | 7 +++--- .../ee/teams/components/TeamListItem.tsx | 22 ++++++++----------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/web/lib/orgMigration.test.ts b/apps/web/lib/orgMigration.test.ts index 2bf8fb7093..18b924dbc2 100644 --- a/apps/web/lib/orgMigration.test.ts +++ b/apps/web/lib/orgMigration.test.ts @@ -3,12 +3,14 @@ import prismock from "../../../tests/libs/__mocks__/prisma"; import { describe, expect, it } from "vitest"; import type { z } from "zod"; +import { WEBSITE_URL } from "@calcom/lib/constants"; import type { MembershipRole, Prisma } from "@calcom/prisma/client"; import { RedirectType } from "@calcom/prisma/enums"; import type { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import { moveTeamToOrg, moveUserToOrg, removeTeamFromOrg, removeUserFromOrg } from "./orgMigration"; +const WEBSITE_PROTOCOL = new URL(WEBSITE_URL).protocol; describe("orgMigration", () => { describe("moveUserToOrg", () => { describe("when user email does not match orgAutoAcceptEmail", () => { @@ -1366,7 +1368,7 @@ async function expectRedirectToBeEnabled({ } expect(redirect).not.toBeNull(); - expect(redirect?.toUrl).toBe(`http://${orgSlug}.cal.local:3000/${to}`); + expect(redirect?.toUrl).toBe(`${WEBSITE_PROTOCOL}//${orgSlug}.cal.local:3000/${to}`); if (!redirect) { throw new Error(`Redirect doesn't exist for ${JSON.stringify(tempOrgRedirectWhere)}`); } diff --git a/apps/web/test/utils/bookingScenario/test.ts b/apps/web/test/utils/bookingScenario/test.ts index 74eb503f86..7a00f894fd 100644 --- a/apps/web/test/utils/bookingScenario/test.ts +++ b/apps/web/test/utils/bookingScenario/test.ts @@ -1,9 +1,11 @@ import type { TestFunction } from "vitest"; +import { WEBSITE_URL } from "@calcom/lib/constants"; import { test } from "@calcom/web/test/fixtures/fixtures"; import type { Fixtures } from "@calcom/web/test/fixtures/fixtures"; import { createOrganization } from "@calcom/web/test/utils/bookingScenario/bookingScenario"; +const WEBSITE_PROTOCOL = new URL(WEBSITE_URL).protocol; const _testWithAndWithoutOrg = ( description: Parameters[0], fn: Parameters[1], @@ -28,7 +30,7 @@ const _testWithAndWithoutOrg = ( skip, org: { organization: org, - urlOrigin: `http://${org.slug}.cal.local:3000`, + urlOrigin: `${WEBSITE_PROTOCOL}//${org.slug}.cal.local:3000`, }, }); }, diff --git a/packages/features/ee/organizations/lib/orgDomains.ts b/packages/features/ee/organizations/lib/orgDomains.ts index 461f5e2c03..ebb0c9d73e 100644 --- a/packages/features/ee/organizations/lib/orgDomains.ts +++ b/packages/features/ee/organizations/lib/orgDomains.ts @@ -1,7 +1,7 @@ import type { Prisma } from "@prisma/client"; import type { IncomingMessage } from "http"; -import { IS_PRODUCTION } from "@calcom/lib/constants"; +import { IS_PRODUCTION, WEBSITE_URL } from "@calcom/lib/constants"; import { ALLOWED_HOSTNAMES, RESERVED_SUBDOMAINS, WEBAPP_URL } from "@calcom/lib/constants"; import logger from "@calcom/lib/logger"; import slugify from "@calcom/lib/slugify"; @@ -100,9 +100,10 @@ export function subdomainSuffix() { } export function getOrgFullOrigin(slug: string, options: { protocol: boolean } = { protocol: true }) { - if (!slug) return options.protocol ? WEBAPP_URL : WEBAPP_URL.replace("https://", "").replace("http://", ""); + if (!slug) + return options.protocol ? WEBSITE_URL : WEBSITE_URL.replace("https://", "").replace("http://", ""); const orgFullOrigin = `${ - options.protocol ? `${new URL(WEBAPP_URL).protocol}//` : "" + options.protocol ? `${new URL(WEBSITE_URL).protocol}//` : "" }${slug}.${subdomainSuffix()}`; return orgFullOrigin; } diff --git a/packages/features/ee/teams/components/TeamListItem.tsx b/packages/features/ee/teams/components/TeamListItem.tsx index 4355ad6461..0eb04d4140 100644 --- a/packages/features/ee/teams/components/TeamListItem.tsx +++ b/packages/features/ee/teams/components/TeamListItem.tsx @@ -107,9 +107,7 @@ export default function TeamListItem(props: Props) { {team.name} {team.slug ? ( - `${getTeamUrlSync({ orgSlug: team.parent ? team.parent.slug : null, teamSlug: team.slug })}/${ - team.slug - }` + `${getTeamUrlSync({ orgSlug: team.parent ? team.parent.slug : null, teamSlug: team.slug })}` ) : ( {t("upgrade")} )} @@ -245,11 +243,10 @@ export default function TeamListItem(props: Props) { color="secondary" onClick={() => { navigator.clipboard.writeText( - `${ - orgBranding - ? `${orgBranding.fullDomain}` - : `${process.env.NEXT_PUBLIC_WEBSITE_URL}/team` - }/${team.slug}` + `${getTeamUrlSync({ + orgSlug: team.parent ? team.parent.slug : null, + teamSlug: team.slug, + })}` ); showToast(t("link_copied"), "success"); }} @@ -285,11 +282,10 @@ export default function TeamListItem(props: Props) { {t("preview_team") as string} From b832289f8ed86c0c7a399339fa85373e7efed505 Mon Sep 17 00:00:00 2001 From: Morgan <33722304+ThyMinimalDev@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:49:39 +0200 Subject: [PATCH 2/9] refactor: booker import dynamically specific ui components instead of the whole package (#13101) --- packages/features/bookings/Booker/Booker.tsx | 4 +++- .../features/bookings/Booker/components/EventMeta.tsx | 9 ++++++--- packages/ui/package.json | 4 +++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/features/bookings/Booker/Booker.tsx b/packages/features/bookings/Booker/Booker.tsx index fa16e1398a..15749a95a6 100644 --- a/packages/features/bookings/Booker/Booker.tsx +++ b/packages/features/bookings/Booker/Booker.tsx @@ -33,7 +33,9 @@ import { useBrandColors } from "./utils/use-brand-colors"; const loadFramerFeatures = () => import("./framer-features").then((res) => res.default); const PoweredBy = dynamic(() => import("@calcom/ee/components/PoweredBy")); -const UnpublishedEntity = dynamic(() => import("@calcom/ui").then((mod) => mod.UnpublishedEntity)); +const UnpublishedEntity = dynamic(() => + import("@calcom/ui/components/unpublished-entity/UnpublishedEntity").then((mod) => mod.UnpublishedEntity) +); const DatePicker = dynamic(() => import("./components/DatePicker").then((mod) => mod.DatePicker), { ssr: false, }); diff --git a/packages/features/bookings/Booker/components/EventMeta.tsx b/packages/features/bookings/Booker/components/EventMeta.tsx index 623704490e..3cb6dab61b 100644 --- a/packages/features/bookings/Booker/components/EventMeta.tsx +++ b/packages/features/bookings/Booker/components/EventMeta.tsx @@ -15,9 +15,12 @@ import { useBookerStore } from "../store"; import { FromToTime } from "../utils/dates"; import { useEvent } from "../utils/event"; -const TimezoneSelect = dynamic(() => import("@calcom/ui").then((mod) => mod.TimezoneSelect), { - ssr: false, -}); +const TimezoneSelect = dynamic( + () => import("@calcom/ui/components/form/timezone-select/TimezoneSelect").then((mod) => mod.TimezoneSelect), + { + ssr: false, + } +); export const EventMeta = () => { const { setTimezone, timeFormat, timezone } = useTimePreferences(); diff --git a/packages/ui/package.json b/packages/ui/package.json index 9802cdc212..caf64f41ca 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -10,7 +10,9 @@ "./components/icon": "./components/icon/index.ts", "./components/icon/Discord": "./components/icon/Discord.tsx", "./components/icon/SatSymbol": "./components/icon/SatSymbol.tsx", - "./components/icon/Spinner": "./components/icon/Spinner.tsx" + "./components/icon/Spinner": "./components/icon/Spinner.tsx", + "./components/unpublished-entity/UnpublishedEntity": "./components/unpublished-entity/index.ts", + "./components/form/timezone-select/TimezoneSelect": "./components/form/timezone-select/index.ts" }, "types": "./index.tsx", "license": "MIT", From fcc50c1d0f90d77253455e7caadab383a35ebefb Mon Sep 17 00:00:00 2001 From: DmytroHryshyn <125881252+DmytroHryshyn@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:51:40 +0200 Subject: [PATCH 3/9] chore: [app-router-migration-11] Migrate settings/teams page group (#12778) * intuita codemod: app-directory-boilerplate-calcom * manual: add title and description metadata for each page * manual: move between folders * manual: finalize migration * manual: fix client components * manual: Change structure & Refactor to make code up to date --------- Co-authored-by: Benny Joo --- .../app/future/apps/categories/[category]/page.tsx | 2 +- .../future/settings/teams/[id]/appearance/layout.tsx | 5 +++++ .../future/settings/teams/[id]/appearance/page.tsx | 11 +++++++++++ .../app/future/settings/teams/[id]/billing/layout.tsx | 5 +++++ .../app/future/settings/teams/[id]/billing/page.tsx | 10 ++++++++++ .../app/future/settings/teams/[id]/members/layout.tsx | 5 +++++ .../app/future/settings/teams/[id]/members/page.tsx | 11 +++++++++++ .../settings/teams/[id]/onboard-members/page.tsx | 11 +++++++++++ .../app/future/settings/teams/[id]/profile/layout.tsx | 5 +++++ .../app/future/settings/teams/[id]/profile/page.tsx | 11 +++++++++++ .../web/app/future/settings/teams/[id]/sso/layout.tsx | 5 +++++ apps/web/app/future/settings/teams/[id]/sso/page.tsx | 11 +++++++++++ apps/web/app/future/settings/teams/layout.tsx | 5 +++++ apps/web/app/future/settings/teams/new/page.tsx | 11 +++++++++++ apps/web/app/future/settings/teams/page.ts | 11 +++++++++++ apps/web/pages/settings/billing/index.tsx | 2 ++ .../web/pages/settings/teams/[id]/onboard-members.tsx | 5 ++++- apps/web/pages/settings/teams/new/index.tsx | 4 +++- packages/features/ee/sso/page/teams-sso-view.tsx | 2 ++ .../features/ee/teams/pages/team-appearance-view.tsx | 2 ++ .../features/ee/teams/pages/team-listing-view.tsx | 2 ++ .../features/ee/teams/pages/team-members-view.tsx | 2 ++ .../features/ee/teams/pages/team-profile-view.tsx | 2 ++ 23 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 apps/web/app/future/settings/teams/[id]/appearance/layout.tsx create mode 100644 apps/web/app/future/settings/teams/[id]/appearance/page.tsx create mode 100644 apps/web/app/future/settings/teams/[id]/billing/layout.tsx create mode 100644 apps/web/app/future/settings/teams/[id]/billing/page.tsx create mode 100644 apps/web/app/future/settings/teams/[id]/members/layout.tsx create mode 100644 apps/web/app/future/settings/teams/[id]/members/page.tsx create mode 100644 apps/web/app/future/settings/teams/[id]/onboard-members/page.tsx create mode 100644 apps/web/app/future/settings/teams/[id]/profile/layout.tsx create mode 100644 apps/web/app/future/settings/teams/[id]/profile/page.tsx create mode 100644 apps/web/app/future/settings/teams/[id]/sso/layout.tsx create mode 100644 apps/web/app/future/settings/teams/[id]/sso/page.tsx create mode 100644 apps/web/app/future/settings/teams/layout.tsx create mode 100644 apps/web/app/future/settings/teams/new/page.tsx create mode 100644 apps/web/app/future/settings/teams/page.ts diff --git a/apps/web/app/future/apps/categories/[category]/page.tsx b/apps/web/app/future/apps/categories/[category]/page.tsx index a4d8532821..6f3d54e434 100644 --- a/apps/web/app/future/apps/categories/[category]/page.tsx +++ b/apps/web/app/future/apps/categories/[category]/page.tsx @@ -67,5 +67,5 @@ const getPageProps = async ({ params }: { params: Record; +export default WithLayout({ getData: getPageProps, Page: CategoryPage })<"P">; export const dynamic = "force-static"; diff --git a/apps/web/app/future/settings/teams/[id]/appearance/layout.tsx b/apps/web/app/future/settings/teams/[id]/appearance/layout.tsx new file mode 100644 index 0000000000..1359b26601 --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/appearance/layout.tsx @@ -0,0 +1,5 @@ +import { WithLayout } from "app/layoutHOC"; + +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir"; + +export default WithLayout({ getLayout })<"L">; diff --git a/apps/web/app/future/settings/teams/[id]/appearance/page.tsx b/apps/web/app/future/settings/teams/[id]/appearance/page.tsx new file mode 100644 index 0000000000..ac76104d07 --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/appearance/page.tsx @@ -0,0 +1,11 @@ +import { _generateMetadata } from "app/_utils"; + +import Page from "@calcom/features/ee/teams/pages/team-appearance-view"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("booking_appearance"), + (t) => t("appearance_team_description") + ); + +export default Page; diff --git a/apps/web/app/future/settings/teams/[id]/billing/layout.tsx b/apps/web/app/future/settings/teams/[id]/billing/layout.tsx new file mode 100644 index 0000000000..1359b26601 --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/billing/layout.tsx @@ -0,0 +1,5 @@ +import { WithLayout } from "app/layoutHOC"; + +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir"; + +export default WithLayout({ getLayout })<"L">; diff --git a/apps/web/app/future/settings/teams/[id]/billing/page.tsx b/apps/web/app/future/settings/teams/[id]/billing/page.tsx new file mode 100644 index 0000000000..96b7f2f3b3 --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/billing/page.tsx @@ -0,0 +1,10 @@ +import Page from "@pages/settings/billing/index"; +import { _generateMetadata } from "app/_utils"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("billing"), + (t) => t("team_billing_description") + ); + +export default Page; diff --git a/apps/web/app/future/settings/teams/[id]/members/layout.tsx b/apps/web/app/future/settings/teams/[id]/members/layout.tsx new file mode 100644 index 0000000000..1359b26601 --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/members/layout.tsx @@ -0,0 +1,5 @@ +import { WithLayout } from "app/layoutHOC"; + +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir"; + +export default WithLayout({ getLayout })<"L">; diff --git a/apps/web/app/future/settings/teams/[id]/members/page.tsx b/apps/web/app/future/settings/teams/[id]/members/page.tsx new file mode 100644 index 0000000000..0f38c54f59 --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/members/page.tsx @@ -0,0 +1,11 @@ +import { _generateMetadata } from "app/_utils"; + +import Page from "@calcom/features/ee/teams/pages/team-members-view"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("team_members"), + (t) => t("members_team_description") + ); + +export default Page; diff --git a/apps/web/app/future/settings/teams/[id]/onboard-members/page.tsx b/apps/web/app/future/settings/teams/[id]/onboard-members/page.tsx new file mode 100644 index 0000000000..adb33f8b63 --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/onboard-members/page.tsx @@ -0,0 +1,11 @@ +import LegacyPage, { GetLayout } from "@pages/settings/teams/[id]/onboard-members"; +import { _generateMetadata } from "app/_utils"; +import { WithLayout } from "app/layoutHOC"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("add_team_members"), + (t) => t("add_team_members_description") + ); + +export default WithLayout({ Page: LegacyPage, getLayout: GetLayout })<"P">; diff --git a/apps/web/app/future/settings/teams/[id]/profile/layout.tsx b/apps/web/app/future/settings/teams/[id]/profile/layout.tsx new file mode 100644 index 0000000000..1359b26601 --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/profile/layout.tsx @@ -0,0 +1,5 @@ +import { WithLayout } from "app/layoutHOC"; + +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir"; + +export default WithLayout({ getLayout })<"L">; diff --git a/apps/web/app/future/settings/teams/[id]/profile/page.tsx b/apps/web/app/future/settings/teams/[id]/profile/page.tsx new file mode 100644 index 0000000000..b2e02352ef --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/profile/page.tsx @@ -0,0 +1,11 @@ +import { _generateMetadata } from "app/_utils"; + +import Page from "@calcom/features/ee/teams/pages/team-profile-view"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("profile"), + (t) => t("profile_team_description") + ); + +export default Page; diff --git a/apps/web/app/future/settings/teams/[id]/sso/layout.tsx b/apps/web/app/future/settings/teams/[id]/sso/layout.tsx new file mode 100644 index 0000000000..1359b26601 --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/sso/layout.tsx @@ -0,0 +1,5 @@ +import { WithLayout } from "app/layoutHOC"; + +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir"; + +export default WithLayout({ getLayout })<"L">; diff --git a/apps/web/app/future/settings/teams/[id]/sso/page.tsx b/apps/web/app/future/settings/teams/[id]/sso/page.tsx new file mode 100644 index 0000000000..8fba20bd29 --- /dev/null +++ b/apps/web/app/future/settings/teams/[id]/sso/page.tsx @@ -0,0 +1,11 @@ +import { _generateMetadata } from "app/_utils"; + +import Page from "@calcom/features/ee/sso/page/teams-sso-view"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("sso_configuration"), + (t) => t("sso_configuration_description") + ); + +export default Page; diff --git a/apps/web/app/future/settings/teams/layout.tsx b/apps/web/app/future/settings/teams/layout.tsx new file mode 100644 index 0000000000..1359b26601 --- /dev/null +++ b/apps/web/app/future/settings/teams/layout.tsx @@ -0,0 +1,5 @@ +import { WithLayout } from "app/layoutHOC"; + +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir"; + +export default WithLayout({ getLayout })<"L">; diff --git a/apps/web/app/future/settings/teams/new/page.tsx b/apps/web/app/future/settings/teams/new/page.tsx new file mode 100644 index 0000000000..592517ab48 --- /dev/null +++ b/apps/web/app/future/settings/teams/new/page.tsx @@ -0,0 +1,11 @@ +import LegacyPage, { LayoutWrapper } from "@pages/settings/teams/new/index"; +import { _generateMetadata } from "app/_utils"; +import { WithLayout } from "app/layoutHOC"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("create_new_team"), + (t) => t("create_new_team_description") + ); + +export default WithLayout({ Page: LegacyPage, getLayout: LayoutWrapper })<"P">; diff --git a/apps/web/app/future/settings/teams/page.ts b/apps/web/app/future/settings/teams/page.ts new file mode 100644 index 0000000000..6175f853ec --- /dev/null +++ b/apps/web/app/future/settings/teams/page.ts @@ -0,0 +1,11 @@ +import { _generateMetadata } from "app/_utils"; + +import Page from "@calcom/features/ee/teams/pages/team-listing-view"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("teams"), + (t) => t("create_manage_teams_collaborative") + ); + +export default Page; diff --git a/apps/web/pages/settings/billing/index.tsx b/apps/web/pages/settings/billing/index.tsx index 501c405a25..91ad564e4e 100644 --- a/apps/web/pages/settings/billing/index.tsx +++ b/apps/web/pages/settings/billing/index.tsx @@ -1,3 +1,5 @@ +"use client"; + import { usePathname } from "next/navigation"; import { useIntercom } from "@calcom/features/ee/support/lib/intercom/useIntercom"; diff --git a/apps/web/pages/settings/teams/[id]/onboard-members.tsx b/apps/web/pages/settings/teams/[id]/onboard-members.tsx index 4ff3fb9d77..36ae12050e 100644 --- a/apps/web/pages/settings/teams/[id]/onboard-members.tsx +++ b/apps/web/pages/settings/teams/[id]/onboard-members.tsx @@ -1,3 +1,5 @@ +"use client"; + import Head from "next/head"; import AddNewTeamMembers from "@calcom/features/ee/teams/components/AddNewTeamMembers"; @@ -19,12 +21,13 @@ const OnboardTeamMembersPage = () => { ); }; -OnboardTeamMembersPage.getLayout = (page: React.ReactElement) => ( +export const GetLayout = (page: React.ReactElement) => ( {page} ); +OnboardTeamMembersPage.getLayout = GetLayout; OnboardTeamMembersPage.PageWrapper = PageWrapper; export default OnboardTeamMembersPage; diff --git a/apps/web/pages/settings/teams/new/index.tsx b/apps/web/pages/settings/teams/new/index.tsx index d3442f1696..9a1ba88808 100644 --- a/apps/web/pages/settings/teams/new/index.tsx +++ b/apps/web/pages/settings/teams/new/index.tsx @@ -1,3 +1,5 @@ +"use client"; + import Head from "next/head"; import { CreateANewTeamForm } from "@calcom/features/ee/teams/components"; @@ -18,7 +20,7 @@ const CreateNewTeamPage = () => { ); }; -const LayoutWrapper = (page: React.ReactElement) => { +export const LayoutWrapper = (page: React.ReactElement) => { return ( {page} diff --git a/packages/features/ee/sso/page/teams-sso-view.tsx b/packages/features/ee/sso/page/teams-sso-view.tsx index 26ab7d0a36..73829f1acd 100644 --- a/packages/features/ee/sso/page/teams-sso-view.tsx +++ b/packages/features/ee/sso/page/teams-sso-view.tsx @@ -1,3 +1,5 @@ +"use client"; + import { useRouter } from "next/navigation"; import { useEffect } from "react"; diff --git a/packages/features/ee/teams/pages/team-appearance-view.tsx b/packages/features/ee/teams/pages/team-appearance-view.tsx index a8cc2ac269..02dfea5357 100644 --- a/packages/features/ee/teams/pages/team-appearance-view.tsx +++ b/packages/features/ee/teams/pages/team-appearance-view.tsx @@ -1,3 +1,5 @@ +"use client"; + import { useRouter } from "next/navigation"; import { useState } from "react"; import { useForm } from "react-hook-form"; diff --git a/packages/features/ee/teams/pages/team-listing-view.tsx b/packages/features/ee/teams/pages/team-listing-view.tsx index 9040e35cd3..2047a2d004 100644 --- a/packages/features/ee/teams/pages/team-listing-view.tsx +++ b/packages/features/ee/teams/pages/team-listing-view.tsx @@ -1,3 +1,5 @@ +"use client"; + import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Meta } from "@calcom/ui"; diff --git a/packages/features/ee/teams/pages/team-members-view.tsx b/packages/features/ee/teams/pages/team-members-view.tsx index b6374df16b..3c142c36ab 100644 --- a/packages/features/ee/teams/pages/team-members-view.tsx +++ b/packages/features/ee/teams/pages/team-members-view.tsx @@ -1,3 +1,5 @@ +"use client"; + import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; import { useState } from "react"; diff --git a/packages/features/ee/teams/pages/team-profile-view.tsx b/packages/features/ee/teams/pages/team-profile-view.tsx index 808b4f379a..d0dff783d4 100644 --- a/packages/features/ee/teams/pages/team-profile-view.tsx +++ b/packages/features/ee/teams/pages/team-profile-view.tsx @@ -1,3 +1,5 @@ +"use client"; + import { zodResolver } from "@hookform/resolvers/zod"; import type { Prisma } from "@prisma/client"; import { useSession } from "next-auth/react"; From 97d5bb9fe63d7e1ee6e85e02bf92aed0dc4f6e99 Mon Sep 17 00:00:00 2001 From: zomars Date: Mon, 8 Jan 2024 15:37:32 -0700 Subject: [PATCH 4/9] Update turbo.json --- turbo.json | 1 + 1 file changed, 1 insertion(+) diff --git a/turbo.json b/turbo.json index dfd687535d..dc8e958a65 100644 --- a/turbo.json +++ b/turbo.json @@ -112,6 +112,7 @@ "cache": false }, "dx": { + "dependsOn": ["//#env-check:common", "//#env-check:app-store"], "cache": false }, "lint": { From 5690718e7f0662f7ffcfbce420e3b118abace867 Mon Sep 17 00:00:00 2001 From: DmytroHryshyn <125881252+DmytroHryshyn@users.noreply.github.com> Date: Tue, 9 Jan 2024 04:59:11 +0200 Subject: [PATCH 5/9] chore: [app-router-migration 10.5]: replace new trpc helper with legacy ssrInit/ssgInit (#13108) --- apps/web/app/_trpc/createTRPCNextLayout.ts | 212 ------------------ apps/web/app/_trpc/ssgInit.ts | 34 --- apps/web/app/_trpc/ssrInit.ts | 57 ----- apps/web/app/future/apps/categories/page.tsx | 20 +- apps/web/app/future/apps/page.tsx | 35 +-- .../app/future/bookings/[status]/layout.tsx | 17 +- .../getting-started/[[...step]]/page.tsx | 31 +-- apps/web/app/future/teams/page.tsx | 16 +- apps/web/app/future/video/[uid]/page.tsx | 15 +- .../future/video/no-meeting-found/page.tsx | 12 +- 10 files changed, 68 insertions(+), 381 deletions(-) delete mode 100644 apps/web/app/_trpc/createTRPCNextLayout.ts delete mode 100644 apps/web/app/_trpc/ssgInit.ts delete mode 100644 apps/web/app/_trpc/ssrInit.ts rename apps/web/app/future/{(individual-page-wrapper) => }/getting-started/[[...step]]/page.tsx (66%) diff --git a/apps/web/app/_trpc/createTRPCNextLayout.ts b/apps/web/app/_trpc/createTRPCNextLayout.ts deleted file mode 100644 index c65b4fe941..0000000000 --- a/apps/web/app/_trpc/createTRPCNextLayout.ts +++ /dev/null @@ -1,212 +0,0 @@ -// originally from in the "experimental playground for tRPC + next.js 13" repo owned by trpc team -// file link: https://github.com/trpc/next-13/blob/main/%40trpc/next-layout/createTRPCNextLayout.ts -// repo link: https://github.com/trpc/next-13 -// code is / will continue to be adapted for our usage -import { dehydrate, QueryClient } from "@tanstack/query-core"; -import type { DehydratedState, QueryKey } from "@tanstack/react-query"; - -import type { Maybe, TRPCClientError, TRPCClientErrorLike } from "@calcom/trpc"; -import { - callProcedure, - type AnyProcedure, - type AnyQueryProcedure, - type AnyRouter, - type DataTransformer, - type inferProcedureInput, - type inferProcedureOutput, - type inferRouterContext, - type MaybePromise, - type ProcedureRouterRecord, -} from "@calcom/trpc/server"; - -import { createRecursiveProxy, createFlatProxy } from "@trpc/server/shared"; - -export function getArrayQueryKey( - queryKey: string | [string] | [string, ...unknown[]] | unknown[], - type: string -): QueryKey { - const queryKeyArrayed = Array.isArray(queryKey) ? queryKey : [queryKey]; - const [arrayPath, input] = queryKeyArrayed; - - if (!input && (!type || type === "any")) { - return Array.isArray(arrayPath) && arrayPath.length !== 0 ? [arrayPath] : ([] as unknown as QueryKey); - } - - return [ - arrayPath, - { - ...(typeof input !== "undefined" && { input: input }), - ...(type && type !== "any" && { type: type }), - }, - ]; -} - -// copy starts -// copied from trpc/trpc repo -// ref: https://github.com/trpc/trpc/blob/main/packages/next/src/withTRPC.tsx#L37-#L58 -function transformQueryOrMutationCacheErrors< - TState extends DehydratedState["queries"][0] | DehydratedState["mutations"][0] ->(result: TState): TState { - const error = result.state.error as Maybe>; - if (error instanceof Error && error.name === "TRPCClientError") { - const newError: TRPCClientErrorLike = { - message: error.message, - data: error.data, - shape: error.shape, - }; - return { - ...result, - state: { - ...result.state, - error: newError, - }, - }; - } - return result; -} -// copy ends - -interface CreateTRPCNextLayoutOptions { - router: TRouter; - createContext: () => MaybePromise>; - transformer?: DataTransformer; -} - -/** - * @internal - */ -export type DecorateProcedure = TProcedure extends AnyQueryProcedure - ? { - fetch(input: inferProcedureInput): Promise>; - fetchInfinite(input: inferProcedureInput): Promise>; - prefetch(input: inferProcedureInput): Promise>; - prefetchInfinite(input: inferProcedureInput): Promise>; - } - : never; - -type OmitNever = Pick< - TType, - { - [K in keyof TType]: TType[K] extends never ? never : K; - }[keyof TType] ->; -/** - * @internal - */ -export type DecoratedProcedureRecord< - TProcedures extends ProcedureRouterRecord, - TPath extends string = "" -> = OmitNever<{ - [TKey in keyof TProcedures]: TProcedures[TKey] extends AnyRouter - ? DecoratedProcedureRecord - : TProcedures[TKey] extends AnyQueryProcedure - ? DecorateProcedure - : never; -}>; - -type CreateTRPCNextLayout = DecoratedProcedureRecord & { - dehydrate(): Promise; - queryClient: QueryClient; -}; - -const getStateContainer = (opts: CreateTRPCNextLayoutOptions) => { - let _trpc: { - queryClient: QueryClient; - context: inferRouterContext; - } | null = null; - - return () => { - if (_trpc === null) { - _trpc = { - context: opts.createContext(), - queryClient: new QueryClient(), - }; - } - - return _trpc; - }; -}; - -export function createTRPCNextLayout( - opts: CreateTRPCNextLayoutOptions -): CreateTRPCNextLayout { - const getState = getStateContainer(opts); - - const transformer = opts.transformer ?? { - serialize: (v) => v, - deserialize: (v) => v, - }; - - return createFlatProxy((key) => { - const state = getState(); - const { queryClient } = state; - if (key === "queryClient") { - return queryClient; - } - - if (key === "dehydrate") { - // copy starts - // copied from trpc/trpc repo - // ref: https://github.com/trpc/trpc/blob/main/packages/next/src/withTRPC.tsx#L214-#L229 - const dehydratedCache = dehydrate(queryClient, { - shouldDehydrateQuery() { - // makes sure errors are also dehydrated - return true; - }, - }); - - // since error instances can't be serialized, let's make them into `TRPCClientErrorLike`-objects - const dehydratedCacheWithErrors = { - ...dehydratedCache, - queries: dehydratedCache.queries.map(transformQueryOrMutationCacheErrors), - mutations: dehydratedCache.mutations.map(transformQueryOrMutationCacheErrors), - }; - - return () => transformer.serialize(dehydratedCacheWithErrors); - } - // copy ends - - return createRecursiveProxy(async (callOpts) => { - const path = [key, ...callOpts.path]; - const utilName = path.pop(); - const ctx = await state.context; - - const caller = opts.router.createCaller(ctx); - - const pathStr = path.join("."); - const input = callOpts.args[0]; - - if (utilName === "fetchInfinite") { - return queryClient.fetchInfiniteQuery(getArrayQueryKey([path, input], "infinite"), () => - caller.query(pathStr, input) - ); - } - - if (utilName === "prefetch") { - return queryClient.prefetchQuery({ - queryKey: getArrayQueryKey([path, input], "query"), - queryFn: async () => { - const res = await callProcedure({ - procedures: opts.router._def.procedures, - path: pathStr, - rawInput: input, - ctx, - type: "query", - }); - return res; - }, - }); - } - - if (utilName === "prefetchInfinite") { - return queryClient.prefetchInfiniteQuery(getArrayQueryKey([path, input], "infinite"), () => - caller.query(pathStr, input) - ); - } - - return queryClient.fetchQuery(getArrayQueryKey([path, input], "query"), () => - caller.query(pathStr, input) - ); - }) as CreateTRPCNextLayout; - }); -} diff --git a/apps/web/app/_trpc/ssgInit.ts b/apps/web/app/_trpc/ssgInit.ts deleted file mode 100644 index 45e38c519d..0000000000 --- a/apps/web/app/_trpc/ssgInit.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { serverSideTranslations } from "next-i18next/serverSideTranslations"; -import { headers } from "next/headers"; -import superjson from "superjson"; - -import { CALCOM_VERSION } from "@calcom/lib/constants"; -import prisma, { readonlyPrisma } from "@calcom/prisma"; -import { appRouter } from "@calcom/trpc/server/routers/_app"; - -import { createTRPCNextLayout } from "./createTRPCNextLayout"; - -export async function ssgInit() { - const locale = headers().get("x-locale") ?? "en"; - - const i18n = (await serverSideTranslations(locale, ["common"])) || "en"; - - const ssg = createTRPCNextLayout({ - router: appRouter, - transformer: superjson, - createContext() { - return { prisma, insightsDb: readonlyPrisma, session: null, locale, i18n }; - }, - }); - - // i18n translations are already retrieved from serverSideTranslations call, there is no need to run a i18n.fetch - // we can set query data directly to the queryClient - const queryKey = [ - ["viewer", "public", "i18n"], - { input: { locale, CalComVersion: CALCOM_VERSION }, type: "query" }, - ]; - - ssg.queryClient.setQueryData(queryKey, { i18n }); - - return ssg; -} diff --git a/apps/web/app/_trpc/ssrInit.ts b/apps/web/app/_trpc/ssrInit.ts deleted file mode 100644 index ab56003578..0000000000 --- a/apps/web/app/_trpc/ssrInit.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { type GetServerSidePropsContext } from "next"; -import { serverSideTranslations } from "next-i18next/serverSideTranslations"; -import { headers, cookies } from "next/headers"; -import superjson from "superjson"; - -import { getLocale } from "@calcom/features/auth/lib/getLocale"; -import { CALCOM_VERSION } from "@calcom/lib/constants"; -import prisma, { readonlyPrisma } from "@calcom/prisma"; -import { appRouter } from "@calcom/trpc/server/routers/_app"; - -import { createTRPCNextLayout } from "./createTRPCNextLayout"; - -export async function ssrInit(options?: { noI18nPreload: boolean }) { - const req = { - headers: headers(), - cookies: cookies(), - }; - - const locale = await getLocale(req); - - const i18n = (await serverSideTranslations(locale, ["common", "vital"])) || "en"; - - const ssr = createTRPCNextLayout({ - router: appRouter, - transformer: superjson, - createContext() { - return { - prisma, - insightsDb: readonlyPrisma, - session: null, - locale, - i18n, - req: req as unknown as GetServerSidePropsContext["req"], - }; - }, - }); - - // i18n translations are already retrieved from serverSideTranslations call, there is no need to run a i18n.fetch - // we can set query data directly to the queryClient - const queryKey = [ - ["viewer", "public", "i18n"], - { input: { locale, CalComVersion: CALCOM_VERSION }, type: "query" }, - ]; - if (!options?.noI18nPreload) { - ssr.queryClient.setQueryData(queryKey, { i18n }); - } - - await Promise.allSettled([ - // So feature flags are available on first render - ssr.viewer.features.map.prefetch(), - // Provides a better UX to the users who have already upgraded. - ssr.viewer.teams.hasTeamPlan.prefetch(), - ssr.viewer.public.session.prefetch(), - ]); - - return ssr; -} diff --git a/apps/web/app/future/apps/categories/page.tsx b/apps/web/app/future/apps/categories/page.tsx index c878d37732..d3b36bb356 100644 --- a/apps/web/app/future/apps/categories/page.tsx +++ b/apps/web/app/future/apps/categories/page.tsx @@ -1,13 +1,15 @@ import LegacyPage from "@pages/apps/categories/index"; -import { ssrInit } from "app/_trpc/ssrInit"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; -import { cookies, headers } from "next/headers"; import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; import { APP_NAME } from "@calcom/lib/constants"; +import type { buildLegacyCtx } from "@lib/buildLegacyCtx"; + +import { ssrInit } from "@server/lib/ssr"; + export const generateMetadata = async () => { return await _generateMetadata( () => `Categories | ${APP_NAME}`, @@ -15,12 +17,12 @@ export const generateMetadata = async () => { ); }; -async function getPageProps() { - const ssr = await ssrInit(); - const req = { headers: headers(), cookies: cookies() }; +const getData = async (ctx: ReturnType) => { + // @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'. + const ssr = await ssrInit(ctx); // @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest | IncomingMessage - const session = await getServerSession({ req }); + const session = await getServerSession({ req: ctx.req }); let appStore; if (session?.user?.id) { @@ -38,8 +40,8 @@ async function getPageProps() { return { categories: Object.entries(categories).map(([name, count]) => ({ name, count })), - dehydratedState: await ssr.dehydrate(), + dehydratedState: ssr.dehydrate(), }; -} +}; -export default WithLayout({ getData: getPageProps, Page: LegacyPage, getLayout: null })<"P">; +export default WithLayout({ getData, Page: LegacyPage, getLayout: null })<"P">; diff --git a/apps/web/app/future/apps/page.tsx b/apps/web/app/future/apps/page.tsx index 80c04c5491..0a24ec567e 100644 --- a/apps/web/app/future/apps/page.tsx +++ b/apps/web/app/future/apps/page.tsx @@ -1,7 +1,6 @@ import AppsPage from "@pages/apps"; -import { ssrInit } from "app/_trpc/ssrInit"; import { _generateMetadata } from "app/_utils"; -import { cookies, headers } from "next/headers"; +import { WithLayout } from "app/layoutHOC"; import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry"; import { getLayout } from "@calcom/features/MainLayoutAppDir"; @@ -11,7 +10,9 @@ import getUserAdminTeams from "@calcom/features/ee/teams/lib/getUserAdminTeams"; import { APP_NAME } from "@calcom/lib/constants"; import type { AppCategories } from "@calcom/prisma/enums"; -import PageWrapper from "@components/PageWrapperAppDir"; +import type { buildLegacyCtx } from "@lib/buildLegacyCtx"; + +import { ssrInit } from "@server/lib/ssr"; export const generateMetadata = async () => { return await _generateMetadata( @@ -20,12 +21,12 @@ export const generateMetadata = async () => { ); }; -const getPageProps = async () => { - const ssr = await ssrInit(); - const req = { headers: headers(), cookies: cookies() }; +const getData = async (ctx: ReturnType) => { + // @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'. + const ssr = await ssrInit(ctx); // @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest - const session = await getServerSession({ req }); + const session = await getServerSession({ req: ctx.req }); let appStore, userAdminTeams: UserAdminTeams; if (session?.user?.id) { @@ -58,24 +59,8 @@ const getPageProps = async () => { }), appStore, userAdminTeams, - dehydratedState: await ssr.dehydrate(), + dehydratedState: ssr.dehydrate(), }; }; -export default async function AppPageAppDir() { - const { categories, appStore, userAdminTeams, dehydratedState } = await getPageProps(); - - const h = headers(); - const nonce = h.get("x-nonce") ?? undefined; - - return ( - - - - ); -} +export default WithLayout({ getLayout, getData, Page: AppsPage }); diff --git a/apps/web/app/future/bookings/[status]/layout.tsx b/apps/web/app/future/bookings/[status]/layout.tsx index 7391f9996f..3eff385303 100644 --- a/apps/web/app/future/bookings/[status]/layout.tsx +++ b/apps/web/app/future/bookings/[status]/layout.tsx @@ -1,22 +1,21 @@ -import { ssgInit } from "app/_trpc/ssgInit"; -import type { Params } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import { notFound } from "next/navigation"; -import type { ReactElement } from "react"; import { z } from "zod"; import { getLayout } from "@calcom/features/MainLayoutAppDir"; import { APP_NAME } from "@calcom/lib/constants"; +import type { buildLegacyCtx } from "@lib/buildLegacyCtx"; + +import { ssgInit } from "@server/lib/ssg"; + const validStatuses = ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] as const; const querySchema = z.object({ status: z.enum(validStatuses), }); -type Props = { params: Params; children: ReactElement }; - export const generateMetadata = async () => await _generateMetadata( (t) => `${APP_NAME} | ${t("bookings")}`, @@ -27,18 +26,18 @@ export const generateStaticParams = async () => { return validStatuses.map((status) => ({ status })); }; -const getData = async ({ params }: { params: Params }) => { - const parsedParams = querySchema.safeParse(params); +const getData = async (ctx: ReturnType) => { + const parsedParams = querySchema.safeParse(ctx.params); if (!parsedParams.success) { notFound(); } - const ssg = await ssgInit(); + const ssg = await ssgInit(ctx); return { status: parsedParams.data.status, - dehydratedState: await ssg.dehydrate(), + dehydratedState: ssg.dehydrate(), }; }; diff --git a/apps/web/app/future/(individual-page-wrapper)/getting-started/[[...step]]/page.tsx b/apps/web/app/future/getting-started/[[...step]]/page.tsx similarity index 66% rename from apps/web/app/future/(individual-page-wrapper)/getting-started/[[...step]]/page.tsx rename to apps/web/app/future/getting-started/[[...step]]/page.tsx index 5175764d9e..2751566cd5 100644 --- a/apps/web/app/future/(individual-page-wrapper)/getting-started/[[...step]]/page.tsx +++ b/apps/web/app/future/getting-started/[[...step]]/page.tsx @@ -1,14 +1,16 @@ import LegacyPage from "@pages/getting-started/[[...step]]"; -import { ssrInit } from "app/_trpc/ssrInit"; +import { WithLayout } from "app/layoutHOC"; import { cookies, headers } from "next/headers"; import { redirect } from "next/navigation"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; import prisma from "@calcom/prisma"; -import PageWrapper from "@components/PageWrapperAppDir"; +import type { buildLegacyCtx } from "@lib/buildLegacyCtx"; -async function getData() { +import { ssrInit } from "@server/lib/ssr"; + +const getData = async (ctx: ReturnType) => { const req = { headers: headers(), cookies: cookies() }; //@ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest @@ -17,8 +19,8 @@ async function getData() { if (!session?.user?.id) { return redirect("/auth/login"); } - - const ssr = await ssrInit(); + // @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'. + const ssr = await ssrInit(ctx); await ssr.viewer.me.prefetch(); const user = await prisma.user.findUnique({ @@ -51,20 +53,11 @@ async function getData() { } return { - dehydratedState: await ssr.dehydrate(), + dehydratedState: ssr.dehydrate(), hasPendingInvites: user.teams.find((team: any) => team.accepted === false) ?? false, + requiresLicense: false, + themeBasis: null, }; -} +}; -export default async function Page() { - const props = await getData(); - - const h = headers(); - const nonce = h.get("x-nonce") ?? undefined; - - return ( - - - - ); -} +export default WithLayout({ getLayout: null, getData, Page: LegacyPage }); diff --git a/apps/web/app/future/teams/page.tsx b/apps/web/app/future/teams/page.tsx index 0f19dd1523..fe17ec9fb4 100644 --- a/apps/web/app/future/teams/page.tsx +++ b/apps/web/app/future/teams/page.tsx @@ -1,24 +1,29 @@ import OldPage from "@pages/teams/index"; -import { ssrInit } from "app/_trpc/ssrInit"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; -import { type GetServerSidePropsContext } from "next"; import { redirect } from "next/navigation"; import { getLayout } from "@calcom/features/MainLayoutAppDir"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; +import type { buildLegacyCtx } from "@lib/buildLegacyCtx"; + +import { ssrInit } from "@server/lib/ssr"; + export const generateMetadata = async () => await _generateMetadata( (t) => t("teams"), (t) => t("create_manage_teams_collaborative") ); -async function getData(context: Omit) { - const ssr = await ssrInit(); +async function getData(context: ReturnType) { + // @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'. + const ssr = await ssrInit(context); + await ssr.viewer.me.prefetch(); const session = await getServerSession({ + // @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest | (IncomingMessage & { cookies: Partial<{ [key: string]: string; }>; })'. req: context.req, }); @@ -29,8 +34,7 @@ async function getData(context: Omit; diff --git a/apps/web/app/future/video/[uid]/page.tsx b/apps/web/app/future/video/[uid]/page.tsx index b781376423..1e4d7ee00a 100644 --- a/apps/web/app/future/video/[uid]/page.tsx +++ b/apps/web/app/future/video/[uid]/page.tsx @@ -1,15 +1,17 @@ import OldPage from "@pages/video/[uid]"; -import { ssrInit } from "app/_trpc/ssrInit"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import MarkdownIt from "markdown-it"; -import { type GetServerSidePropsContext } from "next"; import { redirect } from "next/navigation"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; import { APP_NAME } from "@calcom/lib/constants"; import prisma, { bookingMinimalSelect } from "@calcom/prisma"; +import type { buildLegacyCtx } from "@lib/buildLegacyCtx"; + +import { ssrInit } from "@server/lib/ssr"; + export const generateMetadata = async () => await _generateMetadata( () => `${APP_NAME} Video`, @@ -18,8 +20,9 @@ export const generateMetadata = async () => const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true }); -async function getData(context: Omit) { - const ssr = await ssrInit(); +async function getData(context: ReturnType) { + // @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'. + const ssr = await ssrInit(context); const booking = await prisma.booking.findUnique({ where: { @@ -76,6 +79,7 @@ async function getData(context: Omit; })'. const session = await getServerSession({ req: context.req }); // set meetingPassword to null for guests @@ -94,9 +98,8 @@ async function getData(context: Omit; diff --git a/apps/web/app/future/video/no-meeting-found/page.tsx b/apps/web/app/future/video/no-meeting-found/page.tsx index 31a15a2ad6..9c69388ad3 100644 --- a/apps/web/app/future/video/no-meeting-found/page.tsx +++ b/apps/web/app/future/video/no-meeting-found/page.tsx @@ -1,19 +1,23 @@ import LegacyPage from "@pages/video/no-meeting-found"; -import { ssrInit } from "app/_trpc/ssrInit"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; +import type { buildLegacyCtx } from "@lib/buildLegacyCtx"; + +import { ssrInit } from "@server/lib/ssr"; + export const generateMetadata = async () => await _generateMetadata( (t) => t("no_meeting_found"), (t) => t("no_meeting_found") ); -const getData = async () => { - const ssr = await ssrInit(); +const getData = async (context: ReturnType) => { + // @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'. + const ssr = await ssrInit(context); return { - dehydratedState: await ssr.dehydrate(), + dehydratedState: ssr.dehydrate(), }; }; From 7dc7f949cfffc64676458f7e65b50aa536668888 Mon Sep 17 00:00:00 2001 From: Keith Williams Date: Tue, 9 Jan 2024 08:13:41 -0300 Subject: [PATCH 6/9] chore: Upgrade deprecated parts of bundle analysis (#13104) * chore: Upgrade deprecated parts of bundle analysis * Added workflow_dispatch to allow for manual runs * Removed conditions just for testing --- .github/workflows/nextjs-bundle-analysis.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/nextjs-bundle-analysis.yml b/.github/workflows/nextjs-bundle-analysis.yml index 811d232d54..4c8da86217 100644 --- a/.github/workflows/nextjs-bundle-analysis.yml +++ b/.github/workflows/nextjs-bundle-analysis.yml @@ -2,6 +2,7 @@ name: "Next.js Bundle Analysis" on: workflow_call: + workflow_dispatch: push: branches: - main @@ -34,7 +35,7 @@ jobs: - name: Download base branch bundle stats uses: dawidd6/action-download-artifact@v2 - if: success() && github.event.number + if: success() with: workflow: nextjs-bundle-analysis.yml branch: ${{ github.event.pull_request.base.ref }} @@ -54,39 +55,39 @@ jobs: # Either of these arguments can be changed or removed by editing the `nextBundleAnalysis` # entry in your package.json file. - name: Compare with base branch bundle - if: success() && github.event.number + if: success() run: | cd apps/web ls -laR .next/analyze/base && npx -p nextjs-bundle-analysis compare - name: Get comment body id: get-comment-body - if: success() && github.event.number + if: success() run: | cd apps/web body=$(cat .next/analyze/__bundle_analysis_comment.txt) body="${body//'%'/'%25'}" body="${body//$'\n'/'%0A'}" body="${body//$'\r'/'%0D'}" - echo ::set-output name=body::$body + echo "{name}={$body}" >> $GITHUB_OUTPUT - name: Find Comment - uses: peter-evans/find-comment@v1 - if: success() && github.event.number + uses: peter-evans/find-comment@v2 + if: success() id: fc with: issue-number: ${{ github.event.number }} body-includes: "" - name: Create Comment - uses: peter-evans/create-or-update-comment@v1.4.4 + uses: peter-evans/create-or-update-comment@v3 if: success() && github.event.number && steps.fc.outputs.comment-id == 0 with: issue-number: ${{ github.event.number }} body: ${{ steps.get-comment-body.outputs.body }} - name: Update Comment - uses: peter-evans/create-or-update-comment@v1.4.4 + uses: peter-evans/create-or-update-comment@v3 if: success() && github.event.number && steps.fc.outputs.comment-id != 0 with: issue-number: ${{ github.event.number }} From 6dbf372ab011d8d3528f84cd0dabf09da81fb659 Mon Sep 17 00:00:00 2001 From: Riddhesh Mahajan <40472653+riddhesh-mahajan@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:46:44 +0530 Subject: [PATCH 7/9] add check for parentId (#13078) --- apps/api/pages/api/teams/[teamId]/_patch.ts | 19 +++++++++++++++++++ apps/api/pages/api/teams/_post.ts | 12 ++++++++++++ 2 files changed, 31 insertions(+) diff --git a/apps/api/pages/api/teams/[teamId]/_patch.ts b/apps/api/pages/api/teams/[teamId]/_patch.ts index 93d1a3a46a..e1cfe2a865 100644 --- a/apps/api/pages/api/teams/[teamId]/_patch.ts +++ b/apps/api/pages/api/teams/[teamId]/_patch.ts @@ -64,6 +64,25 @@ export async function patchHandler(req: NextApiRequest) { where: { id: teamId, members: { some: { userId, role: { in: ["OWNER", "ADMIN"] } } } }, }); if (!_team) throw new HttpError({ statusCode: 401, message: "Unauthorized: OWNER or ADMIN required" }); + + // Check if parentId is related to this user + if (data.parentId && data.parentId === teamId) { + throw new HttpError({ + statusCode: 400, + message: "Bad request: Parent id cannot be the same as the team id.", + }); + } + if (data.parentId) { + const parentTeam = await prisma.team.findFirst({ + where: { id: data.parentId, members: { some: { userId, role: { in: ["OWNER", "ADMIN"] } } } }, + }); + if (!parentTeam) + throw new HttpError({ + statusCode: 401, + message: "Unauthorized: Invalid parent id. You can only use parent id of your own teams.", + }); + } + let paymentUrl; if (_team.slug === null && data.slug) { data.metadata = { diff --git a/apps/api/pages/api/teams/_post.ts b/apps/api/pages/api/teams/_post.ts index 56e0820535..a018ed81d6 100644 --- a/apps/api/pages/api/teams/_post.ts +++ b/apps/api/pages/api/teams/_post.ts @@ -68,6 +68,18 @@ async function postHandler(req: NextApiRequest) { } } + // Check if parentId is related to this user + if (data.parentId) { + const parentTeam = await prisma.team.findFirst({ + where: { id: data.parentId, members: { some: { userId, role: { in: ["OWNER", "ADMIN"] } } } }, + }); + if (!parentTeam) + throw new HttpError({ + statusCode: 401, + message: "Unauthorized: Invalid parent id. You can only use parent id of your own teams.", + }); + } + // TODO: Perhaps there is a better fix for this? const cloneData: typeof data & { metadata: NonNullable | undefined; From c34c27fc0a08f32ae106bd16d7eb0931ff981727 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 9 Jan 2024 11:32:25 +0000 Subject: [PATCH 8/9] fix: Button re-enables on sluggish networks whilst redirecting (#13106) * fix: Button re-enables on sluggish networks whilst re-direct is happening * Add explanatory comment * fix: Booking form validation can take a while --------- Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: Peer Richelsen --- .../BookEventForm/BookEventForm.tsx | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx b/packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx index 0ce4dfbd65..18371c74ed 100644 --- a/packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx +++ b/packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx @@ -200,7 +200,7 @@ export const BookEventFormChild = ({ const { uid, paymentUid } = responseData; const fullName = getFullName(bookingForm.getValues("responses.name")); if (paymentUid) { - return router.push( + router.push( createPaymentLink({ paymentUid, date: timeslot, @@ -225,7 +225,7 @@ export const BookEventFormChild = ({ isRescheduling && bookingData?.startTime ? dayjs(bookingData.startTime).toString() : undefined, }; - return bookingSuccessRedirect({ + bookingSuccessRedirect({ successRedirectUrl: eventType?.successRedirectUrl || "", query, booking: responseData, @@ -272,7 +272,7 @@ export const BookEventFormChild = ({ isRescheduling && bookingData?.startTime ? dayjs(bookingData.startTime).toString() : undefined, }; - return bookingSuccessRedirect({ + bookingSuccessRedirect({ successRedirectUrl: eventType?.successRedirectUrl || "", query, booking, @@ -325,10 +325,6 @@ export const BookEventFormChild = ({ ); const bookEvent = (values: BookingFormValues) => { - // Clears form values stored in store, so old values won't stick around. - setFormValues({}); - bookingForm.clearErrors(); - // It shouldn't be possible that this method is fired without having eventQuery data, // but since in theory (looking at the types) it is possible, we still handle that case. if (!eventQuery?.data) { @@ -375,6 +371,9 @@ export const BookEventFormChild = ({ } else { createBookingMutation.mutate(mapBookingToMutationInput(bookingInput)); } + // Clears form values stored in store, so old values won't stick around. + setFormValues({}); + bookingForm.clearErrors(); }; if (!eventType) { @@ -442,7 +441,14 @@ export const BookEventFormChild = ({