Skip to content

Commit bb98143

Browse files
authored
perf: Server Fetching for platform members and org members pages (calcom#21342)
* remove members page * refactor to server fetch * same for /members * use server fetches for all queires * fix * add skeletons * refactor * fix loading page * fix * fix type checks * fix details * refactor platform members page
1 parent 6258ca1 commit bb98143

14 files changed

Lines changed: 231 additions & 130 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { getTranslate } from "app/_utils";
2+
3+
import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils";
4+
import Shell from "@calcom/features/shell/Shell";
5+
6+
export default async function Layout({ children }: { children: React.ReactNode }) {
7+
const t = await getTranslate();
8+
9+
return (
10+
<Shell
11+
withoutMain={false}
12+
heading={t("organization_members")}
13+
subtitle={t("organization_description")}
14+
headerClassName="hidden md:block"
15+
actions={<div className={`mb-2 ${CTA_CONTAINER_CLASS_NAME}`} />}>
16+
{children}
17+
</Shell>
18+
);
19+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { UserListTableSkeleton } from "@calcom/features/users/components/UserTable/UserListTableSkeleton";
2+
3+
export default function Loading() {
4+
return <UserListTableSkeleton />;
5+
}
Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import { createRouterCaller } from "app/_trpc/context";
12
import { _generateMetadata } from "app/_utils";
23

3-
import MembersPage from "~/members/members-view";
4+
import { attributesRouter } from "@calcom/trpc/server/routers/viewer/attributes/_router";
5+
import { viewerOrganizationsRouter } from "@calcom/trpc/server/routers/viewer/organizations/_router";
6+
7+
import { MembersView } from "~/members/members-view";
48

59
export const generateMetadata = async ({ params }: { params: Promise<{ id: string }> }) =>
610
await _generateMetadata(
@@ -11,8 +15,20 @@ export const generateMetadata = async ({ params }: { params: Promise<{ id: strin
1115
`/settings/organizations/${(await params).id}/members`
1216
);
1317

14-
const ServerPageWrapper = () => {
15-
return <MembersPage />;
18+
const ServerPageWrapper = async () => {
19+
const [orgCaller, attributesCaller] = await Promise.all([
20+
createRouterCaller(viewerOrganizationsRouter),
21+
createRouterCaller(attributesRouter),
22+
]);
23+
const [org, teams, facetedTeamValues, attributes] = await Promise.all([
24+
orgCaller.listCurrent(),
25+
orgCaller.getTeams(),
26+
orgCaller.getFacetedValues(),
27+
attributesCaller.list(),
28+
]);
29+
return (
30+
<MembersView org={org} teams={teams} facetedTeamValues={facetedTeamValues} attributes={attributes} />
31+
);
1632
};
1733

1834
export default ServerPageWrapper;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import SettingsLayoutAppDirClient from "app/(use-page-wrapper)/settings/(settings-layout)/SettingsLayoutAppDirClient";
2+
import { getTranslate } from "app/_utils";
3+
4+
import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils";
5+
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
6+
7+
export default async function Layout({ children }: { children: React.ReactNode }) {
8+
const t = await getTranslate();
9+
10+
return (
11+
<SettingsLayoutAppDirClient containerClassName="lg:max-w-screen-2xl">
12+
<SettingsHeader
13+
title={t("organization_members")}
14+
description={t("organization_description")}
15+
ctaClassName={CTA_CONTAINER_CLASS_NAME}>
16+
{children}
17+
</SettingsHeader>
18+
</SettingsLayoutAppDirClient>
19+
);
20+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { UserListTableSkeleton } from "@calcom/features/users/components/UserTable/UserListTableSkeleton";
2+
3+
export default function Loading() {
4+
return <UserListTableSkeleton />;
5+
}
Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import SettingsLayoutAppDir from "app/(use-page-wrapper)/settings/(settings-layout)/layout";
2-
import { _generateMetadata, getTranslate } from "app/_utils";
1+
import { createRouterCaller } from "app/_trpc/context";
2+
import { _generateMetadata } from "app/_utils";
33

4-
import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils";
5-
import LegacyPage from "@calcom/features/ee/organizations/pages/members";
6-
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
4+
import { attributesRouter } from "@calcom/trpc/server/routers/viewer/attributes/_router";
5+
import { viewerOrganizationsRouter } from "@calcom/trpc/server/routers/viewer/organizations/_router";
6+
7+
import { MembersView } from "~/members/members-view";
78

89
export const generateMetadata = async () =>
910
await _generateMetadata(
@@ -15,17 +16,20 @@ export const generateMetadata = async () =>
1516
);
1617

1718
const Page = async () => {
18-
const t = await getTranslate();
19+
const [orgCaller, attributesCaller] = await Promise.all([
20+
createRouterCaller(viewerOrganizationsRouter),
21+
createRouterCaller(attributesRouter),
22+
]);
23+
const [org, teams, facetedTeamValues, attributes] = await Promise.all([
24+
orgCaller.listCurrent(),
25+
orgCaller.getTeams(),
26+
orgCaller.getFacetedValues(),
27+
attributesCaller.list(),
28+
]);
1929

20-
const children = (
21-
<SettingsHeader
22-
title={t("organization_members")}
23-
description={t("organization_description")}
24-
ctaClassName={CTA_CONTAINER_CLASS_NAME}>
25-
<LegacyPage />
26-
</SettingsHeader>
30+
return (
31+
<MembersView org={org} teams={teams} facetedTeamValues={facetedTeamValues} attributes={attributes} />
2732
);
28-
return await SettingsLayoutAppDir({ children, containerClassName: "lg:max-w-screen-2xl" });
2933
};
3034

3135
export default Page;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { getTranslate } from "app/_utils";
2+
3+
import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils";
4+
import Shell from "@calcom/features/shell/Shell";
5+
import { Button } from "@calcom/ui/components/button";
6+
7+
export default async function Layout({ children }: { children: React.ReactNode }) {
8+
const t = await getTranslate();
9+
10+
return (
11+
<Shell
12+
heading={
13+
<div className="flex">
14+
<h1>Member management</h1>
15+
<Button
16+
tooltip="Only teammates invited as admins can create OAuth clients while teammates invited as members have read only access"
17+
tooltipSide="right"
18+
className="mx-2 hover:bg-transparent"
19+
color="minimal"
20+
variant="icon"
21+
StartIcon="info"
22+
/>
23+
</div>
24+
}
25+
title={t("platform_members")}
26+
subtitle={t("platform_members_description")}
27+
withoutMain={false}
28+
isPlatformUser={true}
29+
actions={<div className={CTA_CONTAINER_CLASS_NAME} />}>
30+
{children}
31+
</Shell>
32+
);
33+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { UserListTableSkeleton } from "@calcom/features/users/components/UserTable/UserListTableSkeleton";
2+
3+
export default function Loading() {
4+
return <UserListTableSkeleton />;
5+
}

apps/web/app/(use-page-wrapper)/settings/platform/members/page.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import { createRouterCaller } from "app/_trpc/context";
12
import { _generateMetadata } from "app/_utils";
23

34
import PlatformMembersView from "@calcom/features/ee/platform/pages/settings/members";
5+
import { attributesRouter } from "@calcom/trpc/server/routers/viewer/attributes/_router";
6+
import { viewerOrganizationsRouter } from "@calcom/trpc/server/routers/viewer/organizations/_router";
47

58
export const generateMetadata = async () =>
69
await _generateMetadata(
@@ -11,8 +14,26 @@ export const generateMetadata = async () =>
1114
"/settings/platform/members"
1215
);
1316

14-
const ServerPageWrapper = () => {
15-
return <PlatformMembersView />;
17+
const ServerPageWrapper = async () => {
18+
const [orgCaller, attributesCaller] = await Promise.all([
19+
createRouterCaller(viewerOrganizationsRouter),
20+
createRouterCaller(attributesRouter),
21+
]);
22+
const [org, teams, facetedTeamValues, attributes] = await Promise.all([
23+
orgCaller.listCurrent(),
24+
orgCaller.getTeams(),
25+
orgCaller.getFacetedValues(),
26+
attributesCaller.list(),
27+
]);
28+
29+
return (
30+
<PlatformMembersView
31+
org={org}
32+
teams={teams}
33+
facetedTeamValues={facetedTeamValues}
34+
attributes={attributes}
35+
/>
36+
);
1637
};
1738

1839
export default ServerPageWrapper;

apps/web/modules/members/members-view.tsx

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
"use client";
22

3-
import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils";
4-
import MembersView from "@calcom/features/ee/organizations/pages/members";
5-
import Shell from "@calcom/features/shell/Shell";
3+
import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
4+
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
5+
import { UserListTable } from "@calcom/features/users/components/UserTable/UserListTable";
6+
import type { UserListTableProps } from "@calcom/features/users/components/UserTable/UserListTable";
67
import { useLocale } from "@calcom/lib/hooks/useLocale";
78

8-
const MembersPage: React.FC = () => {
9+
export const MembersView = (props: UserListTableProps) => {
910
const { t } = useLocale();
11+
const isOrgAdminOrOwner = props.org && checkAdminOrOwner(props.org.user.role);
12+
const canLoggedInUserSeeMembers =
13+
(props.org?.isPrivate && isOrgAdminOrOwner) || isOrgAdminOrOwner || !props.org?.isPrivate;
14+
1015
return (
11-
<Shell
12-
withoutMain={false}
13-
title={t("organization_members")}
14-
description={t("organization_description")}
15-
heading={t("organization_members")}
16-
subtitle={t("organization_description")}
17-
headerClassName="hidden md:block"
18-
actions={<div className={`mb-2 ${CTA_CONTAINER_CLASS_NAME}`} />}>
19-
<MembersView />
20-
</Shell>
16+
<LicenseRequired>
17+
<div>{canLoggedInUserSeeMembers && <UserListTable {...props} />}</div>
18+
{!canLoggedInUserSeeMembers && (
19+
<div className="border-subtle rounded-xl border p-6" data-testid="members-privacy-warning">
20+
<h2 className="text-default">{t("only_admin_can_see_members_of_org")}</h2>
21+
</div>
22+
)}
23+
</LicenseRequired>
2124
);
2225
};
23-
24-
export default MembersPage;

0 commit comments

Comments
 (0)