diff --git a/apps/web/components/team/screens/Team.tsx b/apps/web/components/team/screens/Team.tsx deleted file mode 100644 index 105e06d737..0000000000 --- a/apps/web/components/team/screens/Team.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery"; -import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; -import type { UserProfile } from "@calcom/types/UserProfile"; -import { UserAvatar } from "@calcom/ui/components/avatar"; -import Link from "next/link"; - -type TeamWithMembers = { - id: number; - name: string; - slug: string | null; - logoUrl?: string | null; - bio?: string | null; - members: { - id: number; - name: string | null; - bio?: string | null; - username?: string | null; - avatar?: string | null; - }[]; -}; - -type TeamType = Omit, "inviteToken">; -type MembersType = TeamType["members"]; -type MemberType = Pick & { - username: string | null; - organizationId?: number | null; - avatarUrl: string | null; -} & { - profile: Omit; - safeBio: string | null; - bookerUrl: string; -}; - -const Member = ({ member, teamName }: { member: MemberType; teamName: string | null }) => { - const routerQuery = useRouterQuery(); - const { t } = useLocale(); - const isBioEmpty = !member.bio || !member.bio.replace("


", "").length; - - // We don't want to forward orgSlug and user which are route params to the next route - const { slug: _slug, orgSlug: _orgSlug, user: _user, ...queryParamsToForward } = routerQuery; - - return ( - -
- -
-

{member.name}

