Migrate /team/* page group
improve import type Finalize support isBookingPage finalize fix Fix build error fix type errors
This commit is contained in:
parent
0bdc45a1a5
commit
060f2785a9
|
@ -2,7 +2,8 @@ import type { GetServerSideProps, GetServerSidePropsContext } from "next";
|
||||||
import { notFound, redirect } from "next/navigation";
|
import { notFound, redirect } from "next/navigation";
|
||||||
|
|
||||||
export const withAppDir =
|
export const withAppDir =
|
||||||
(getServerSideProps: GetServerSideProps) => async (context: GetServerSidePropsContext) => {
|
<T extends Record<string, any>>(getServerSideProps: GetServerSideProps<T>) =>
|
||||||
|
async (context: GetServerSidePropsContext): Promise<T> => {
|
||||||
const ssrResponse = await getServerSideProps(context);
|
const ssrResponse = await getServerSideProps(context);
|
||||||
|
|
||||||
if ("redirect" in ssrResponse) {
|
if ("redirect" in ssrResponse) {
|
||||||
|
@ -13,5 +14,9 @@ export const withAppDir =
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ssrResponse.props;
|
return {
|
||||||
|
...ssrResponse.props,
|
||||||
|
// includes dehydratedState required for future page trpcPropvider
|
||||||
|
...("trpcState" in ssrResponse.props && { dehydratedState: ssrResponse.props.trpcState }),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@ import CategoryPage from "@pages/apps/categories/[category]";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
import { type GetServerSidePropsContext } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
|
@ -38,8 +39,8 @@ const querySchema = z.object({
|
||||||
category: z.nativeEnum(AppCategories),
|
category: z.nativeEnum(AppCategories),
|
||||||
});
|
});
|
||||||
|
|
||||||
const getPageProps = async ({ params }: { params: Record<string, string | string[]> }) => {
|
const getPageProps = async (context: GetServerSidePropsContext) => {
|
||||||
const p = querySchema.safeParse(params);
|
const p = querySchema.safeParse(context.params);
|
||||||
|
|
||||||
if (!p.success) {
|
if (!p.success) {
|
||||||
return notFound();
|
return notFound();
|
||||||
|
@ -66,6 +67,5 @@ const getPageProps = async ({ params }: { params: Record<string, string | string
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-expect-error getData arg
|
export default WithLayout({ getData: getPageProps, Page: CategoryPage, getLayout: null })<"P">;
|
||||||
export default WithLayout({ getData: getPageProps, Page: CategoryPage })<"P">;
|
|
||||||
export const dynamic = "force-static";
|
export const dynamic = "force-static";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import LegacyPage from "@pages/apps/categories/index";
|
import LegacyPage from "@pages/apps/categories/index";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import { type GetServerSidePropsContext } from "next";
|
||||||
|
|
||||||
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
|
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import AppsPage from "@pages/apps";
|
import AppsPage from "@pages/apps";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import { type GetServerSidePropsContext } from "next";
|
||||||
|
|
||||||
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
|
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
|
||||||
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import { type GetServerSidePropsContext } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import LegacyPage from "@pages/getting-started/[[...step]]";
|
import LegacyPage from "@pages/getting-started/[[...step]]";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import { type GetServerSidePropsContext } from "next";
|
||||||
import { cookies, headers } from "next/headers";
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
|
@ -10,10 +9,7 @@ import prisma from "@calcom/prisma";
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
const getData = async (ctx: GetServerSidePropsContext) => {
|
const getData = async (ctx: GetServerSidePropsContext) => {
|
||||||
const req = { headers: headers(), cookies: cookies() };
|
const session = await getServerSession({ req: ctx.req });
|
||||||
|
|
||||||
//@ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest
|
|
||||||
const session = await getServerSession({ req });
|
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
if (!session?.user?.id) {
|
||||||
return redirect("/auth/login");
|
return redirect("/auth/login");
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { getServerSideProps } from "@pages/team/[slug]/[type]";
|
||||||
|
import { withAppDir } from "app/AppDirSSRHOC";
|
||||||
|
import type { PageProps } from "app/_types";
|
||||||
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
import { cookies, headers } from "next/headers";
|
||||||
|
|
||||||
|
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||||
|
import withEmbedSsr from "@lib/withEmbedSsr";
|
||||||
|
|
||||||
|
const Page = async ({ params }: PageProps) => {
|
||||||
|
const legacyCtx = buildLegacyCtx(headers(), cookies(), params);
|
||||||
|
|
||||||
|
await withAppDir(withEmbedSsr(getServerSideProps))(legacyCtx as unknown as GetServerSidePropsContext);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
|
@ -0,0 +1,21 @@
|
||||||
|
import LegacyPage, {
|
||||||
|
type PageProps,
|
||||||
|
getServerSideProps as _getServerSideProps,
|
||||||
|
} from "@pages/team/[slug]/[type]";
|
||||||
|
import { withAppDir } from "app/AppDirSSRHOC";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
() => "",
|
||||||
|
() => ""
|
||||||
|
);
|
||||||
|
const getData = withAppDir<PageProps>(_getServerSideProps);
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
Page: LegacyPage,
|
||||||
|
getData,
|
||||||
|
getLayout: null,
|
||||||
|
isBookingPage: true,
|
||||||
|
})<"P">;
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { getServerSideProps } from "@pages/team/[slug]";
|
||||||
|
import { withAppDir } from "app/AppDirSSRHOC";
|
||||||
|
import type { PageProps } from "app/_types";
|
||||||
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
import { cookies, headers } from "next/headers";
|
||||||
|
|
||||||
|
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||||
|
import withEmbedSsr from "@lib/withEmbedSsr";
|
||||||
|
|
||||||
|
const Page = async ({ params }: PageProps) => {
|
||||||
|
const legacyCtx = buildLegacyCtx(headers(), cookies(), params);
|
||||||
|
|
||||||
|
await withAppDir(withEmbedSsr(getServerSideProps))(legacyCtx as unknown as GetServerSidePropsContext);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
|
@ -0,0 +1,29 @@
|
||||||
|
import LegacyPage, { type PageProps, getServerSideProps as _getServerSideProps } from "@pages/team/[slug]";
|
||||||
|
import { withAppDir } from "app/AppDirSSRHOC";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
import { type GetServerSidePropsContext } from "next";
|
||||||
|
import { cookies, headers } from "next/headers";
|
||||||
|
|
||||||
|
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||||
|
|
||||||
|
export const generateMetadata = async ({ params }: { params: Record<string, string | string[]> }) => {
|
||||||
|
const props = await getData(
|
||||||
|
buildLegacyCtx(headers(), cookies(), params) as unknown as GetServerSidePropsContext
|
||||||
|
);
|
||||||
|
const teamName = props.team.name || "Nameless Team";
|
||||||
|
|
||||||
|
return await _generateMetadata(
|
||||||
|
() => teamName,
|
||||||
|
() => teamName
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = withAppDir<PageProps>(_getServerSideProps);
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
Page: LegacyPage,
|
||||||
|
getData,
|
||||||
|
getLayout: null,
|
||||||
|
isBookingPage: true,
|
||||||
|
})<"P">;
|
|
@ -1,7 +1,7 @@
|
||||||
import OldPage from "@pages/teams/index";
|
import OldPage from "@pages/teams/index";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import { type GetServerSidePropsContext } from "next";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||||
|
|
|
@ -2,7 +2,7 @@ import OldPage from "@pages/video/[uid]";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import MarkdownIt from "markdown-it";
|
import MarkdownIt from "markdown-it";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import { type GetServerSidePropsContext } from "next";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
|
|
|
@ -10,9 +10,15 @@ type WithLayoutParams<T extends Record<string, any>> = {
|
||||||
getLayout: ((page: React.ReactElement) => React.ReactNode) | null;
|
getLayout: ((page: React.ReactElement) => React.ReactNode) | null;
|
||||||
Page?: (props: T) => React.ReactElement | null;
|
Page?: (props: T) => React.ReactElement | null;
|
||||||
getData?: (arg: GetServerSidePropsContext) => Promise<T>;
|
getData?: (arg: GetServerSidePropsContext) => Promise<T>;
|
||||||
|
isBookingPage?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function WithLayout<T extends Record<string, any>>({ getLayout, getData, Page }: WithLayoutParams<T>) {
|
export function WithLayout<T extends Record<string, any>>({
|
||||||
|
getLayout,
|
||||||
|
getData,
|
||||||
|
Page,
|
||||||
|
isBookingPage,
|
||||||
|
}: WithLayoutParams<T>) {
|
||||||
return async <P extends "P" | "L">(p: P extends "P" ? PageProps : LayoutProps) => {
|
return async <P extends "P" | "L">(p: P extends "P" ? PageProps : LayoutProps) => {
|
||||||
const h = headers();
|
const h = headers();
|
||||||
const nonce = h.get("x-nonce") ?? undefined;
|
const nonce = h.get("x-nonce") ?? undefined;
|
||||||
|
@ -23,7 +29,13 @@ export function WithLayout<T extends Record<string, any>>({ getLayout, getData,
|
||||||
const children = "children" in p ? p.children : null;
|
const children = "children" in p ? p.children : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper getLayout={getLayout} requiresLicense={false} nonce={nonce} themeBasis={null} {...props}>
|
<PageWrapper
|
||||||
|
getLayout={getLayout}
|
||||||
|
requiresLicense={false}
|
||||||
|
nonce={nonce}
|
||||||
|
themeBasis={null}
|
||||||
|
isBookingPage={isBookingPage}
|
||||||
|
{...props}>
|
||||||
{Page ? <Page {...props} /> : children}
|
{Page ? <Page {...props} /> : children}
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
|
import { getBookingForReschedule, getMultipleDurationValue } from "@calcom/features/bookings/lib/get-booking";
|
||||||
|
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
|
||||||
|
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||||
|
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||||
|
import slugify from "@calcom/lib/slugify";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
import { RedirectType } from "@calcom/prisma/client";
|
||||||
|
|
||||||
|
import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect";
|
||||||
|
|
||||||
|
const paramsSchema = z.object({
|
||||||
|
type: z.string().transform((s) => slugify(s)),
|
||||||
|
slug: z.string().transform((s) => slugify(s)),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Booker page fetches a tiny bit of data server side:
|
||||||
|
// 1. Check if team exists, to show 404
|
||||||
|
// 2. If rescheduling, get the booking details
|
||||||
|
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||||
|
const session = await getServerSession(context);
|
||||||
|
const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(context.params);
|
||||||
|
const { rescheduleUid, duration: queryDuration, isInstantMeeting: queryIsInstantMeeting } = context.query;
|
||||||
|
const { ssrInit } = await import("@server/lib/ssr");
|
||||||
|
const ssr = await ssrInit(context);
|
||||||
|
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug);
|
||||||
|
const isOrgContext = currentOrgDomain && isValidOrgDomain;
|
||||||
|
|
||||||
|
if (!isOrgContext) {
|
||||||
|
const redirect = await getTemporaryOrgRedirect({
|
||||||
|
slug: teamSlug,
|
||||||
|
redirectType: RedirectType.Team,
|
||||||
|
eventTypeSlug: meetingSlug,
|
||||||
|
currentQuery: context.query,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (redirect) {
|
||||||
|
return redirect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const team = await prisma.team.findFirst({
|
||||||
|
where: {
|
||||||
|
...getSlugOrRequestedSlug(teamSlug),
|
||||||
|
parent: isValidOrgDomain && currentOrgDomain ? getSlugOrRequestedSlug(currentOrgDomain) : null,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
hideBranding: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!team) {
|
||||||
|
return {
|
||||||
|
notFound: true,
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
let booking: GetBookingType | null = null;
|
||||||
|
if (rescheduleUid) {
|
||||||
|
booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = isValidOrgDomain ? currentOrgDomain : null;
|
||||||
|
// We use this to both prefetch the query on the server,
|
||||||
|
// as well as to check if the event exist, so we c an show a 404 otherwise.
|
||||||
|
const eventData = await ssr.viewer.public.event.fetch({
|
||||||
|
username: teamSlug,
|
||||||
|
eventSlug: meetingSlug,
|
||||||
|
isTeamEvent: true,
|
||||||
|
org,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!eventData) {
|
||||||
|
return {
|
||||||
|
notFound: true,
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
entity: eventData.entity,
|
||||||
|
duration: getMultipleDurationValue(
|
||||||
|
eventData.metadata?.multipleDuration,
|
||||||
|
queryDuration,
|
||||||
|
eventData.length
|
||||||
|
),
|
||||||
|
booking,
|
||||||
|
away: false,
|
||||||
|
user: teamSlug,
|
||||||
|
teamId: team.id,
|
||||||
|
slug: meetingSlug,
|
||||||
|
trpcState: ssr.dehydrate(),
|
||||||
|
isBrandingHidden: team?.hideBranding,
|
||||||
|
isInstantMeeting: eventData.isInstantEvent && queryIsInstantMeeting ? true : false,
|
||||||
|
themeBasis: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,137 @@
|
||||||
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
|
||||||
|
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||||
|
import { getFeatureFlagMap } from "@calcom/features/flags/server/utils";
|
||||||
|
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
|
||||||
|
import logger from "@calcom/lib/logger";
|
||||||
|
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||||
|
import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
|
||||||
|
import slugify from "@calcom/lib/slugify";
|
||||||
|
import { stripMarkdown } from "@calcom/lib/stripMarkdown";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
import { RedirectType } from "@calcom/prisma/client";
|
||||||
|
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||||
|
|
||||||
|
import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect";
|
||||||
|
|
||||||
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
|
const log = logger.getSubLogger({ prefix: ["team/[slug]"] });
|
||||||
|
|
||||||
|
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||||
|
const slug = Array.isArray(context.query?.slug) ? context.query.slug.pop() : context.query.slug;
|
||||||
|
const { isValidOrgDomain, currentOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug);
|
||||||
|
const isOrgContext = isValidOrgDomain && currentOrgDomain;
|
||||||
|
|
||||||
|
// Provided by Rewrite from next.config.js
|
||||||
|
const isOrgProfile = context.query?.isOrgProfile === "1";
|
||||||
|
const flags = await getFeatureFlagMap(prisma);
|
||||||
|
const isOrganizationFeatureEnabled = flags["organizations"];
|
||||||
|
|
||||||
|
log.debug("getServerSideProps", {
|
||||||
|
isOrgProfile,
|
||||||
|
isOrganizationFeatureEnabled,
|
||||||
|
isValidOrgDomain,
|
||||||
|
currentOrgDomain,
|
||||||
|
});
|
||||||
|
|
||||||
|
const team = await getTeamWithMembers({
|
||||||
|
slug: slugify(slug ?? ""),
|
||||||
|
orgSlug: currentOrgDomain,
|
||||||
|
isTeamView: true,
|
||||||
|
isOrgView: isValidOrgDomain && isOrgProfile,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isOrgContext && slug) {
|
||||||
|
const redirect = await getTemporaryOrgRedirect({
|
||||||
|
slug: slug,
|
||||||
|
redirectType: RedirectType.Team,
|
||||||
|
eventTypeSlug: null,
|
||||||
|
currentQuery: context.query,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (redirect) {
|
||||||
|
return redirect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ssr = await ssrInit(context);
|
||||||
|
const metadata = teamMetadataSchema.parse(team?.metadata ?? {});
|
||||||
|
|
||||||
|
// Taking care of sub-teams and orgs
|
||||||
|
if (
|
||||||
|
(!isValidOrgDomain && team?.parent) ||
|
||||||
|
(!isValidOrgDomain && !!metadata?.isOrganization) ||
|
||||||
|
!isOrganizationFeatureEnabled
|
||||||
|
) {
|
||||||
|
return { notFound: true } as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!team || (team.parent && !team.parent.slug)) {
|
||||||
|
const unpublishedTeam = await prisma.team.findFirst({
|
||||||
|
where: {
|
||||||
|
...(team?.parent
|
||||||
|
? { id: team.parent.id }
|
||||||
|
: {
|
||||||
|
metadata: {
|
||||||
|
path: ["requestedSlug"],
|
||||||
|
equals: slug,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!unpublishedTeam) return { notFound: true } as const;
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
isUnpublished: true,
|
||||||
|
team: { ...unpublishedTeam, createdAt: null },
|
||||||
|
trpcState: ssr.dehydrate(),
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
team.eventTypes =
|
||||||
|
team.eventTypes?.map((type) => ({
|
||||||
|
...type,
|
||||||
|
users: type.users.map((user) => ({
|
||||||
|
...user,
|
||||||
|
avatar: `/${user.username}/avatar.png`,
|
||||||
|
})),
|
||||||
|
descriptionAsSafeHTML: markdownToSafeHTML(type.description),
|
||||||
|
})) ?? null;
|
||||||
|
|
||||||
|
const safeBio = markdownToSafeHTML(team.bio) || "";
|
||||||
|
|
||||||
|
const members = !team.isPrivate
|
||||||
|
? team.members.map((member) => {
|
||||||
|
return {
|
||||||
|
name: member.name,
|
||||||
|
id: member.id,
|
||||||
|
bio: member.bio,
|
||||||
|
subteams: member.subteams,
|
||||||
|
username: member.username,
|
||||||
|
accepted: member.accepted,
|
||||||
|
organizationId: member.organizationId,
|
||||||
|
safeBio: markdownToSafeHTML(member.bio || ""),
|
||||||
|
bookerUrl: getBookerBaseUrlSync(member.organization?.slug || ""),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const markdownStrippedBio = stripMarkdown(team?.bio || "");
|
||||||
|
|
||||||
|
const { inviteToken: _inviteToken, ...serializableTeam } = team;
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
team: { ...serializableTeam, safeBio, members, metadata },
|
||||||
|
themeBasis: serializableTeam.slug,
|
||||||
|
trpcState: ssr.dehydrate(),
|
||||||
|
markdownStrippedBio,
|
||||||
|
isValidOrgDomain,
|
||||||
|
currentOrgDomain,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
};
|
|
@ -1,3 +1,5 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
// This route is reachable by
|
// This route is reachable by
|
||||||
// 1. /team/[slug]
|
// 1. /team/[slug]
|
||||||
// 2. / (when on org domain e.g. http://calcom.cal.com/. This is through a rewrite from next.config.js)
|
// 2. / (when on org domain e.g. http://calcom.cal.com/. This is through a rewrite from next.config.js)
|
||||||
|
@ -5,45 +7,32 @@
|
||||||
// 1. org/[orgSlug]/team/[slug]
|
// 1. org/[orgSlug]/team/[slug]
|
||||||
// 2. org/[orgSlug]/[user]/[type]
|
// 2. org/[orgSlug]/[user]/[type]
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
import { sdkActionManager, useIsEmbed } from "@calcom/embed-core/embed-iframe";
|
import { sdkActionManager, useIsEmbed } from "@calcom/embed-core/embed-iframe";
|
||||||
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
|
||||||
import EventTypeDescription from "@calcom/features/eventtypes/components/EventTypeDescription";
|
import EventTypeDescription from "@calcom/features/eventtypes/components/EventTypeDescription";
|
||||||
import { getFeatureFlagMap } from "@calcom/features/flags/server/utils";
|
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
||||||
import useTheme from "@calcom/lib/hooks/useTheme";
|
import useTheme from "@calcom/lib/hooks/useTheme";
|
||||||
import logger from "@calcom/lib/logger";
|
|
||||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
|
||||||
import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
|
|
||||||
import slugify from "@calcom/lib/slugify";
|
|
||||||
import { stripMarkdown } from "@calcom/lib/stripMarkdown";
|
|
||||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||||
import prisma from "@calcom/prisma";
|
|
||||||
import { RedirectType } from "@calcom/prisma/client";
|
|
||||||
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||||
import { Avatar, Button, HeadSeo, UnpublishedEntity } from "@calcom/ui";
|
import { Avatar, Button, HeadSeo, UnpublishedEntity } from "@calcom/ui";
|
||||||
import { ArrowRight } from "@calcom/ui/components/icon";
|
import { ArrowRight } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import { useToggleQuery } from "@lib/hooks/useToggleQuery";
|
import { useToggleQuery } from "@lib/hooks/useToggleQuery";
|
||||||
|
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
|
||||||
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
|
|
||||||
import PageWrapper from "@components/PageWrapper";
|
import PageWrapper from "@components/PageWrapper";
|
||||||
import Team from "@components/team/screens/Team";
|
import Team from "@components/team/screens/Team";
|
||||||
import { UserAvatarGroup } from "@components/ui/avatar/UserAvatarGroup";
|
import { UserAvatarGroup } from "@components/ui/avatar/UserAvatarGroup";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
export { getServerSideProps };
|
||||||
|
|
||||||
import { getTemporaryOrgRedirect } from "../../lib/getTemporaryOrgRedirect";
|
|
||||||
|
|
||||||
export type PageProps = inferSSRProps<typeof getServerSideProps>;
|
export type PageProps = inferSSRProps<typeof getServerSideProps>;
|
||||||
const log = logger.getSubLogger({ prefix: ["team/[slug]"] });
|
|
||||||
function TeamPage({
|
function TeamPage({
|
||||||
team,
|
team,
|
||||||
isUnpublished,
|
isUnpublished,
|
||||||
|
@ -268,124 +257,6 @@ function TeamPage({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
|
||||||
const slug = Array.isArray(context.query?.slug) ? context.query.slug.pop() : context.query.slug;
|
|
||||||
const { isValidOrgDomain, currentOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug);
|
|
||||||
const isOrgContext = isValidOrgDomain && currentOrgDomain;
|
|
||||||
|
|
||||||
// Provided by Rewrite from next.config.js
|
|
||||||
const isOrgProfile = context.query?.isOrgProfile === "1";
|
|
||||||
const flags = await getFeatureFlagMap(prisma);
|
|
||||||
const isOrganizationFeatureEnabled = flags["organizations"];
|
|
||||||
|
|
||||||
log.debug("getServerSideProps", {
|
|
||||||
isOrgProfile,
|
|
||||||
isOrganizationFeatureEnabled,
|
|
||||||
isValidOrgDomain,
|
|
||||||
currentOrgDomain,
|
|
||||||
});
|
|
||||||
|
|
||||||
const team = await getTeamWithMembers({
|
|
||||||
slug: slugify(slug ?? ""),
|
|
||||||
orgSlug: currentOrgDomain,
|
|
||||||
isTeamView: true,
|
|
||||||
isOrgView: isValidOrgDomain && isOrgProfile,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isOrgContext && slug) {
|
|
||||||
const redirect = await getTemporaryOrgRedirect({
|
|
||||||
slug: slug,
|
|
||||||
redirectType: RedirectType.Team,
|
|
||||||
eventTypeSlug: null,
|
|
||||||
currentQuery: context.query,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (redirect) {
|
|
||||||
return redirect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ssr = await ssrInit(context);
|
|
||||||
const metadata = teamMetadataSchema.parse(team?.metadata ?? {});
|
|
||||||
|
|
||||||
// Taking care of sub-teams and orgs
|
|
||||||
if (
|
|
||||||
(!isValidOrgDomain && team?.parent) ||
|
|
||||||
(!isValidOrgDomain && !!metadata?.isOrganization) ||
|
|
||||||
!isOrganizationFeatureEnabled
|
|
||||||
) {
|
|
||||||
return { notFound: true } as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!team || (team.parent && !team.parent.slug)) {
|
|
||||||
const unpublishedTeam = await prisma.team.findFirst({
|
|
||||||
where: {
|
|
||||||
...(team?.parent
|
|
||||||
? { id: team.parent.id }
|
|
||||||
: {
|
|
||||||
metadata: {
|
|
||||||
path: ["requestedSlug"],
|
|
||||||
equals: slug,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!unpublishedTeam) return { notFound: true } as const;
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
isUnpublished: true,
|
|
||||||
team: { ...unpublishedTeam, createdAt: null },
|
|
||||||
trpcState: ssr.dehydrate(),
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
team.eventTypes =
|
|
||||||
team.eventTypes?.map((type) => ({
|
|
||||||
...type,
|
|
||||||
users: type.users.map((user) => ({
|
|
||||||
...user,
|
|
||||||
avatar: `/${user.username}/avatar.png`,
|
|
||||||
})),
|
|
||||||
descriptionAsSafeHTML: markdownToSafeHTML(type.description),
|
|
||||||
})) ?? null;
|
|
||||||
|
|
||||||
const safeBio = markdownToSafeHTML(team.bio) || "";
|
|
||||||
|
|
||||||
const members = !team.isPrivate
|
|
||||||
? team.members.map((member) => {
|
|
||||||
return {
|
|
||||||
name: member.name,
|
|
||||||
id: member.id,
|
|
||||||
bio: member.bio,
|
|
||||||
subteams: member.subteams,
|
|
||||||
username: member.username,
|
|
||||||
accepted: member.accepted,
|
|
||||||
organizationId: member.organizationId,
|
|
||||||
safeBio: markdownToSafeHTML(member.bio || ""),
|
|
||||||
bookerUrl: getBookerBaseUrlSync(member.organization?.slug || ""),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const markdownStrippedBio = stripMarkdown(team?.bio || "");
|
|
||||||
|
|
||||||
const { inviteToken: _inviteToken, ...serializableTeam } = team;
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
team: { ...serializableTeam, safeBio, members, metadata },
|
|
||||||
themeBasis: serializableTeam.slug,
|
|
||||||
trpcState: ssr.dehydrate(),
|
|
||||||
markdownStrippedBio,
|
|
||||||
isValidOrgDomain,
|
|
||||||
currentOrgDomain,
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
};
|
|
||||||
|
|
||||||
TeamPage.isBookingPage = true;
|
TeamPage.isBookingPage = true;
|
||||||
TeamPage.PageWrapper = PageWrapper;
|
TeamPage.PageWrapper = PageWrapper;
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,19 @@
|
||||||
import type { GetServerSidePropsContext } from "next";
|
"use client";
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { Booker } from "@calcom/atoms";
|
import { Booker } from "@calcom/atoms";
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
|
||||||
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
|
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
|
||||||
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
|
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
|
||||||
import { getBookingForReschedule, getMultipleDurationValue } from "@calcom/features/bookings/lib/get-booking";
|
|
||||||
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
|
|
||||||
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
|
|
||||||
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
|
||||||
import slugify from "@calcom/lib/slugify";
|
|
||||||
import prisma from "@calcom/prisma";
|
|
||||||
import { RedirectType } from "@calcom/prisma/client";
|
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
|
||||||
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
import type { EmbedProps } from "@lib/withEmbedSsr";
|
import type { EmbedProps } from "@lib/withEmbedSsr";
|
||||||
|
|
||||||
import PageWrapper from "@components/PageWrapper";
|
import PageWrapper from "@components/PageWrapper";
|
||||||
|
|
||||||
import { getTemporaryOrgRedirect } from "../../../lib/getTemporaryOrgRedirect";
|
|
||||||
|
|
||||||
export type PageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;
|
export type PageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;
|
||||||
|
|
||||||
|
export { getServerSideProps };
|
||||||
|
|
||||||
export default function Type({
|
export default function Type({
|
||||||
slug,
|
slug,
|
||||||
user,
|
user,
|
||||||
|
@ -61,92 +53,3 @@ export default function Type({
|
||||||
|
|
||||||
Type.PageWrapper = PageWrapper;
|
Type.PageWrapper = PageWrapper;
|
||||||
Type.isBookingPage = true;
|
Type.isBookingPage = true;
|
||||||
|
|
||||||
const paramsSchema = z.object({
|
|
||||||
type: z.string().transform((s) => slugify(s)),
|
|
||||||
slug: z.string().transform((s) => slugify(s)),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Booker page fetches a tiny bit of data server side:
|
|
||||||
// 1. Check if team exists, to show 404
|
|
||||||
// 2. If rescheduling, get the booking details
|
|
||||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
|
||||||
const session = await getServerSession(context);
|
|
||||||
const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(context.params);
|
|
||||||
const { rescheduleUid, duration: queryDuration, isInstantMeeting: queryIsInstantMeeting } = context.query;
|
|
||||||
const { ssrInit } = await import("@server/lib/ssr");
|
|
||||||
const ssr = await ssrInit(context);
|
|
||||||
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug);
|
|
||||||
const isOrgContext = currentOrgDomain && isValidOrgDomain;
|
|
||||||
|
|
||||||
if (!isOrgContext) {
|
|
||||||
const redirect = await getTemporaryOrgRedirect({
|
|
||||||
slug: teamSlug,
|
|
||||||
redirectType: RedirectType.Team,
|
|
||||||
eventTypeSlug: meetingSlug,
|
|
||||||
currentQuery: context.query,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (redirect) {
|
|
||||||
return redirect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const team = await prisma.team.findFirst({
|
|
||||||
where: {
|
|
||||||
...getSlugOrRequestedSlug(teamSlug),
|
|
||||||
parent: isValidOrgDomain && currentOrgDomain ? getSlugOrRequestedSlug(currentOrgDomain) : null,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
hideBranding: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!team) {
|
|
||||||
return {
|
|
||||||
notFound: true,
|
|
||||||
} as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
let booking: GetBookingType | null = null;
|
|
||||||
if (rescheduleUid) {
|
|
||||||
booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
const org = isValidOrgDomain ? currentOrgDomain : null;
|
|
||||||
// We use this to both prefetch the query on the server,
|
|
||||||
// as well as to check if the event exist, so we c an show a 404 otherwise.
|
|
||||||
const eventData = await ssr.viewer.public.event.fetch({
|
|
||||||
username: teamSlug,
|
|
||||||
eventSlug: meetingSlug,
|
|
||||||
isTeamEvent: true,
|
|
||||||
org,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!eventData) {
|
|
||||||
return {
|
|
||||||
notFound: true,
|
|
||||||
} as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
entity: eventData.entity,
|
|
||||||
duration: getMultipleDurationValue(
|
|
||||||
eventData.metadata?.multipleDuration,
|
|
||||||
queryDuration,
|
|
||||||
eventData.length
|
|
||||||
),
|
|
||||||
booking,
|
|
||||||
away: false,
|
|
||||||
user: teamSlug,
|
|
||||||
teamId: team.id,
|
|
||||||
slug: meetingSlug,
|
|
||||||
trpcState: ssr.dehydrate(),
|
|
||||||
isBrandingHidden: team?.hideBranding,
|
|
||||||
isInstantMeeting: eventData.isInstantEvent && queryIsInstantMeeting ? true : false,
|
|
||||||
themeBasis: null,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import withEmbedSsr from "@lib/withEmbedSsr";
|
import withEmbedSsr from "@lib/withEmbedSsr";
|
||||||
|
|
||||||
import { getServerSideProps as _getServerSideProps } from "../[type]";
|
import { getServerSideProps as _getServerSideProps } from "../[type]";
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import withEmbedSsr from "@lib/withEmbedSsr";
|
import withEmbedSsr from "@lib/withEmbedSsr";
|
||||||
|
|
||||||
import { getServerSideProps as _getServerSideProps } from "../../team/[slug]";
|
import { getServerSideProps as _getServerSideProps } from "../../team/[slug]";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user