perf: Fetch data server-side for Api Keys page (#20677)

* server-side fetching for api keys page

* refactor billing page

* fix
This commit is contained in:
Benny Joo
2025-04-22 18:20:41 +00:00
committed by GitHub
parent e82cc328d3
commit c00e8cfa54
8 changed files with 67 additions and 40 deletions
@@ -0,0 +1,7 @@
"use server";
import { revalidatePath } from "next/cache";
export async function revalidateApiKeysList() {
revalidatePath("/settings/developer/api-keys");
}
@@ -0,0 +1,5 @@
import { SkeletonLoader } from "~/settings/developer/api-keys-skeleton";
export default function Loading() {
return <SkeletonLoader />;
}
@@ -1,9 +1,10 @@
import { getTranslate, _generateMetadata } from "app/_utils";
import { createRouterCaller } from "app/_trpc/context";
import { _generateMetadata } from "app/_utils";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { APP_NAME } from "@calcom/lib/constants";
import { apiKeysRouter } from "@calcom/trpc/server/routers/viewer/apiKeys/_router";
import ApiKeysView, { NewApiKeyButton } from "~/settings/developer/api-keys-view";
import ApiKeysView from "~/settings/developer/api-keys-view";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -15,17 +16,9 @@ export const generateMetadata = async () =>
);
const Page = async () => {
const t = await getTranslate();
return (
<SettingsHeader
title={t("api_keys")}
description={t("create_first_api_key_description", { appName: APP_NAME })}
CTA={<NewApiKeyButton />}
borderInShellHeader={true}>
<ApiKeysView />
</SettingsHeader>
);
const caller = await createRouterCaller(apiKeysRouter);
const apiKeys = await caller.list();
return <ApiKeysView apiKeys={apiKeys} />;
};
export default Page;
+2
View File
@@ -20,6 +20,7 @@ export const POST_METHODS_ALLOWED_API_ROUTES = ["/api/auth/signup", "/api/trpc/"
export const POST_METHODS_ALLOWED_APP_ROUTES = [
"/settings/my-account/general",
"/settings/developer/webhooks",
"/settings/developer/api-keys",
];
export function checkPostMethod(req: NextRequest) {
const pathname = req.nextUrl.pathname;
@@ -192,6 +193,7 @@ export const config = {
// Routes allowed for POST method (matching `POST_METHODS_ALLOWED_APP_ROUTES` array)
"/settings/my-account/general",
"/settings/developer/webhooks",
"/settings/developer/api-keys",
],
};
@@ -0,0 +1,24 @@
"use client";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { APP_NAME } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { SkeletonText, SkeletonContainer } from "@calcom/ui/components/skeleton";
export const SkeletonLoader = () => {
const { t } = useLocale();
return (
<SettingsHeader
title={t("api_keys")}
description={t("create_first_api_key_description", { appName: APP_NAME })}
borderInShellHeader={true}>
<SkeletonContainer>
<div className="divide-subtle border-subtle space-y-6 rounded-b-lg border border-t-0 px-6 py-4">
<SkeletonText className="h-8 w-full" />
<SkeletonText className="h-8 w-full" />
</div>
</SkeletonContainer>
</SettingsHeader>
);
};
@@ -7,24 +7,13 @@ import LicenseRequired from "@calcom/ee/common/components/LicenseRequired";
import { Dialog } from "@calcom/features/components/controlled-dialog";
import ApiKeyDialogForm from "@calcom/features/ee/api-keys/components/ApiKeyDialogForm";
import ApiKeyListItem from "@calcom/features/ee/api-keys/components/ApiKeyListItem";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { APP_NAME } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { RouterOutputs } from "@calcom/trpc/react";
import { Button } from "@calcom/ui/components/button";
import { DialogContent } from "@calcom/ui/components/dialog";
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
import { SkeletonText, SkeletonContainer } from "@calcom/ui/components/skeleton";
const SkeletonLoader = () => {
return (
<SkeletonContainer>
<div className="divide-subtle border-subtle space-y-6 rounded-b-lg border border-t-0 px-6 py-4">
<SkeletonText className="h-8 w-full" />
<SkeletonText className="h-8 w-full" />
</div>
</SkeletonContainer>
);
};
export const apiKeyModalRef = {
current: null as null | ((show: boolean) => void),
@@ -48,10 +37,12 @@ export const NewApiKeyButton = () => {
);
};
const ApiKeysView = () => {
const { t } = useLocale();
type Props = {
apiKeys: RouterOutputs["viewer"]["apiKeys"]["list"];
};
const { data, isPending } = trpc.viewer.apiKeys.list.useQuery();
const ApiKeysView = ({ apiKeys: data }: Props) => {
const { t } = useLocale();
const [apiKeyModal, setApiKeyModal] = useState(false);
const [apiKeyToEdit, setApiKeyToEdit] = useState<(TApiKeys & { neverExpires?: boolean }) | undefined>(
@@ -67,12 +58,12 @@ const ApiKeysView = () => {
};
}, []);
if (isPending || !data) {
return <SkeletonLoader />;
}
return (
<>
<SettingsHeader
title={t("api_keys")}
description={t("create_first_api_key_description", { appName: APP_NAME })}
CTA={<NewApiKeyButton />}
borderInShellHeader={true}>
<LicenseRequired>
<div>
{data?.length ? (
@@ -108,7 +99,7 @@ const ApiKeysView = () => {
<ApiKeyDialogForm handleClose={() => setApiKeyModal(false)} defaultValues={apiKeyToEdit} />
</DialogContent>
</Dialog>
</>
</SettingsHeader>
);
};
@@ -6,14 +6,15 @@ import type { TApiKeys } from "@calcom/ee/api-keys/components/ApiKeyListItem";
import LicenseRequired from "@calcom/ee/common/components/LicenseRequired";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { DialogFooter } from "@calcom/ui/components/dialog";
import { Button } from "@calcom/ui/components/button";
import { DialogFooter } from "@calcom/ui/components/dialog";
import { Form } from "@calcom/ui/components/form";
import { TextField } from "@calcom/ui/components/form";
import { SelectField } from "@calcom/ui/components/form";
import { Switch } from "@calcom/ui/components/form";
import { showToast } from "@calcom/ui/components/toast";
import { Tooltip } from "@calcom/ui/components/tooltip";
import { revalidateApiKeysList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/api-keys/actions";
export default function ApiKeyDialogForm({
defaultValues,
@@ -28,6 +29,7 @@ export default function ApiKeyDialogForm({
const updateApiKeyMutation = trpc.viewer.apiKeys.edit.useMutation({
onSuccess() {
utils.viewer.apiKeys.list.invalidate();
revalidateApiKeysList();
showToast(t("api_key_updated"), "success");
handleClose();
},
@@ -131,6 +133,7 @@ export default function ApiKeyDialogForm({
setApiKey(apiKey);
setApiKeyDetails({ ...event });
await utils.viewer.apiKeys.list.invalidate();
revalidateApiKeysList();
setSuccessfulNewApiKeyModal(true);
}
}}
@@ -2,6 +2,9 @@ import dayjs from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import classNames from "@calcom/ui/classNames";
import { Badge } from "@calcom/ui/components/badge";
import { Button } from "@calcom/ui/components/button";
import {
Dropdown,
DropdownItem,
@@ -9,10 +12,8 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@calcom/ui/components/dropdown";
import { Badge } from "@calcom/ui/components/badge";
import { Button } from "@calcom/ui/components/button";
import classNames from "@calcom/ui/classNames";
import { showToast } from "@calcom/ui/components/toast";
import { revalidateApiKeysList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/api-keys/actions";
export type TApiKeys = RouterOutputs["viewer"]["apiKeys"]["list"][number];
@@ -34,6 +35,7 @@ const ApiKeyListItem = ({
const deleteApiKey = trpc.viewer.apiKeys.delete.useMutation({
async onSuccess() {
await utils.viewer.apiKeys.list.invalidate();
revalidateApiKeysList();
showToast(t("api_key_deleted"), "success");
},
onError(err) {