-
- {!isBioEmpty ? ( -
- ) : ( - t("user_from_team", { user: member.name, team: teamName }) - )} -
-
-
- - ); -}; - -const Members = ({ members, teamName }: { members: MemberType[]; teamName: string | null }) => { - if (!members || members.length === 0) { - return null; - } - - return ( -
- {members.map((member) => { - return member.username !== null && ; - })} -
- ); -}; - -const Team = ({ members, teamName }: { members: MemberType[]; teamName: string | null }) => { - return ( -
- -
- ); -}; - -export default Team; diff --git a/apps/web/lib/org/[orgSlug]/[user]/[type]/getServerSideProps.tsx b/apps/web/lib/org/[orgSlug]/[user]/[type]/getServerSideProps.tsx deleted file mode 100644 index 81522501e9..0000000000 --- a/apps/web/lib/org/[orgSlug]/[user]/[type]/getServerSideProps.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import slugify from "@calcom/lib/slugify"; -import prisma from "@calcom/prisma"; -import { getServerSideProps as GSSTeamTypePage } from "@lib/team/[slug]/[type]/getServerSideProps"; -import { getServerSideProps as GSSUserTypePage } from "@server/lib/[user]/[type]/getServerSideProps"; -import type { GetServerSidePropsContext } from "next"; -import z from "zod"; - -const paramsSchema = z.object({ - orgSlug: z.string().transform((s) => slugify(s)), - user: z.string(), - type: z.string().transform((s) => slugify(s)), -}); - -export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { - const { user: teamOrUserSlugOrDynamicGroup, orgSlug, type } = paramsSchema.parse(ctx.params); - const team = await prisma.team.findFirst({ - where: { - slug: slugify(teamOrUserSlugOrDynamicGroup), - parentId: { - not: null, - }, - parent: { slug: orgSlug }, - }, - select: { - id: true, - }, - }); - - if (team) { - const params = { slug: teamOrUserSlugOrDynamicGroup, type }; - return GSSTeamTypePage({ - ...ctx, - params: { - ...ctx.params, - ...params, - }, - query: { - ...ctx.query, - ...params, - }, - }); - } - const params = { user: teamOrUserSlugOrDynamicGroup, type }; - return GSSUserTypePage({ - ...ctx, - params: { - ...ctx.params, - ...params, - }, - query: { - ...ctx.query, - ...params, - }, - }); -}; diff --git a/apps/web/lib/org/[orgSlug]/[user]/getServerSideProps.tsx b/apps/web/lib/org/[orgSlug]/[user]/getServerSideProps.tsx deleted file mode 100644 index b22e22dc5c..0000000000 --- a/apps/web/lib/org/[orgSlug]/[user]/getServerSideProps.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import prisma from "@calcom/prisma"; -import { getServerSideProps as GSSTeamPage } from "@lib/team/[slug]/getServerSideProps"; -import { getServerSideProps as GSSUserPage } from "@server/lib/[user]/getServerSideProps"; -import type { GetServerSidePropsContext } from "next"; - -export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { - const team = await prisma.team.findFirst({ - where: { - slug: ctx.query.user as string, - parentId: { - not: null, - }, - parent: { slug: ctx.query.orgSlug as string }, - }, - select: { - id: true, - }, - }); - if (team) { - return GSSTeamPage({ - ...ctx, - query: { - slug: ctx.query.user, - ...(ctx.query.orgRedirection !== undefined && { orgRedirection: ctx.query.orgRedirection }), - }, - }); - } - return GSSUserPage({ - ...ctx, - query: { - user: ctx.query.user, - ...(ctx.query.redirect !== undefined && { redirect: ctx.query.redirect }), - ...(ctx.query.orgRedirection !== undefined && { orgRedirection: ctx.query.orgRedirection }), - }, - }); -}; diff --git a/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx b/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx deleted file mode 100644 index 92d5999ac6..0000000000 --- a/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx +++ /dev/null @@ -1,305 +0,0 @@ -import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -import { getBookingForReschedule } from "@calcom/features/bookings/lib/get-booking"; -import { getBrandingForEventType } from "@calcom/features/profile/lib/getBranding"; -import { shouldHideBrandingForTeamEvent } from "@calcom/features/profile/lib/hideBranding"; -import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; -import logger from "@calcom/lib/logger"; -import slugify from "@calcom/lib/slugify"; -import { prisma } from "@calcom/prisma"; -import type { User } from "@calcom/prisma/client"; -import { BookingStatus, RedirectType, SchedulingType } from "@calcom/prisma/enums"; -import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; -import { handleOrgRedirect } from "@lib/handleOrgRedirect"; -import type { GetServerSidePropsContext } from "next"; -import { z } from "zod"; - -const paramsSchema = z.object({ - type: z.string().transform((s) => slugify(s)), - slug: z.string().transform((s) => slugify(s)), -}); - -export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const { req, params, query } = context; - const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(params); - const { rescheduleUid, bookingUid } = query; - const allowRescheduleForCancelledBooking = query.allowRescheduleForCancelledBooking === "true"; - const currentOrgDomain = null; - const isValidOrgDomain = false; - - const redirect = await handleOrgRedirect({ - slugs: [teamSlug], - redirectType: RedirectType.Team, - eventTypeSlug: meetingSlug, - context, - currentOrgDomain: isValidOrgDomain ? currentOrgDomain : null, - }); - - if (redirect) { - return redirect; - } - - const team = await getTeamWithEventsData(teamSlug, meetingSlug, isValidOrgDomain, currentOrgDomain); - - if (!team || !team.eventTypes?.[0]) { - return { notFound: true } as const; - } - - const eventData = team.eventTypes[0]; - - if (eventData.schedulingType === SchedulingType.MANAGED) { - return { notFound: true } as const; - } - - if (rescheduleUid && eventData.disableRescheduling) { - return { redirect: { destination: `/booking/${rescheduleUid}`, permanent: false } }; - } - - const eventTypeId = eventData.id; - const orgSlug = isValidOrgDomain ? currentOrgDomain : null; - const name = team.parent?.name ?? team.name ?? null; - const fromRedirectOfNonOrgLink = context.query.orgRedirection === "true"; - const isUnpublished = team.parent ? !team.parent.slug : !team.slug; - - const crmContactOwnerEmail = query["cal.crmContactOwnerEmail"]; - const crmContactOwnerRecordType = query["cal.crmContactOwnerRecordType"]; - const crmAppSlugParam = query["cal.crmAppSlug"]; - const crmRecordIdParam = query["cal.crmRecordId"]; - const crmLookupDoneParam = query["cal.crmLookupDone"]; - - const crmContactOwnerEmailStr = Array.isArray(crmContactOwnerEmail) - ? crmContactOwnerEmail[0] - : crmContactOwnerEmail; - const crmOwnerRecordTypeStr = Array.isArray(crmContactOwnerRecordType) - ? crmContactOwnerRecordType[0] - : crmContactOwnerRecordType; - const crmAppSlugStr = Array.isArray(crmAppSlugParam) ? crmAppSlugParam[0] : crmAppSlugParam; - const crmRecordIdStr = Array.isArray(crmRecordIdParam) ? crmRecordIdParam[0] : crmRecordIdParam; - - // If crmLookupDone is true, the router already performed the CRM lookup, so skip it here - const crmLookupDone = - (Array.isArray(crmLookupDoneParam) ? crmLookupDoneParam[0] : crmLookupDoneParam) === "true"; - - const needsCrmLookup = - !crmLookupDone && (!crmContactOwnerEmailStr || !crmOwnerRecordTypeStr || !crmAppSlugStr); - - // Run independent queries in parallel — these all depend on team/eventData but not on each other - const log = logger.getSubLogger({ prefix: ["team-event-ssr", `${teamSlug}/${meetingSlug}`] }); - const [eventHostsUserData, crmResult, booking] = await Promise.all([ - getUsersData( - team.isPrivate, - eventTypeId, - eventData.hosts.map((h) => h.user) - ).catch((err) => { - log.error("Failed to get users data", err); - throw err; - }), - Promise.resolve( - null as { email: string; recordType: string; crmAppSlug: string; recordId: string } | null - ), - rescheduleUid - ? getServerSession({ req }) - .then((session) => getBookingForReschedule(`${rescheduleUid}`, session?.user?.id)) - .catch((err) => { - log.error("Failed to get booking for reschedule", err); - throw err; - }) - : Promise.resolve(null), - ]); - - if ( - booking?.status === BookingStatus.CANCELLED && - !allowRescheduleForCancelledBooking && - !eventData.allowReschedulingCancelledBookings - ) { - return { - redirect: { - permanent: false, - destination: `/team/${teamSlug}/${meetingSlug}`, - }, - }; - } - - const teamMemberEmail = crmResult?.email ?? crmContactOwnerEmailStr; - const crmOwnerRecordType = crmResult?.recordType ?? crmOwnerRecordTypeStr; - const crmAppSlug = crmResult?.crmAppSlug ?? crmAppSlugStr; - const crmRecordId = crmResult?.recordId ?? crmRecordIdStr; - - const organizationSettings = {} as { allowSEOIndexing?: boolean }; - const allowSEOIndexing = organizationSettings?.allowSEOIndexing ?? false; - - const useApiV2 = false; - - const branding = getBrandingForEventType({ - eventType: { - team: team.parent ?? team, - users: [], - profile: null, - }, - }); - - return { - props: { - useApiV2, - eventData: { - eventTypeId, - entity: { - fromRedirectOfNonOrgLink, - considerUnpublished: isUnpublished && !fromRedirectOfNonOrgLink, - orgSlug, - teamSlug: team.slug ?? null, - name, - }, - length: eventData.length, - metadata: EventTypeMetaDataSchema.parse(eventData.metadata), - profile: { - image: team.parent - ? getPlaceholderAvatar(team.parent.logoUrl, team.parent.name) - : getPlaceholderAvatar(team.logoUrl, team.name), - name, - username: orgSlug ?? null, - ...branding, - }, - title: eventData.title, - users: eventHostsUserData, - hidden: eventData.hidden, - interfaceLanguage: eventData.interfaceLanguage, - }, - booking, - user: teamSlug, - teamId: team.id, - slug: meetingSlug, - isBrandingHidden: shouldHideBrandingForTeamEvent({ - eventTypeId: eventData.id, - team, - }), - themeBasis: null, - orgBannerUrl: team.parent?.bannerUrl ?? "", - teamMemberEmail, - crmOwnerRecordType, - crmAppSlug, - crmRecordId, - isSEOIndexable: allowSEOIndexing, - }, - }; -}; - -const getTeamWithEventsData = async ( - teamSlug: string, - meetingSlug: string, - isValidOrgDomain: boolean, - currentOrgDomain: string | null -) => { - return await prisma.team.findFirst({ - where: { - ...{ slug: teamSlug }, - parent: isValidOrgDomain && currentOrgDomain ? { slug: currentOrgDomain } : null, - }, - orderBy: { - slug: { sort: "asc", nulls: "last" }, - }, - select: { - id: true, - isPrivate: true, - hideBranding: true, - parent: { - select: { - slug: true, - name: true, - bannerUrl: true, - logoUrl: true, - hideBranding: true, - brandColor: true, - darkBrandColor: true, - theme: true, - organizationSettings: { - select: { - allowSEOIndexing: true, - }, - }, - }, - }, - logoUrl: true, - name: true, - slug: true, - brandColor: true, - darkBrandColor: true, - theme: true, - eventTypes: { - where: { - slug: meetingSlug, - }, - select: { - id: true, - title: true, - isInstantEvent: true, - schedulingType: true, - metadata: true, - length: true, - hidden: true, - disableCancelling: true, - disableRescheduling: true, - allowReschedulingCancelledBookings: true, - interfaceLanguage: true, - hosts: { - take: 3, - select: { - user: { - select: { - name: true, - username: true, - email: true, - }, - }, - }, - }, - }, - }, - isOrganization: true, - organizationSettings: { - select: { - allowSEOIndexing: true, - }, - }, - }, - }); -}; - -const getUsersData = async ( - isPrivateTeam: boolean, - eventTypeId: number, - users: Pick[] -) => { - if (!isPrivateTeam && users.length > 0) { - return users - .filter((user) => user.username) - .map((user) => ({ - username: user.username ?? "", - name: user.name ?? "", - })); - } - if (!isPrivateTeam && users.length === 0) { - const { users: data } = await prisma.eventType.findUniqueOrThrow({ - where: { id: eventTypeId }, - select: { - users: { - take: 1, - select: { - username: true, - name: true, - }, - }, - }, - }); - - return data.length > 0 - ? [ - { - username: data[0].username ?? "", - name: data[0].name ?? "", - }, - ] - : []; - } - - return []; -}; diff --git a/apps/web/lib/team/[slug]/getServerSideProps.tsx b/apps/web/lib/team/[slug]/getServerSideProps.tsx deleted file mode 100644 index c2a6b5cb32..0000000000 --- a/apps/web/lib/team/[slug]/getServerSideProps.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { IS_CALCOM } from "@calcom/lib/constants"; -import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl"; -import logger from "@calcom/lib/logger"; -import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; -import slugify from "@calcom/lib/slugify"; -import { stripMarkdown } from "@calcom/lib/stripMarkdown"; -import prisma from "@calcom/prisma"; -import type { OrganizationSettings, Team } from "@calcom/prisma/client"; -import { RedirectType } from "@calcom/prisma/enums"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; -import { handleOrgRedirect } from "@lib/handleOrgRedirect"; -import type { GetServerSidePropsContext } from "next"; - -const log = logger.getSubLogger({ prefix: ["team/[slug]"] }); - -function getOrgProfileRedirectToVerifiedDomain( - team: { - isOrganization: boolean; - }, - settings: Pick -) { - if (!team.isOrganization) { - return null; - } - // when this is not on a Cal.diy page we don't auto redirect - - // good for diagnosis purposes. - if (!IS_CALCOM) { - return null; - } - - const verifiedDomain = null; - - if (!settings.orgProfileRedirectsToVerifiedDomain || !verifiedDomain) { - return null; - } - - return { - redirect: { - permanent: false, - destination: `https://${verifiedDomain}`, - }, - }; -} - -const getTheLastArrayElement = (value: ReadonlyArray | string | undefined): string | undefined => { - if (value === undefined || typeof value === "string") { - return value; - } - - return value.at(-1); -}; - -export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const slug = getTheLastArrayElement(context.query.slug) ?? getTheLastArrayElement(context.query.orgSlug); - - const currentOrgDomain = null; - const isValidOrgDomain = false; - - // Provided by Rewrite from next.config.js - const isOrgProfile = context.query?.isOrgProfile === "1"; - const organizationsEnabled = false; - - log.debug("getServerSideProps", { - isOrgProfile, - isOrganizationFeatureEnabled: organizationsEnabled, - isValidOrgDomain, - currentOrgDomain, - }); - - const team = null as { metadata: unknown; parent: { slug: string | null } | null; isOrganization: boolean } | null; - - if (slug) { - const redirect = await handleOrgRedirect({ - slugs: [slug], - redirectType: RedirectType.Team, - eventTypeSlug: null, - context, - currentOrgDomain: isValidOrgDomain ? currentOrgDomain : null, - }); - - if (redirect) { - return redirect; - } - } - - const metadata = teamMetadataSchema.parse(team?.metadata ?? {}); - - // Taking care of sub-teams and orgs - if ( - (!isValidOrgDomain && team?.parent) || - (!isValidOrgDomain && !!team?.isOrganization) || - !organizationsEnabled - ) { - return { notFound: true } as const; - } - - if (!team) { - const unpublishedTeam = await prisma.team.findFirst({ - where: { - metadata: { - path: ["requestedSlug"], - equals: slug, - }, - }, - include: { - parent: { - select: { - id: true, - slug: true, - name: true, - isPrivate: true, - isOrganization: true, - metadata: true, - logoUrl: true, - }, - }, - }, - }); - - if (!unpublishedTeam) return { notFound: true } as const; - const teamParent = unpublishedTeam.parent ? getTeamWithoutMetadata(unpublishedTeam.parent) : null; - return { - props: { - considerUnpublished: true, - team: { - ...unpublishedTeam, - parent: teamParent, - createdAt: null, - }, - }, - } as const; - } - - return { notFound: true } as const; -}; - -/** - * Removes metadata from team and just adds requestedSlug - */ -function getTeamWithoutMetadata>(team: T) { - const { metadata, ...rest } = team; - const teamMetadata = teamMetadataSchema.parse(metadata); - return { - ...rest, - ...(typeof teamMetadata?.requestedSlug !== "undefined" - ? { requestedSlug: teamMetadata?.requestedSlug } - : {}), - }; -}