chore: settings/developer - remove pages router and use app router (#16794)

* remove pages router and use app router

* remove Meta / improve code quality

* remove isAppDir

* refactor code for better code

* fix test

* remove meta

* improve tests
This commit is contained in:
Benny Joo
2024-12-04 11:00:37 -05:00
committed by GitHub
parent 3211c5bcff
commit 7e2b5b12b4
19 changed files with 60 additions and 277 deletions
-1
View File
@@ -347,7 +347,6 @@ E2E_TEST_OIDC_USER_PASSWORD=
AB_TEST_BUCKET_PROBABILITY=50
# whether we redirect to the future/event-types from event-types or not
APP_ROUTER_EVENT_TYPES_ENABLED=0
APP_ROUTER_SETTINGS_DEVELOPER_ENABLED=0
APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED=0
APP_ROUTER_APPS_SLUG_ENABLED=0
APP_ROUTER_APPS_SLUG_SETUP_ENABLED=0
-1
View File
@@ -7,7 +7,6 @@ import { FUTURE_ROUTES_ENABLED_COOKIE_NAME, FUTURE_ROUTES_OVERRIDE_COOKIE_NAME }
const ROUTES: [URLPattern, boolean][] = [
["/event-types", process.env.APP_ROUTER_EVENT_TYPES_ENABLED === "1"] as const,
["/settings/developer/:path*", process.env.APP_ROUTER_SETTINGS_DEVELOPER_ENABLED === "1"] as const,
["/apps/installed/:category", process.env.APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED === "1"] as const,
["/apps/:slug", process.env.APP_ROUTER_APPS_SLUG_ENABLED === "1"] as const,
["/apps/:slug/setup", process.env.APP_ROUTER_APPS_SLUG_SETUP_ENABLED === "1"] as const,
@@ -4,8 +4,7 @@ import { getServerSessionForAppDir } from "@calcom/feature-auth/lib/get-server-s
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { APP_NAME } from "@calcom/lib/constants";
import ApiKeysView from "~/settings/developer/api-keys-view";
import NewApiKeyButton from "~/settings/developer/components/CreateApiKeyButton";
import ApiKeysView, { NewApiKeyButton } from "~/settings/developer/api-keys-view";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -25,7 +24,7 @@ const Page = async () => {
description={t("create_first_api_key_description", { appName: APP_NAME })}
CTA={<NewApiKeyButton />}
borderInShellHeader={true}>
<ApiKeysView isAppDir={true} />
<ApiKeysView />
</SettingsHeader>
);
};
@@ -20,7 +20,7 @@ const Page = async ({ params }: PageProps) => {
const id = typeof params?.id === "string" ? params.id : undefined;
const webhook = await WebhookRepository.findByWebhookId(id);
console.log("This is the webhook", webhook);
return (
<SettingsHeader
title={t("edit_webhook")}
@@ -1,6 +1,6 @@
import { _generateMetadata } from "app/_utils";
import { WebhooksViewAppDir } from "@calcom/features/webhooks/pages/webhooks-view";
import WebhooksView from "@calcom/features/webhooks/pages/webhooks-view";
import { APP_NAME } from "@calcom/lib/constants";
export const generateMetadata = async () =>
@@ -9,8 +9,4 @@ export const generateMetadata = async () =>
(t) => t("add_webhook_description", { appName: APP_NAME })
);
const Page = () => {
return <WebhooksViewAppDir />;
};
export default Page;
export default WebhooksView;
@@ -1,6 +1,6 @@
"use client";
import { useState } from "react";
import { useEffect, useState } from "react";
import type { TApiKeys } from "@calcom/ee/api-keys/components/ApiKeyListItem";
import LicenseRequired from "@calcom/ee/common/components/LicenseRequired";
@@ -19,18 +19,9 @@ import {
SkeletonText,
} from "@calcom/ui";
const SkeletonLoader = ({
title,
description,
isAppDir,
}: {
title: string;
description: string;
isAppDir?: boolean;
}) => {
const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
return (
<SkeletonContainer>
{!isAppDir ? <Meta title={title} description={description} borderInShellHeader={true} /> : null}
<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" />
@@ -39,7 +30,29 @@ const SkeletonLoader = ({
);
};
const ApiKeysView = ({ isAppDir }: { isAppDir?: boolean }) => {
export const apiKeyModalRef = {
current: null as null | ((show: boolean) => void),
};
export const apiKeyToEditRef = {
current: null as null | ((apiKey: (TApiKeys & { neverExpires?: boolean }) | undefined) => void),
};
export const NewApiKeyButton = () => {
const { t } = useLocale();
return (
<Button
color="secondary"
StartIcon="plus"
onClick={() => {
apiKeyModalRef.current?.(true);
apiKeyToEditRef.current?.(undefined);
}}>
{t("add")}
</Button>
);
};
const ApiKeysView = () => {
const { t } = useLocale();
const { data, isPending } = trpc.viewer.apiKeys.list.useQuery();
@@ -49,24 +62,18 @@ const ApiKeysView = ({ isAppDir }: { isAppDir?: boolean }) => {
undefined
);
const NewApiKeyButton = () => {
return (
<Button
color="secondary"
StartIcon="plus"
onClick={() => {
setApiKeyToEdit(undefined);
setApiKeyModal(true);
}}>
{t("add")}
</Button>
);
};
useEffect(() => {
apiKeyModalRef.current = setApiKeyModal;
apiKeyToEditRef.current = setApiKeyToEdit;
return () => {
apiKeyModalRef.current = null;
apiKeyToEditRef.current = null;
};
}, []);
if (isPending || !data) {
return (
<SkeletonLoader
isAppDir={isAppDir}
title={t("api_keys")}
description={t("create_first_api_key_description", { appName: APP_NAME })}
/>
@@ -75,14 +82,6 @@ const ApiKeysView = ({ isAppDir }: { isAppDir?: boolean }) => {
return (
<>
{!isAppDir ? (
<Meta
title={t("api_keys")}
description={t("create_first_api_key_description", { appName: APP_NAME })}
CTA={<NewApiKeyButton />}
borderInShellHeader={true}
/>
) : null}
<LicenseRequired>
<div>
{data?.length ? (
@@ -1,38 +0,0 @@
"use client";
import { useState } from "react";
import type { TApiKeys } from "@calcom/ee/api-keys/components/ApiKeyListItem";
import ApiKeyDialogForm from "@calcom/features/ee/api-keys/components/ApiKeyDialogForm";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button, Dialog, DialogContent } from "@calcom/ui";
const NewApiKeyButton = () => {
const { t } = useLocale();
const [apiKeyModal, setApiKeyModal] = useState(false);
const [apiKeyToEdit, setApiKeyToEdit] = useState<(TApiKeys & { neverExpires?: boolean }) | undefined>(
undefined
);
return (
<>
<Button
color="secondary"
StartIcon="plus"
onClick={() => {
setApiKeyToEdit(undefined);
setApiKeyModal(true);
}}>
{t("add")}
</Button>
<Dialog open={apiKeyModal} onOpenChange={setApiKeyModal}>
<DialogContent type="creation">
<ApiKeyDialogForm handleClose={() => setApiKeyModal(false)} defaultValues={apiKeyToEdit} />
</DialogContent>
</Dialog>
</>
);
};
export default NewApiKeyButton;
@@ -1,14 +0,0 @@
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import PageWrapper from "@components/PageWrapper";
import ApiKeysView from "~/settings/developer/api-keys-view";
const Page = () => {
return <ApiKeysView />;
};
Page.getLayout = getLayout;
Page.PageWrapper = PageWrapper;
export default Page;
@@ -1,43 +0,0 @@
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import EditWebhookView from "@calcom/features/webhooks/pages/webhook-edit-view";
import { APP_NAME } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { SkeletonContainer, Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
const Page = () => {
const searchParams = useCompatSearchParams();
const id = searchParams?.get("id") ?? undefined;
const { t } = useLocale();
const { data: webhook } = trpc.viewer.webhook.get.useQuery(
{ webhookId: id },
{
suspense: true,
enabled: !!id,
}
);
if (!id) return <SkeletonContainer />;
// I think we should do SSR for this page
return (
<>
<Meta
title={t("edit_webhook")}
description={t("add_webhook_description", { appName: APP_NAME })}
borderInShellHeader={true}
backButton
/>
<EditWebhookView webhook={webhook} />
</>
);
};
Page.getLayout = getLayout;
Page.PageWrapper = PageWrapper;
export default Page;
@@ -1,38 +0,0 @@
import { useSession } from "next-auth/react";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import { CreateNewWebhookButton } from "@calcom/features/webhooks/components";
import WebhooksView from "@calcom/features/webhooks/pages/webhooks-view";
import { APP_NAME } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { UserPermissionRole } from "@calcom/prisma/enums";
import { trpc } from "@calcom/trpc/react";
import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
const Page = () => {
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",
});
return (
<>
<Meta
title={t("webhooks")}
description={t("add_webhook_description", { appName: APP_NAME })}
CTA={data && data.webhookGroups.length > 0 ? <CreateNewWebhookButton isAdmin={isAdmin} /> : <></>}
borderInShellHeader={(data && data.profiles.length === 1) || !data?.webhookGroups?.length}
/>
<WebhooksView />
</>
);
};
Page.getLayout = getLayout;
Page.PageWrapper = PageWrapper;
export default Page;
@@ -1,27 +0,0 @@
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import WebhookNewView from "@calcom/features/webhooks/pages/webhook-new-view";
import { APP_NAME } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
const Page = () => {
const { t } = useLocale();
return (
<>
<Meta
title={t("add_webhook")}
description={t("add_webhook_description", { appName: APP_NAME })}
backButton
borderInShellHeader={true}
/>
<WebhookNewView />
</>
);
};
Page.PageWrapper = PageWrapper;
Page.getLayout = getLayout;
export default Page;
+2 -1
View File
@@ -12,9 +12,9 @@ export function createWebhookPageFixture(page: Page) {
await page.waitForURL((u) => u.pathname === "/settings/developer/webhooks/new");
const url = page.url();
const teamId = Number(new URL(url).searchParams.get("teamId")) as number;
await page.click('[data-testid="new_webhook"]');
await page.fill('[name="subscriberUrl"]', webhookReceiver.url);
await page.fill('[name="secret"]', "secret");
await expect(page.locator('[data-testid="create_webhook"]')).toBeEnabled();
await Promise.all([
page.click("[type=submit]"),
page.waitForURL((url) => url.pathname.endsWith("/settings/developer/webhooks")),
@@ -28,6 +28,7 @@ export function createWebhookPageFixture(page: Page) {
await page.click('[data-testid="new_webhook"]');
await page.fill('[name="subscriberUrl"]', webhookReceiver.url);
await page.fill('[name="secret"]', "secret");
await expect(page.locator('[data-testid="create_webhook"]')).toBeEnabled();
await Promise.all([
page.click("[type=submit]"),
page.waitForURL((url) => url.pathname.endsWith("/settings/developer/webhooks")),
+7 -3
View File
@@ -490,7 +490,6 @@ test.describe("MEETING_ENDED, MEETING_STARTED", async () => {
}, _testInfo) => {
const user = await users.create();
await user.apiLogin();
const tomorrow = dayjs().add(1, "day");
const [eventType] = user.eventTypes;
bookings.create(user.id, user.name, eventType.id);
bookings.create(user.id, user.name, eventType.id, { startTime: dayjs().add(2, "day").toDate() });
@@ -659,7 +658,12 @@ test.describe("FORM_SUBMITTED", async () => {
webhookReceiver.close();
});
test("on submitting team form, triggers team webhook", async ({ page, users, routingForms, webhooks }) => {
test("on submitting team form, triggers team webhook @test", async ({
page,
users,
routingForms,
webhooks,
}) => {
const user = await users.create(null, {
hasTeam: true,
});
@@ -814,7 +818,7 @@ test.describe("OOO_CREATED", async () => {
hasTeam: true,
});
await user.apiLogin();
const { webhookReceiver, teamId } = await webhooks.createTeamReceiver();
const { webhookReceiver } = await webhooks.createTeamReceiver();
await page.goto("/settings/my-account/out-of-office");
@@ -8,7 +8,6 @@ checkRoute () {
# This is to reduce the size of the build and prevent OOM errors
checkRoute "$APP_ROUTER_EVENT_TYPES_ENABLED" app/future/event-types
checkRoute "$APP_ROUTER_AVAILABILITY_ENABLED" app/future/availability
checkRoute "$APP_ROUTER_SETTINGS_DEVELOPER_ENABLED" app/future/settings/developer
checkRoute "$APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED" app/future/apps/installed
checkRoute "$APP_ROUTER_APPS_SLUG_ENABLED" app/future/apps/\[slug\]
checkRoute "$APP_ROUTER_APPS_SLUG_SETUP_ENABLED" app/future/apps/\[slug\]/setup
@@ -346,6 +346,7 @@ const WebhookForm = (props: {
</Button>
<Button
type="submit"
data-testid="create_webhook"
disabled={!formMethods.formState.isDirty && !changeSecret}
loading={formMethods.formState.isSubmitting}>
{props?.webhook?.id ? t("save") : t("create_webhook")}
@@ -3,20 +3,18 @@
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { APP_NAME } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { Meta, showToast, SkeletonContainer, SkeletonText } from "@calcom/ui";
import { showToast, SkeletonContainer, SkeletonText } from "@calcom/ui";
import type { WebhookFormSubmitData } from "../components/WebhookForm";
import WebhookForm from "../components/WebhookForm";
import { subscriberUrlReserved } from "../lib/subscriberUrlReserved";
const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
const SkeletonLoader = () => {
return (
<SkeletonContainer>
<Meta title={title} description={description} borderInShellHeader={true} />
<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" />
@@ -89,13 +87,7 @@ export const NewWebhookView = () => {
});
};
if (isPending)
return (
<SkeletonLoader
title={t("add_webhook")}
description={t("add_webhook_description", { appName: APP_NAME })}
/>
);
if (isPending) return <SkeletonLoader />;
return (
<WebhookForm
@@ -15,18 +15,9 @@ import { Avatar, EmptyScreen, Meta, SkeletonContainer, SkeletonText } from "@cal
import { WebhookListItem, CreateNewWebhookButton } from "../components";
const SkeletonLoader = ({
title,
description,
borderInShellHeader,
}: {
title: string;
description: string;
borderInShellHeader: boolean;
}) => {
const SkeletonLoader = () => {
return (
<SkeletonContainer>
<Meta title={title} description={description} borderInShellHeader={borderInShellHeader} />
<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" />
@@ -35,7 +26,7 @@ const SkeletonLoader = ({
);
};
const WebhooksView = () => {
export const WebhooksView = () => {
const { t } = useLocale();
const session = useSession();
const isAdmin = session.data?.user.role === UserPermissionRole.ADMIN;
@@ -45,55 +36,19 @@ const WebhooksView = () => {
});
if (isPending || !data) {
return (
<SkeletonLoader
title={t("webhooks")}
description={t("add_webhook_description", { appName: APP_NAME })}
borderInShellHeader={true}
/>
);
return <SkeletonLoader />;
}
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>
</>
);
};
export const WebhooksViewAppDir = () => {
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
title={t("webhooks")}
description={t("add_webhook_description", { appName: APP_NAME })}
borderInShellHeader={true}
/>
);
}
return (
<>
<SettingsHeader
title={t("webhooks")}
description={t("add_webhook_description", { appName: APP_NAME })}
CTA={data && data.webhookGroups.length > 0 ? <CreateNewWebhookButton isAdmin={isAdmin} /> : <></>}
borderInShellHeader={(data && data.profiles.length === 1) || !data?.webhookGroups?.length}>
<div>
<WebhooksList webhooksByViewer={data} isAdmin={isAdmin} />
</div>
</SettingsHeader>
</>
</SettingsHeader>
);
};
-1
View File
@@ -242,7 +242,6 @@
"APP_ROUTER_BOOKINGS_STATUS_ENABLED",
"APP_ROUTER_EVENT_TYPES_ENABLED",
"APP_ROUTER_GETTING_STARTED_STEP_ENABLED",
"APP_ROUTER_SETTINGS_DEVELOPER_ENABLED",
"APP_ROUTER_SETTINGS_TEAMS_ENABLED",
"APP_ROUTER_WORKFLOWS_ENABLED",
"APP_ROUTER_VIDEO_ENABLED",