chore: reduce team data footprint (#11478)
parent
86b3a33ff6
commit
578495de87
|
@ -8,6 +8,7 @@ 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 { getFeatureFlagMap } from "@calcom/features/flags/server/utils";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
||||
|
@ -65,9 +66,9 @@ function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }
|
|||
// slug is a route parameter, we don't want to forward it to the next route
|
||||
const { slug: _slug, orgSlug: _orgSlug, user: _user, ...queryParamsToForward } = routerQuery;
|
||||
|
||||
const EventTypes = () => (
|
||||
const EventTypes = ({ eventTypes }: { eventTypes: NonNullable<(typeof team)["eventTypes"]> }) => (
|
||||
<ul className="border-subtle rounded-md border">
|
||||
{team.eventTypes.map((type, index) => (
|
||||
{eventTypes.map((type, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={classNames(
|
||||
|
@ -160,8 +161,14 @@ function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }
|
|||
<div className="space-y-6" data-testid="event-types">
|
||||
<div className="overflow-hidden rounded-sm border dark:border-gray-900">
|
||||
<div className="text-muted p-8 text-center">
|
||||
<h2 className="font-cal text-emphasis mb-2 text-3xl">{" " + t("org_no_teams_yet")}</h2>
|
||||
<p className="text-emphasis mx-auto max-w-md">{t("org_no_teams_yet_description")}</p>
|
||||
<h2 className="font-cal text-emphasis mb-2 text-3xl">
|
||||
{" " + t("org_no_teams_yet", { defaultValue: "This organization has no teams yet" })}
|
||||
</h2>
|
||||
<p className="text-emphasis mx-auto max-w-md">
|
||||
{t("org_no_teams_yet_description", {
|
||||
defaultValue: "If you are an administrator, be sure to create teams to be shown here.",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -174,7 +181,10 @@ function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }
|
|||
description={teamName}
|
||||
meeting={{
|
||||
title: markdownStrippedBio,
|
||||
profile: { name: `${team.name}`, image: getPlaceholderAvatar(team.logo, team.name) },
|
||||
profile: {
|
||||
name: `${team.name}`,
|
||||
image: `${WEBAPP_URL}/${team.metadata?.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<main className="dark:bg-darkgray-50 bg-subtle mx-auto max-w-3xl rounded-md px-4 pb-12 pt-12">
|
||||
|
@ -182,7 +192,9 @@ function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }
|
|||
<div className="relative">
|
||||
<Avatar
|
||||
alt={teamName}
|
||||
imageSrc={getPlaceholderAvatar(team.parent ? team.parent.logo : team.logo, team.name)}
|
||||
imageSrc={`${WEBAPP_URL}/${team.metadata?.isOrganization ? "org" : "team"}/${
|
||||
team.slug
|
||||
}/avatar.png`}
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
|
@ -203,7 +215,7 @@ function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }
|
|||
<SubTeams />
|
||||
) : (
|
||||
<>
|
||||
{(showMembers.isOn || !team.eventTypes.length) &&
|
||||
{(showMembers.isOn || !team.eventTypes?.length) &&
|
||||
(team.isPrivate ? (
|
||||
<div className="w-full text-center">
|
||||
<h2 className="text-emphasis font-semibold">{t("you_cannot_see_team_members")}</h2>
|
||||
|
@ -211,9 +223,9 @@ function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }
|
|||
) : (
|
||||
<Team team={team} />
|
||||
))}
|
||||
{!showMembers.isOn && team.eventTypes.length > 0 && (
|
||||
{!showMembers.isOn && team.eventTypes && team.eventTypes.length > 0 && (
|
||||
<div className="mx-auto max-w-3xl ">
|
||||
<EventTypes />
|
||||
<EventTypes eventTypes={team.eventTypes} />
|
||||
|
||||
{/* Hide "Book a team member button when team is private or hideBookATeamMember is true" */}
|
||||
{!team.hideBookATeamMember && !team.isPrivate && (
|
||||
|
@ -263,7 +275,12 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
context.params?.orgSlug
|
||||
);
|
||||
const flags = await getFeatureFlagMap(prisma);
|
||||
const team = await getTeamWithMembers({ slug, orgSlug: currentOrgDomain });
|
||||
const team = await getTeamWithMembers({
|
||||
slug,
|
||||
orgSlug: currentOrgDomain,
|
||||
isTeamView: true,
|
||||
isOrgView: isValidOrgDomain && context.resolvedUrl === "/",
|
||||
});
|
||||
const ssr = await ssrInit(context, {
|
||||
noI18nPreload: isValidOrgDomain && !team?.parent,
|
||||
});
|
||||
|
@ -308,14 +325,15 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
} as const;
|
||||
}
|
||||
|
||||
team.eventTypes = team.eventTypes.map((type) => ({
|
||||
...type,
|
||||
users: type.users.map((user) => ({
|
||||
...user,
|
||||
avatar: "/" + user.username + "/avatar.png",
|
||||
})),
|
||||
descriptionAsSafeHTML: markdownToSafeHTML(type.description),
|
||||
}));
|
||||
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) || "";
|
||||
|
||||
|
|
|
@ -10,10 +10,15 @@ import type { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
|||
*/
|
||||
export type OrganizationBranding =
|
||||
| ({
|
||||
/** 1 */
|
||||
id: number;
|
||||
/** Acme */
|
||||
name?: string;
|
||||
/** acme */
|
||||
slug: string;
|
||||
/** https://acme.cal.com */
|
||||
fullDomain: string;
|
||||
/** cal.com */
|
||||
domainSuffix: string;
|
||||
} & z.infer<typeof teamMetadataSchema>)
|
||||
| null
|
||||
|
|
|
@ -121,7 +121,7 @@ export default function MemberListItem(props: Props) {
|
|||
const bookerUrlWithoutProtocol = bookerUrl.replace(/^https?:\/\//, "");
|
||||
const bookingLink = !!props.member.username && `${bookerUrlWithoutProtocol}/${props.member.username}`;
|
||||
const isAdmin = props.team && ["ADMIN", "OWNER"].includes(props.team.membership?.role);
|
||||
const appList = props.member.connectedApps.map(({ logo, name, externalId }) => {
|
||||
const appList = props.member.connectedApps?.map(({ logo, name, externalId }) => {
|
||||
return logo ? (
|
||||
externalId ? (
|
||||
<div className="ltr:mr-2 rtl:ml-2 ">
|
||||
|
|
|
@ -11,7 +11,6 @@ interface Props {
|
|||
id?: number;
|
||||
name?: string | null;
|
||||
slug?: string | null;
|
||||
logo?: string | null;
|
||||
bio?: string | null;
|
||||
hideBranding?: boolean | undefined;
|
||||
role: MembershipRole;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from "@calcom/lib/classNames";
|
||||
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { MembershipRole } from "@calcom/prisma/enums";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
@ -20,7 +20,6 @@ interface Props {
|
|||
id?: number;
|
||||
name?: string | null;
|
||||
slug?: string | null;
|
||||
logo?: string | null;
|
||||
bio?: string | null;
|
||||
hideBranding?: boolean | undefined;
|
||||
role: MembershipRole;
|
||||
|
@ -66,7 +65,7 @@ export default function TeamInviteListItem(props: Props) {
|
|||
<div className="flex">
|
||||
<Avatar
|
||||
size="mdLg"
|
||||
imageSrc={getPlaceholderAvatar(team?.logo, team?.name as string)}
|
||||
imageSrc={`${WEBAPP_URL}/team/${team.slug}/avatar.png`}
|
||||
alt="Team Logo"
|
||||
className=""
|
||||
/>
|
||||
|
|
|
@ -149,7 +149,6 @@ const MembersView = () => {
|
|||
{
|
||||
id: team.id,
|
||||
accepted: team.membership.accepted || false,
|
||||
logo: team.logo,
|
||||
name: team.name,
|
||||
slug: team.slug,
|
||||
role: team.membership.role,
|
||||
|
|
|
@ -94,7 +94,6 @@ const ProfileView = () => {
|
|||
if (team) {
|
||||
form.setValue("name", team.name || "");
|
||||
form.setValue("slug", team.slug || "");
|
||||
form.setValue("logo", team.logo || "");
|
||||
form.setValue("bio", team.bio || "");
|
||||
if (team.slug === null && (team?.metadata as Prisma.JsonObject)?.requestedSlug) {
|
||||
form.setValue("slug", ((team?.metadata as Prisma.JsonObject)?.requestedSlug as string) || "");
|
||||
|
@ -165,7 +164,6 @@ const ProfileView = () => {
|
|||
handleSubmit={(values) => {
|
||||
if (team) {
|
||||
const variables = {
|
||||
logo: values.logo,
|
||||
name: values.name,
|
||||
slug: values.slug,
|
||||
bio: values.bio,
|
||||
|
|
|
@ -6,6 +6,7 @@ export const CALCOM_ENV = process.env.CALCOM_ENV || process.env.NODE_ENV;
|
|||
export const IS_PRODUCTION = CALCOM_ENV === "production";
|
||||
export const IS_PRODUCTION_BUILD = process.env.NODE_ENV === "production";
|
||||
|
||||
/** https://app.cal.com */
|
||||
export const WEBAPP_URL =
|
||||
process.env.NEXT_PUBLIC_WEBAPP_URL ||
|
||||
VERCEL_URL ||
|
||||
|
|
|
@ -15,19 +15,16 @@ export async function getTeamWithMembers(args: {
|
|||
slug?: string;
|
||||
userId?: number;
|
||||
orgSlug?: string | null;
|
||||
isTeamView?: boolean;
|
||||
isOrgView?: boolean;
|
||||
}) {
|
||||
const { id, slug, userId, orgSlug } = args;
|
||||
const { id, slug, userId, orgSlug, isTeamView, isOrgView } = args;
|
||||
const userSelect = Prisma.validator<Prisma.UserSelect>()({
|
||||
username: true,
|
||||
email: true,
|
||||
name: true,
|
||||
id: true,
|
||||
bio: true,
|
||||
destinationCalendar: {
|
||||
select: {
|
||||
externalId: true,
|
||||
},
|
||||
},
|
||||
teams: {
|
||||
select: {
|
||||
team: {
|
||||
|
@ -37,7 +34,6 @@ export async function getTeamWithMembers(args: {
|
|||
},
|
||||
},
|
||||
},
|
||||
selectedCalendars: true,
|
||||
credentials: {
|
||||
select: {
|
||||
app: {
|
||||
|
@ -58,7 +54,6 @@ export async function getTeamWithMembers(args: {
|
|||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
logo: true,
|
||||
bio: true,
|
||||
hideBranding: true,
|
||||
hideBookATeamMember: true,
|
||||
|
@ -136,8 +131,9 @@ export async function getTeamWithMembers(args: {
|
|||
// This should improve performance saving already app data found.
|
||||
const appDataMap = new Map();
|
||||
const members = team.members.map((obj) => {
|
||||
const { credentials, ...restUser } = obj.user;
|
||||
return {
|
||||
...obj.user,
|
||||
...restUser,
|
||||
role: obj.role,
|
||||
accepted: obj.accepted,
|
||||
disableImpersonation: obj.disableImpersonation,
|
||||
|
@ -145,24 +141,26 @@ export async function getTeamWithMembers(args: {
|
|||
? obj.user.teams.filter((obj) => obj.team.slug !== orgSlug).map((obj) => obj.team.slug)
|
||||
: null,
|
||||
avatar: `${WEBAPP_URL}/${obj.user.username}/avatar.png`,
|
||||
connectedApps: obj?.user?.credentials?.map((cred) => {
|
||||
const appSlug = cred.app?.slug;
|
||||
let appData = appDataMap.get(appSlug);
|
||||
connectedApps: !isTeamView
|
||||
? credentials?.map((cred) => {
|
||||
const appSlug = cred.app?.slug;
|
||||
let appData = appDataMap.get(appSlug);
|
||||
|
||||
if (!appData) {
|
||||
appData = getAppFromSlug(appSlug);
|
||||
appDataMap.set(appSlug, appData);
|
||||
}
|
||||
if (!appData) {
|
||||
appData = getAppFromSlug(appSlug);
|
||||
appDataMap.set(appSlug, appData);
|
||||
}
|
||||
|
||||
const isCalendar = cred?.app?.categories?.includes("calendar") ?? false;
|
||||
const externalId = isCalendar ? cred.destinationCalendars?.[0]?.externalId : null;
|
||||
return {
|
||||
name: appData?.name ?? null,
|
||||
logo: appData?.logo ?? null,
|
||||
app: cred.app,
|
||||
externalId: externalId ?? null,
|
||||
};
|
||||
}),
|
||||
const isCalendar = cred?.app?.categories?.includes("calendar") ?? false;
|
||||
const externalId = isCalendar ? cred.destinationCalendars?.[0]?.externalId : null;
|
||||
return {
|
||||
name: appData?.name ?? null,
|
||||
logo: appData?.logo ?? null,
|
||||
app: cred.app,
|
||||
externalId: externalId ?? null,
|
||||
};
|
||||
})
|
||||
: null,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -182,7 +180,7 @@ export async function getTeamWithMembers(args: {
|
|||
token.expires > new Date(new Date().setHours(24))
|
||||
),
|
||||
metadata: teamMetadataSchema.parse(team.metadata),
|
||||
eventTypes,
|
||||
eventTypes: !isOrgView ? eventTypes : null,
|
||||
members,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -22,8 +22,13 @@ export function UnpublishedEntity(props: UnpublishedEntityProps) {
|
|||
}
|
||||
headline={t("team_is_unpublished", {
|
||||
team: props.name,
|
||||
defaultValue: `${props.name} is unpublished`,
|
||||
})}
|
||||
description={t(`${props.orgSlug ? "org" : "team"}_is_unpublished_description`, {
|
||||
defaultValue: `This ${
|
||||
props.orgSlug ? "organization" : "team"
|
||||
} link is currently not available. Please contact the organization owner or ask them to publish it.`,
|
||||
})}
|
||||
description={t(`${props.orgSlug ? "org" : "team"}_is_unpublished_description`)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue