perf: fetch data server side for /settings/webhooks (#20583)
* update client components * update server components * update middleware * fix * fix --------- Co-authored-by: Keith Williams <keithwillcode@gmail.com>
This commit is contained in:
co-authored by
Keith Williams
parent
e9b5aa6f22
commit
134086f229
+7
@@ -0,0 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
|
||||
export async function revalidateWebhooksList() {
|
||||
revalidatePath("/settings/developer/webhooks");
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||
import { SkeletonLoader } from "@calcom/features/webhooks/pages/webhooks-view";
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<SettingsHeader
|
||||
title="Webhooks"
|
||||
description="Receive meeting data in real-time when something happens in Cal.com"
|
||||
borderInShellHeader={true}
|
||||
CTA={null}>
|
||||
<SkeletonLoader />
|
||||
</SettingsHeader>
|
||||
);
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { createRouterCaller } from "app/_trpc/context";
|
||||
import { _generateMetadata, getTranslate } from "app/_utils";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||
import { CreateNewWebhookButton } from "@calcom/features/webhooks/components";
|
||||
import WebhooksView from "@calcom/features/webhooks/pages/webhooks-view";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import { UserPermissionRole } from "@calcom/prisma/enums";
|
||||
import { webhookRouter } from "@calcom/trpc/server/routers/viewer/webhook/_router";
|
||||
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("webhooks"),
|
||||
(t) => t("add_webhook_description", { appName: APP_NAME })
|
||||
);
|
||||
|
||||
const WebhooksViewServerWrapper = async () => {
|
||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||
if (!session?.user?.id) {
|
||||
redirect("/auth/login");
|
||||
}
|
||||
|
||||
const t = await getTranslate();
|
||||
const isAdmin = session.user.role === UserPermissionRole.ADMIN;
|
||||
const caller = await createRouterCaller(webhookRouter);
|
||||
const data = await caller.getByViewer();
|
||||
|
||||
return (
|
||||
<SettingsHeader
|
||||
title={t("webhooks")}
|
||||
description={t("add_webhook_description", { appName: APP_NAME })}
|
||||
CTA={data && data.webhookGroups.length > 0 ? <CreateNewWebhookButton isAdmin={isAdmin} /> : null}
|
||||
borderInShellHeader={(data && data.profiles.length === 1) || !data?.webhookGroups?.length}>
|
||||
<WebhooksView data={data} isAdmin={isAdmin} />
|
||||
</SettingsHeader>
|
||||
);
|
||||
};
|
||||
|
||||
export default WebhooksViewServerWrapper;
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
|
||||
import WebhooksView from "@calcom/features/webhooks/pages/webhooks-view";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("webhooks"),
|
||||
(t) => t("add_webhook_description", { appName: APP_NAME })
|
||||
);
|
||||
|
||||
const WebhooksViewServerWrapper = async () => {
|
||||
return <WebhooksView />;
|
||||
};
|
||||
|
||||
export default WebhooksViewServerWrapper;
|
||||
@@ -18,7 +18,10 @@ const safeGet = async <T = any>(key: string): Promise<T | undefined> => {
|
||||
|
||||
export const POST_METHODS_ALLOWED_API_ROUTES = ["/api/"]; // trailing slash in "/api/" is actually important to block edge cases like `/api.php`
|
||||
// Some app routes are allowed because "revalidatePath()" is used to revalidate the cache for them
|
||||
export const POST_METHODS_ALLOWED_APP_ROUTES = ["/settings/my-account/general"];
|
||||
export const POST_METHODS_ALLOWED_APP_ROUTES = [
|
||||
"/settings/my-account/general",
|
||||
"/settings/developer/webhooks",
|
||||
];
|
||||
|
||||
export function checkPostMethod(req: NextRequest) {
|
||||
const pathname = req.nextUrl.pathname;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
|
||||
@@ -7,10 +9,10 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { TimeUnit } from "@calcom/prisma/enums";
|
||||
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { Select } from "@calcom/ui/components/form";
|
||||
import { TextArea } from "@calcom/ui/components/form";
|
||||
import { ToggleGroup } from "@calcom/ui/components/form";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { Form } from "@calcom/ui/components/form";
|
||||
import { Label } from "@calcom/ui/components/form";
|
||||
import { TextField } from "@calcom/ui/components/form";
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
@@ -15,6 +17,7 @@ import {
|
||||
import { Switch } from "@calcom/ui/components/form";
|
||||
import { showToast } from "@calcom/ui/components/toast";
|
||||
import { Tooltip } from "@calcom/ui/components/tooltip";
|
||||
import { revalidateWebhooksList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions";
|
||||
|
||||
type WebhookProps = {
|
||||
id: string;
|
||||
@@ -45,6 +48,7 @@ export default function WebhookListItem(props: {
|
||||
await utils.viewer.webhook.getByViewer.invalidate();
|
||||
await utils.viewer.webhook.list.invalidate();
|
||||
await utils.viewer.eventTypes.get.invalidate();
|
||||
revalidateWebhooksList();
|
||||
},
|
||||
});
|
||||
const toggleWebhook = trpc.viewer.webhook.edit.useMutation({
|
||||
@@ -54,6 +58,7 @@ export default function WebhookListItem(props: {
|
||||
await utils.viewer.webhook.getByViewer.invalidate();
|
||||
await utils.viewer.webhook.list.invalidate();
|
||||
await utils.viewer.eventTypes.get.invalidate();
|
||||
revalidateWebhooksList();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { useWatch } from "react-hook-form";
|
||||
import { ZodError } from "zod";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { ZTestTriggerInputSchema } from "@calcom/trpc/server/routers/viewer/webhook/testTrigger.schema";
|
||||
import { showToast } from "@calcom/ui/components/toast";
|
||||
import { Badge } from "@calcom/ui/components/badge";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { showToast } from "@calcom/ui/components/toast";
|
||||
|
||||
export default function WebhookTestDisclosure() {
|
||||
const [subscriberUrl, webhookSecret]: [string, string] = useWatch({ name: ["subscriberUrl", "secret"] });
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { SkeletonContainer } from "@calcom/ui/components/skeleton";
|
||||
import { showToast } from "@calcom/ui/components/toast";
|
||||
import { revalidateWebhooksList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions";
|
||||
|
||||
import type { WebhookFormSubmitData } from "../components/WebhookForm";
|
||||
import WebhookForm from "../components/WebhookForm";
|
||||
@@ -45,7 +46,8 @@ export function EditWebhookView({ webhook }: { webhook?: WebhookProps }) {
|
||||
await utils.viewer.webhook.list.invalidate();
|
||||
await utils.viewer.webhook.get.invalidate({ webhookId: webhook?.id });
|
||||
showToast(t("webhook_updated_successfully"), "success");
|
||||
router.back();
|
||||
revalidateWebhooksList();
|
||||
router.push("/settings/developer/webhooks");
|
||||
},
|
||||
onError(error) {
|
||||
showToast(`${error.message}`, "error");
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { SkeletonText, SkeletonContainer } from "@calcom/ui/components/skeleton";
|
||||
import { showToast } from "@calcom/ui/components/toast";
|
||||
import { revalidateWebhooksList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions";
|
||||
|
||||
import type { WebhookFormSubmitData } from "../components/WebhookForm";
|
||||
import WebhookForm from "../components/WebhookForm";
|
||||
@@ -50,7 +51,8 @@ export const NewWebhookView = () => {
|
||||
async onSuccess() {
|
||||
showToast(t("webhook_created_successfully"), "success");
|
||||
await utils.viewer.webhook.list.invalidate();
|
||||
router.back();
|
||||
revalidateWebhooksList();
|
||||
router.push("/settings/developer/webhooks");
|
||||
},
|
||||
onError(error) {
|
||||
showToast(`${error.message}`, "error");
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||
import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { UserPermissionRole } from "@calcom/prisma/enums";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import type { WebhooksByViewer } from "@calcom/trpc/server/routers/viewer/webhook/getByViewer.handler";
|
||||
import { Avatar } from "@calcom/ui/components/avatar";
|
||||
import classNames from "@calcom/ui/classNames";
|
||||
import { Avatar } from "@calcom/ui/components/avatar";
|
||||
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
|
||||
import { SkeletonText, SkeletonContainer } from "@calcom/ui/components/skeleton";
|
||||
|
||||
import { WebhookListItem, CreateNewWebhookButton } from "../components";
|
||||
|
||||
const SkeletonLoader = () => {
|
||||
export const SkeletonLoader = () => {
|
||||
return (
|
||||
<SkeletonContainer>
|
||||
<div className="divide-subtle border-subtle space-y-6 rounded-b-lg border border-t-0 px-6 py-4">
|
||||
@@ -28,29 +25,16 @@ const SkeletonLoader = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const WebhooksView = () => {
|
||||
const { t } = useLocale();
|
||||
const session = useSession();
|
||||
const isAdmin = session.data?.user.role === UserPermissionRole.ADMIN;
|
||||
|
||||
const { data, isPending } = trpc.viewer.webhook.getByViewer.useQuery(undefined, {
|
||||
enabled: session.status === "authenticated",
|
||||
});
|
||||
|
||||
if (isPending || !data) {
|
||||
return <SkeletonLoader />;
|
||||
}
|
||||
type Props = {
|
||||
data: RouterOutputs["viewer"]["webhook"]["getByViewer"];
|
||||
isAdmin: boolean;
|
||||
};
|
||||
|
||||
const WebhooksView = ({ data, isAdmin }: Props) => {
|
||||
return (
|
||||
<SettingsHeader
|
||||
title={t("webhooks")}
|
||||
description={t("add_webhook_description", { appName: APP_NAME })}
|
||||
CTA={data && data.webhookGroups.length > 0 ? <CreateNewWebhookButton isAdmin={isAdmin} /> : null}
|
||||
borderInShellHeader={(data && data.profiles.length === 1) || !data?.webhookGroups?.length}>
|
||||
<div>
|
||||
<WebhooksList webhooksByViewer={data} isAdmin={isAdmin} />
|
||||
</div>
|
||||
</SettingsHeader>
|
||||
<div>
|
||||
<WebhooksList webhooksByViewer={data} isAdmin={isAdmin} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -70,59 +54,54 @@ const WebhooksList = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{webhookGroups && (
|
||||
<>
|
||||
{!!webhookGroups.length && (
|
||||
<div className={classNames("mt-0", hasTeams && "mt-6")}>
|
||||
{webhookGroups.map((group) => (
|
||||
<div key={group.teamId}>
|
||||
{hasTeams && (
|
||||
<div className="items-centers flex">
|
||||
<Avatar
|
||||
alt={group.profile.image || ""}
|
||||
imageSrc={group.profile.image || `${bookerUrl}/${group.profile.name}/avatar.png`}
|
||||
size="md"
|
||||
className="inline-flex justify-center"
|
||||
/>
|
||||
<div className="text-emphasis ml-2 flex flex-grow items-center font-bold">
|
||||
{group.profile.name || ""}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col" key={group.profile.slug}>
|
||||
<div
|
||||
className={classNames(
|
||||
"border-subtle rounded-lg rounded-t-none border border-t-0",
|
||||
hasTeams && "mb-8 mt-3 rounded-t-lg border-t"
|
||||
)}>
|
||||
{group.webhooks.map((webhook, index) => (
|
||||
<WebhookListItem
|
||||
key={webhook.id}
|
||||
webhook={webhook}
|
||||
readOnly={group.metadata?.readOnly ?? false}
|
||||
lastItem={group.webhooks.length === index + 1}
|
||||
onEditWebhook={() =>
|
||||
router.push(`${WEBAPP_URL}/settings/developer/webhooks/${webhook.id}`)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{!!webhookGroups.length ? (
|
||||
<div className={classNames("mt-0", hasTeams && "mt-6")}>
|
||||
{webhookGroups.map((group) => (
|
||||
<div key={group.teamId}>
|
||||
{hasTeams && (
|
||||
<div className="items-centers flex">
|
||||
<Avatar
|
||||
alt={group.profile.image || ""}
|
||||
imageSrc={group.profile.image || `${bookerUrl}/${group.profile.name}/avatar.png`}
|
||||
size="md"
|
||||
className="inline-flex justify-center"
|
||||
/>
|
||||
<div className="text-emphasis ml-2 flex flex-grow items-center font-bold">
|
||||
{group.profile.name || ""}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
<div className="flex flex-col" key={group.profile.slug}>
|
||||
<div
|
||||
className={classNames(
|
||||
"border-subtle rounded-lg rounded-t-none border border-t-0",
|
||||
hasTeams && "mb-8 mt-3 rounded-t-lg border-t"
|
||||
)}>
|
||||
{group.webhooks.map((webhook, index) => (
|
||||
<WebhookListItem
|
||||
key={webhook.id}
|
||||
webhook={webhook}
|
||||
readOnly={group.metadata?.readOnly ?? false}
|
||||
lastItem={group.webhooks.length === index + 1}
|
||||
onEditWebhook={() =>
|
||||
router.push(`${WEBAPP_URL}/settings/developer/webhooks/${webhook.id}`)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!webhookGroups.length && (
|
||||
<EmptyScreen
|
||||
Icon="link"
|
||||
headline={t("create_your_first_webhook")}
|
||||
description={t("create_your_first_webhook_description", { appName: APP_NAME })}
|
||||
className="rounded-b-lg rounded-t-none border-t-0"
|
||||
buttonRaw={<CreateNewWebhookButton isAdmin={isAdmin} />}
|
||||
border={true}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<EmptyScreen
|
||||
Icon="link"
|
||||
headline={t("create_your_first_webhook")}
|
||||
description={t("create_your_first_webhook_description", { appName: APP_NAME })}
|
||||
className="rounded-b-lg rounded-t-none border-t-0"
|
||||
buttonRaw={<CreateNewWebhookButton isAdmin={isAdmin} />}
|
||||
border={true}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user