chore: App router migration - settings/platform/**/*, settings/organizations/new and settings/organizations/[id]/* (#16334)

* remove WizardLayoutAppDir

* refactor organizations/new and add platform/new

* refactor organizations/[id]/*

* add APP_ROUTER_SETTINGS_ORG_ENABLED

* fix type error

* fix organizations/new page

* remove code not needed and fix type errors

* Fix Metadata utils

* remove unnecessary code

* remove unneeded code

* wip

* do not remove buildWrappedOnboardTeamMembersPage

* fix type error

* do not introduce changes to yarn.lock

* fix wrong route name

* fix onboard members

* fix platform pages

* fix settings/platform main view

* add requiresLicense to WithLayout

* refactor

* add requiresLicense to platform/new

* Add LicenseRequired back to platform/new

* add env variable

* fix onboard-members

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
This commit is contained in:
Benny Joo
2024-09-09 17:19:44 -07:00
committed by GitHub
co-authored by Keith Williams
parent 5dff1c6df0
commit 9ed1624111
36 changed files with 738 additions and 570 deletions
+2
View File
@@ -334,6 +334,8 @@ 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_ADMIN_ENABLED=0
APP_ROUTER_SETTINGS_PLATFORM_ENABLED=0
APP_ROUTER_SETTINGS_ORG_ENABLED=0
APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED=0
APP_ROUTER_APPS_SLUG_ENABLED=0
APP_ROUTER_APPS_SLUG_SETUP_ENABLED=0
+2
View File
@@ -8,6 +8,8 @@ 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/admin/:path*", process.env.APP_ROUTER_SETTINGS_ADMIN_ENABLED === "1"] as const,
["/settings/platform/:path*", process.env.APP_ROUTER_SETTINGS_PLATFORM_ENABLED === "1"] as const,
["/settings/organizations/:path*", process.env.APP_ROUTER_SETTINGS_ORG_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,
@@ -1,18 +0,0 @@
import LegacyPage, {
buildWrappedOnboardTeamMembersPage,
} from "@pages/settings/organizations/[id]/onboard-members";
import type { PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
export const generateMetadata = async () =>
await _generateMetadata(
(t) => t("invite_organization_admins"),
(t) => t("invite_organization_admins_description")
);
const Page = ({ params }: PageProps) => {
return buildWrappedOnboardTeamMembersPage(params.id, <LegacyPage />);
};
export default WithLayout({ Page });
@@ -1,11 +1,15 @@
import LegacyPage, { LayoutWrapper } from "@pages/settings/organizations/[id]/about";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import LegacyPage, { LayoutWrapper } from "~/settings/organizations/[id]/about-view";
export const generateMetadata = async () =>
await _generateMetadata(
(t) => t("about_your_organization"),
(t) => t("about_your_organization_description")
);
export default WithLayout({ Page: LegacyPage, getLayout: LayoutWrapper });
export default WithLayout({
Page: LegacyPage,
getLayout: LayoutWrapper,
});
@@ -1,11 +1,15 @@
import LegacyPage, { LayoutWrapper } from "@pages/settings/organizations/[id]/add-teams";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import LegacyPage, { LayoutWrapper } from "~/settings/organizations/[id]/add-teams-view";
export const generateMetadata = async () =>
await _generateMetadata(
(t) => t("create_your_teams"),
(t) => t("create_your_teams_description")
);
export default WithLayout({ Page: LegacyPage, getLayout: LayoutWrapper });
export default WithLayout({
Page: LegacyPage,
getLayout: LayoutWrapper,
});
@@ -0,0 +1,15 @@
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import LegacyPage, { LayoutWrapper } from "~/settings/organizations/[id]/onboard-members-view";
export const generateMetadata = async () =>
await _generateMetadata(
(t) => t("invite_organization_admins"),
(t) => t("invite_organization_admins_description")
);
export default WithLayout({
Page: LegacyPage,
getLayout: LayoutWrapper,
});
@@ -1,10 +1,12 @@
import LegacyPage, { getServerSideProps, LayoutWrapper } from "@pages/settings/organizations/new/index";
import { withAppDirSsr } from "app/WithAppDirSsr";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { getServerSideProps } from "@lib/settings/organizations/new/getServerSideProps";
import { type inferSSRProps } from "@lib/types/inferSSRProps";
import LegacyPage, { LayoutWrapper } from "~/settings/organizations/new/create-new-view";
export const generateMetadata = async () =>
await _generateMetadata(
(t) => t("set_up_your_organization"),
@@ -12,6 +14,7 @@ export const generateMetadata = async () =>
);
export default WithLayout({
requiresLicense: true,
getLayout: LayoutWrapper,
Page: LegacyPage,
getData: withAppDirSsr<inferSSRProps<typeof getServerSideProps>>(getServerSideProps),
@@ -0,0 +1,21 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { getServerSideProps } from "@lib/settings/organizations/new/getServerSideProps";
import { type inferSSRProps } from "@lib/types/inferSSRProps";
import LegacyPage, { LayoutWrapper } from "~/settings/platform/new/create-new-view";
export const generateMetadata = async () =>
await _generateMetadata(
(t) => t("set_up_your_platform_organization"),
(t) => t("platform_organization_description")
);
export default WithLayout({
getLayout: LayoutWrapper,
Page: LegacyPage,
getData: withAppDirSsr<inferSSRProps<typeof getServerSideProps>>(getServerSideProps),
requiresLicense: true,
});
@@ -0,0 +1,15 @@
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import EditView from "~/settings/platform/oauth-clients/[clientId]/edit/edit-view";
export const generateMetadata = async () =>
await _generateMetadata(
() => "OAuth client updation form",
() => ""
);
export default WithLayout({
getLayout: null,
Page: EditView,
});
@@ -0,0 +1,15 @@
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import EditWebhooksView from "~/settings/platform/oauth-clients/[clientId]/edit/edit-webhooks-view";
export const generateMetadata = async () =>
await _generateMetadata(
() => "OAuth client updation form",
() => ""
);
export default WithLayout({
getLayout: null,
Page: EditWebhooksView,
});
@@ -0,0 +1,19 @@
import type { PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import CreateNewView from "~/settings/platform/oauth-clients/create-new-view";
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
const p = { ...params, ...searchParams };
const clientId = p?.clientId ?? "";
return await _generateMetadata(
() => `OAuth client ${!!clientId ? "updation" : "creation"} form`,
() => ""
);
};
export default WithLayout({
getLayout: null,
Page: CreateNewView,
});
@@ -0,0 +1,16 @@
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import PlatformView from "~/settings/platform/platform-view";
export const generateMetadata = async () => {
return await _generateMetadata(
() => "Platform",
() => "Manage everything related to platform."
);
};
export default WithLayout({
getLayout: null,
Page: PlatformView,
});
@@ -0,0 +1,14 @@
"use client";
import { AboutOrganizationForm } from "@calcom/features/ee/organizations/components";
import { WizardLayout } from "@calcom/ui";
export const LayoutWrapper = (page: React.ReactElement) => {
return (
<WizardLayout currentStep={3} maxSteps={5}>
{page}
</WizardLayout>
);
};
export default AboutOrganizationForm;
@@ -0,0 +1,14 @@
"use client";
import { AddNewTeamsForm } from "@calcom/features/ee/organizations/components";
import { WizardLayout } from "@calcom/ui";
export const LayoutWrapper = (page: React.ReactElement) => {
return (
<WizardLayout currentStep={5} maxSteps={5}>
{page}
</WizardLayout>
);
};
export default AddNewTeamsForm;
@@ -0,0 +1,28 @@
"use client";
import { useRouter } from "next/navigation";
import AddNewTeamMembers from "@calcom/features/ee/teams/components/AddNewTeamMembers";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { WizardLayout } from "@calcom/ui";
export const LayoutWrapper = (page: React.ReactElement) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const router = useRouter();
// eslint-disable-next-line react-hooks/rules-of-hooks
const query = useCompatSearchParams();
return (
<WizardLayout
currentStep={4}
maxSteps={5}
isOptionalCallback={() => {
router.push(`/settings/organizations/${query.get("id")}/add-teams`);
}}>
{page}
</WizardLayout>
);
};
const OrgAddNewTeamMembers = () => <AddNewTeamMembers isOrg={true} />;
export default OrgAddNewTeamMembers;
@@ -0,0 +1,14 @@
"use client";
import { CreateANewOrganizationForm } from "@calcom/features/ee/organizations/components";
import { WizardLayout } from "@calcom/ui";
export const LayoutWrapper = (page: React.ReactElement) => {
return (
<WizardLayout currentStep={1} maxSteps={5}>
{page}
</WizardLayout>
);
};
export default CreateANewOrganizationForm;
@@ -0,0 +1,14 @@
"use client";
import { CreateANewPlatformForm } from "@calcom/features/ee/platform/components/index";
import { WizardLayout } from "@calcom/ui";
export const LayoutWrapper = (page: React.ReactElement) => {
return (
<WizardLayout currentStep={1} maxSteps={1}>
{page}
</WizardLayout>
);
};
export default CreateANewPlatformForm;
@@ -0,0 +1,133 @@
"use client";
import { useParams } from "next/navigation";
import { useRouter } from "next/navigation";
import Shell from "@calcom/features/shell/Shell";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { PERMISSIONS_GROUPED_MAP } from "@calcom/platform-constants";
import { showToast } from "@calcom/ui";
import { useOAuthClient } from "@lib/hooks/settings/platform/oauth-clients/useOAuthClients";
import { useUpdateOAuthClient } from "@lib/hooks/settings/platform/oauth-clients/usePersistOAuthClient";
import NoPlatformPlan from "@components/settings/platform/dashboard/NoPlatformPlan";
import { useGetUserAttributes } from "@components/settings/platform/hooks/useGetUserAttributes";
import type { FormValues } from "@components/settings/platform/oauth-clients/oauth-client-form";
import { OAuthClientForm as EditOAuthClientForm } from "@components/settings/platform/oauth-clients/oauth-client-form";
import {
hasAppsReadPermission,
hasAppsWritePermission,
hasBookingReadPermission,
hasBookingWritePermission,
hasEventTypeReadPermission,
hasEventTypeWritePermission,
hasProfileReadPermission,
hasProfileWritePermission,
hasScheduleReadPermission,
hasScheduleWritePermission,
} from "../../../../../../../../packages/platform/utils/permissions";
export default function EditOAuthClient() {
const router = useRouter();
const params = useParams<{ clientId: string }>();
const clientId = params?.clientId || "";
const { isUserLoading, isPlatformUser, isPaidUser } = useGetUserAttributes();
const { data, isFetched, isFetching, isError, refetch } = useOAuthClient(clientId);
const { mutateAsync: update, isPending: isUpdating } = useUpdateOAuthClient({
onSuccess: () => {
showToast("OAuth client updated successfully", "success");
refetch();
router.push("/settings/platform/");
},
onError: () => {
showToast(ErrorCode.UpdatingOauthClientError, "error");
},
clientId,
});
const onSubmit = (data: FormValues) => {
let userPermissions = 0;
const userRedirectUris = data.redirectUris.map((uri) => uri.uri).filter((uri) => !!uri);
Object.keys(PERMISSIONS_GROUPED_MAP).forEach((key) => {
const entity = key as keyof typeof PERMISSIONS_GROUPED_MAP;
const entityKey = PERMISSIONS_GROUPED_MAP[entity].key;
const read = PERMISSIONS_GROUPED_MAP[entity].read;
const write = PERMISSIONS_GROUPED_MAP[entity].write;
if (data[`${entityKey}Read`]) userPermissions |= read;
if (data[`${entityKey}Write`]) userPermissions |= write;
});
update({
name: data.name,
// logo: data.logo,
redirectUris: userRedirectUris,
bookingRedirectUri: data.bookingRedirectUri,
bookingCancelRedirectUri: data.bookingCancelRedirectUri,
bookingRescheduleRedirectUri: data.bookingRescheduleRedirectUri,
areEmailsEnabled: data.areEmailsEnabled,
});
};
if (isUserLoading) return <div className="m-5">Loading...</div>;
if (isPlatformUser && isPaidUser) {
return (
<div>
<Shell title="OAuth client updation form" isPlatformUser={true}>
<div className="m-2 md:mx-14 md:mx-5">
<div className="border-subtle mx-auto block justify-between rounded-t-lg border px-4 py-6 sm:flex sm:px-6">
<div className="flex w-full flex-col">
<h1 className="font-cal text-emphasis mb-1 text-xl font-semibold leading-5 tracking-wide">
OAuth client updation form
</h1>
<p className="text-default text-sm ltr:mr-4 rtl:ml-4">
This is the form to edit an existing OAuth client
</p>
</div>
</div>
{(!Boolean(clientId) || (isFetched && !data)) && <p>OAuth Client not found.</p>}
{isFetched && !!data && (
<EditOAuthClientForm
defaultValues={{
name: data?.name ?? "",
areEmailsEnabled: data.areEmailsEnabled ?? false,
redirectUris: data?.redirectUris?.map((uri) => ({ uri })) ?? [{ uri: "" }],
bookingRedirectUri: data?.bookingRedirectUri ?? "",
bookingCancelRedirectUri: data?.bookingCancelRedirectUri ?? "",
bookingRescheduleRedirectUri: data?.bookingRescheduleRedirectUri ?? "",
appsRead: hasAppsReadPermission(data?.permissions),
appsWrite: hasAppsWritePermission(data?.permissions),
bookingRead: hasBookingReadPermission(data?.permissions),
bookingWrite: hasBookingWritePermission(data?.permissions),
eventTypeRead: hasEventTypeReadPermission(data?.permissions),
eventTypeWrite: hasEventTypeWritePermission(data?.permissions),
profileRead: hasProfileReadPermission(data?.permissions),
profileWrite: hasProfileWritePermission(data?.permissions),
scheduleRead: hasScheduleReadPermission(data?.permissions),
scheduleWrite: hasScheduleWritePermission(data?.permissions),
}}
onSubmit={onSubmit}
isPending={isUpdating}
/>
)}
{isFetching && <p>Loading...</p>}
{isError && <p>Something went wrong.</p>}
</div>
</Shell>
</div>
);
}
return (
<div>
<Shell isPlatformUser={true} hideHeadingOnMobile withoutMain={false} SidebarContainer={<></>}>
<NoPlatformPlan />
</Shell>
</div>
);
}
@@ -0,0 +1,122 @@
"use client";
import { useParams } from "next/navigation";
import { useRouter } from "next/navigation";
import Shell from "@calcom/features/shell/Shell";
import { WebhookForm } from "@calcom/features/webhooks/components";
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
import { showToast } from "@calcom/ui";
import {
useOAuthClientWebhooks,
useCreateOAuthClientWebhook,
useUpdateOAuthClientWebhook,
} from "@lib/hooks/settings/platform/oauth-clients/useOAuthClientWebhooks";
import NoPlatformPlan from "@components/settings/platform/dashboard/NoPlatformPlan";
import { useGetUserAttributes } from "@components/settings/platform/hooks/useGetUserAttributes";
export default function EditOAuthClientWebhooks() {
const router = useRouter();
const params = useParams<{ clientId: string }>();
const clientId = params?.clientId || "";
const { isUserLoading, isPlatformUser, isPaidUser } = useGetUserAttributes();
const {
data: webhooks,
status: webhooksStatus,
isFetched: isWebhooksFetched,
refetch: refetchWebhooks,
} = useOAuthClientWebhooks(clientId);
const webhookId = webhooks?.[0]?.id ?? "";
const webhook = webhooks?.[0];
const { mutateAsync: createWebhook } = useCreateOAuthClientWebhook(clientId);
const { mutateAsync: updateWebhook } = useUpdateOAuthClientWebhook(clientId);
if (isUserLoading) return <div className="m-5">Loading...</div>;
if (isPlatformUser && isPaidUser) {
return (
<div>
<Shell title="OAuth client updation form" isPlatformUser={true}>
<div className="m-2 md:mx-5">
<div className="border-subtle mx-auto block justify-between rounded-t-lg border px-4 py-6 sm:flex sm:px-6">
<div className="flex w-full flex-col">
<h1 className="font-cal text-emphasis mb-1 text-xl font-semibold leading-5 tracking-wide">
Webhook update form
</h1>
<p className="text-default text-sm ltr:mr-4 rtl:ml-4">Add a webhook to your OAuthClient.</p>
</div>
</div>
{webhooksStatus !== "success" && <p>Error while trying to access webhooks.</p>}
{isWebhooksFetched && webhooksStatus === "success" && (
<WebhookForm
overrideTriggerOptions={[
{ value: WebhookTriggerEvents.BOOKING_CANCELLED, label: "booking_cancelled" },
{ value: WebhookTriggerEvents.BOOKING_CREATED, label: "booking_created" },
{ value: WebhookTriggerEvents.BOOKING_REJECTED, label: "booking_rejected" },
{ value: WebhookTriggerEvents.BOOKING_REQUESTED, label: "booking_requested" },
{ value: WebhookTriggerEvents.BOOKING_RESCHEDULED, label: "booking_rescheduled" },
{ value: WebhookTriggerEvents.BOOKING_NO_SHOW_UPDATED, label: "booking_no_show_updated" },
{ value: WebhookTriggerEvents.MEETING_ENDED, label: "meeting_ended" },
{ value: WebhookTriggerEvents.MEETING_STARTED, label: "meeting_started" },
{ value: WebhookTriggerEvents.RECORDING_READY, label: "recording_ready" },
{
value: WebhookTriggerEvents.RECORDING_TRANSCRIPTION_GENERATED,
label: "recording_transcription_generated",
},
]}
onSubmit={async (data) => {
try {
const body = {
active: data.active,
payloadTemplate: data.payloadTemplate ?? undefined,
subscriberUrl: data.subscriberUrl,
triggers: data.eventTriggers,
secret: data.secret ?? undefined,
};
if (webhook) {
await updateWebhook({
webhookId,
body,
});
showToast("Webhook updated successfully.", "success");
} else {
await createWebhook(body);
showToast("Webhook created successfully.", "success");
}
await refetchWebhooks();
router.push("/settings/platform/");
} catch (err) {
showToast(`Failed to ${webhookId ? "update" : "create"} webhook.`, "error");
}
}}
onCancel={() => {
router.push("/settings/platform/");
}}
noRoutingFormTriggers={true}
webhook={
webhook
? { ...webhook, eventTriggers: webhook.triggers, secret: webhook.secret ?? null }
: undefined
}
/>
)}
</div>
</Shell>
</div>
);
}
return (
<div>
<Shell isPlatformUser={true} hideHeadingOnMobile withoutMain={false} SidebarContainer={<></>}>
<NoPlatformPlan />
</Shell>
</div>
);
}
@@ -0,0 +1,91 @@
"use client";
import { useRouter } from "next/navigation";
import Shell from "@calcom/features/shell/Shell";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { PERMISSIONS_GROUPED_MAP } from "@calcom/platform-constants/permissions";
import { showToast } from "@calcom/ui";
import { useCreateOAuthClient } from "@lib/hooks/settings/platform/oauth-clients/usePersistOAuthClient";
import NoPlatformPlan from "@components/settings/platform/dashboard/NoPlatformPlan";
import { useGetUserAttributes } from "@components/settings/platform/hooks/useGetUserAttributes";
import type { FormValues } from "@components/settings/platform/oauth-clients/oauth-client-form";
import { OAuthClientForm } from "@components/settings/platform/oauth-clients/oauth-client-form";
export default function CreateOAuthClient() {
const searchParams = useCompatSearchParams();
const router = useRouter();
const clientId = searchParams?.get("clientId") || "";
const { isUserLoading, isPlatformUser, isPaidUser } = useGetUserAttributes();
const { mutateAsync: save, isPending: isSaving } = useCreateOAuthClient({
onSuccess: () => {
showToast("OAuth client created successfully", "success");
router.push("/settings/platform/");
},
onError: () => {
showToast(ErrorCode.CreatingOauthClientError, "error");
},
});
const onSubmit = (data: FormValues) => {
let userPermissions = 0;
const userRedirectUris = data.redirectUris.map((uri) => uri.uri).filter((uri) => !!uri);
Object.keys(PERMISSIONS_GROUPED_MAP).forEach((key) => {
const entity = key as keyof typeof PERMISSIONS_GROUPED_MAP;
const entityKey = PERMISSIONS_GROUPED_MAP[entity].key;
const read = PERMISSIONS_GROUPED_MAP[entity].read;
const write = PERMISSIONS_GROUPED_MAP[entity].write;
if (data[`${entityKey}Read`]) userPermissions |= read;
if (data[`${entityKey}Write`]) userPermissions |= write;
});
save({
name: data.name,
permissions: userPermissions,
// logo: data.logo,
redirectUris: userRedirectUris,
bookingRedirectUri: data.bookingRedirectUri,
bookingCancelRedirectUri: data.bookingCancelRedirectUri,
bookingRescheduleRedirectUri: data.bookingRescheduleRedirectUri,
areEmailsEnabled: data.areEmailsEnabled,
});
};
if (isUserLoading) return <div className="m-5">Loading...</div>;
if (isPlatformUser && isPaidUser) {
return (
<div>
<Shell title={`OAuth client ${!!clientId ? "updation" : "creation"} form`} isPlatformUser={true}>
<div className="m-2 md:mx-5">
<div className="border-subtle mx-auto block justify-between rounded-t-lg border px-4 py-6 sm:flex sm:px-6">
<div className="flex w-full flex-col">
<h1 className="font-cal text-emphasis mb-1 text-xl font-semibold leading-5 tracking-wide">
OAuth client creation form
</h1>
<p className="text-default text-sm ltr:mr-4 rtl:ml-4">
This is the form to create a new OAuth client
</p>
</div>
</div>
<OAuthClientForm isPending={isSaving} onSubmit={onSubmit} />
</div>
</Shell>
</div>
);
}
return (
<div>
<Shell isPlatformUser={true} hideHeadingOnMobile withoutMain={false} SidebarContainer={<></>}>
<NoPlatformPlan />
</Shell>
</div>
);
}
@@ -0,0 +1,107 @@
"use client";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { useState, useEffect } from "react";
import Shell from "@calcom/features/shell/Shell";
import { showToast } from "@calcom/ui";
import {
useOAuthClients,
useGetOAuthClientManagedUsers,
} from "@lib/hooks/settings/platform/oauth-clients/useOAuthClients";
import { useDeleteOAuthClient } from "@lib/hooks/settings/platform/oauth-clients/usePersistOAuthClient";
import { HelpCards } from "@components/settings/platform/dashboard/HelpCards";
import NoPlatformPlan from "@components/settings/platform/dashboard/NoPlatformPlan";
import { ManagedUserList } from "@components/settings/platform/dashboard/managed-user-list";
import { OAuthClientsList } from "@components/settings/platform/dashboard/oauth-clients-list";
import { useGetUserAttributes } from "@components/settings/platform/hooks/useGetUserAttributes";
import { PlatformPricing } from "@components/settings/platform/pricing/platform-pricing";
const queryClient = new QueryClient();
export default function Platform() {
const [initialClientId, setInitialClientId] = useState("");
const [initialClientName, setInitialClientName] = useState("");
const { data, isLoading: isOAuthClientLoading, refetch: refetchClients } = useOAuthClients();
const {
isLoading: isManagedUserLoading,
data: managedUserData,
refetch: refetchManagedUsers,
} = useGetOAuthClientManagedUsers(initialClientId);
const { isUserLoading, isUserBillingDataLoading, isPlatformUser, isPaidUser, userBillingData, userOrgId } =
useGetUserAttributes();
const { mutateAsync, isPending: isDeleting } = useDeleteOAuthClient({
onSuccess: () => {
showToast("OAuth client deleted successfully", "success");
refetchClients();
refetchManagedUsers();
},
});
const handleDelete = async (id: string) => {
await mutateAsync({ id: id });
};
useEffect(() => {
setInitialClientId(data[0]?.id);
setInitialClientName(data[0]?.name);
}, [data]);
if (isUserLoading || isOAuthClientLoading) return <div className="m-5">Loading...</div>;
if (isUserBillingDataLoading && !userBillingData) {
return <div className="m-5">Loading...</div>;
}
if (isPlatformUser && !isPaidUser) return <PlatformPricing teamId={userOrgId} />;
if (isPlatformUser) {
return (
<QueryClientProvider client={queryClient}>
<div>
<Shell
heading="Platform"
title="Platform"
hideHeadingOnMobile
withoutMain={false}
subtitle="Manage everything related to platform."
isPlatformUser={true}>
<HelpCards />
<OAuthClientsList oauthClients={data} isDeleting={isDeleting} handleDelete={handleDelete} />
<ManagedUserList
oauthClients={data}
managedUsers={managedUserData}
isManagedUserLoading={isManagedUserLoading}
initialClientName={initialClientName}
initialClientId={initialClientId}
handleChange={(id: string, name: string) => {
setInitialClientId(id);
setInitialClientName(name);
refetchManagedUsers();
}}
/>
</Shell>
</div>
</QueryClientProvider>
);
}
return (
<div>
<Shell
// we want to hide org banner and have different sidebar tabs for platform clients
// hence we pass isPlatformUser boolean as prop
isPlatformUser={true}
hideHeadingOnMobile
withoutMain={false}
SidebarContainer={<></>}>
<NoPlatformPlan />
</Shell>
</div>
);
}
@@ -1,14 +1,12 @@
"use client";
import { AboutOrganizationForm } from "@calcom/features/ee/organizations/components";
import { getServerSideProps } from "@calcom/features/ee/organizations/pages/organization";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Meta, WizardLayout } from "@calcom/ui";
import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
export { getServerSideProps } from "@calcom/features/ee/organizations/pages/organization";
import AboutOrganizationForm, { LayoutWrapper } from "~/settings/organizations/[id]/about-view";
const AboutOrganizationPage = () => {
const Page = () => {
const { t } = useLocale();
return (
<>
@@ -17,15 +15,9 @@ const AboutOrganizationPage = () => {
</>
);
};
export const LayoutWrapper = (page: React.ReactElement) => {
return (
<WizardLayout currentStep={3} maxSteps={5}>
{page}
</WizardLayout>
);
};
AboutOrganizationPage.getLayout = LayoutWrapper;
AboutOrganizationPage.PageWrapper = PageWrapper;
Page.getLayout = LayoutWrapper;
Page.PageWrapper = PageWrapper;
export default AboutOrganizationPage;
export default Page;
export { getServerSideProps };
@@ -1,14 +1,12 @@
"use client";
import { AddNewTeamsForm } from "@calcom/features/ee/organizations/components";
import { getServerSideProps } from "@calcom/features/ee/organizations/pages/organization";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Meta, WizardLayout } from "@calcom/ui";
import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
export { getServerSideProps } from "@calcom/features/ee/organizations/pages/organization";
import AddNewTeamsForm, { LayoutWrapper } from "~/settings/organizations/[id]/add-teams-view";
const AddNewTeamsPage = () => {
const Page = () => {
const { t } = useLocale();
return (
<>
@@ -18,15 +16,9 @@ const AddNewTeamsPage = () => {
);
};
export const LayoutWrapper = (page: React.ReactElement) => {
return (
<WizardLayout currentStep={5} maxSteps={5}>
{page}
</WizardLayout>
);
};
Page.getLayout = LayoutWrapper;
Page.PageWrapper = PageWrapper;
AddNewTeamsPage.getLayout = LayoutWrapper;
AddNewTeamsPage.PageWrapper = PageWrapper;
export default Page;
export default AddNewTeamsPage;
export { getServerSideProps };
@@ -1,17 +1,12 @@
"use client";
import { redirect, useRouter } from "next/navigation";
import AddNewTeamMembers from "@calcom/features/ee/teams/components/AddNewTeamMembers";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { getServerSideProps } from "@calcom/features/ee/organizations/pages/organization";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Meta, WizardLayout } from "@calcom/ui";
import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
export { getServerSideProps } from "@calcom/features/ee/organizations/pages/organization";
import OrgAddNewTeamMembers, { LayoutWrapper } from "~/settings/organizations/[id]/onboard-members-view";
const OnboardTeamMembersPage = () => {
const Page = () => {
const { t } = useLocale();
return (
@@ -20,45 +15,13 @@ const OnboardTeamMembersPage = () => {
title={t("invite_organization_admins")}
description={t("invite_organization_admins_description")}
/>
<AddNewTeamMembers isOrg={true} />
<OrgAddNewTeamMembers />
</>
);
};
OnboardTeamMembersPage.getLayout = (page: React.ReactElement) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const router = useRouter();
// eslint-disable-next-line react-hooks/rules-of-hooks
const query = useCompatSearchParams();
Page.getLayout = LayoutWrapper;
Page.PageWrapper = PageWrapper;
return (
<WizardLayout
currentStep={4}
maxSteps={5}
isOptionalCallback={() => {
router.push(`/settings/organizations/${query.get("id")}/add-teams`);
}}>
{page}
</WizardLayout>
);
};
export const buildWrappedOnboardTeamMembersPage = (
id: string | string[] | undefined,
page: React.ReactElement
) => {
return (
<WizardLayout
currentStep={4}
maxSteps={5}
isOptionalCallback={() => {
redirect(`/settings/organizations/${id}/add-teams`);
}}>
{page}
</WizardLayout>
);
};
OnboardTeamMembersPage.PageWrapper = PageWrapper;
export default OnboardTeamMembersPage;
export default Page;
export { getServerSideProps };
@@ -1,16 +1,14 @@
"use client";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import { CreateANewOrganizationForm } from "@calcom/features/ee/organizations/components";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { WizardLayout, Meta } from "@calcom/ui";
import { Meta } from "@calcom/ui";
import { getServerSideProps } from "@lib/settings/organizations/new/getServerSideProps";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
import PageWrapper from "@components/PageWrapper";
const CreateNewOrganizationPage = ({ querySlug }: inferSSRProps<typeof getServerSideProps>) => {
import CreateANewOrganizationForm, { LayoutWrapper } from "~/settings/organizations/new/create-new-view";
const Page = () => {
const { t } = useLocale();
return (
<LicenseRequired>
@@ -19,17 +17,10 @@ const CreateNewOrganizationPage = ({ querySlug }: inferSSRProps<typeof getServer
</LicenseRequired>
);
};
export const LayoutWrapper = (page: React.ReactElement) => {
return (
<WizardLayout currentStep={1} maxSteps={5}>
{page}
</WizardLayout>
);
};
CreateNewOrganizationPage.getLayout = LayoutWrapper;
CreateNewOrganizationPage.PageWrapper = PageWrapper;
Page.getLayout = LayoutWrapper;
Page.PageWrapper = PageWrapper;
export default CreateNewOrganizationPage;
export default Page;
export { getServerSideProps };
+7 -103
View File
@@ -1,108 +1,12 @@
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { useState, useEffect } from "react";
import Shell from "@calcom/features/shell/Shell";
import { showToast } from "@calcom/ui";
import {
useOAuthClients,
useGetOAuthClientManagedUsers,
} from "@lib/hooks/settings/platform/oauth-clients/useOAuthClients";
import { useDeleteOAuthClient } from "@lib/hooks/settings/platform/oauth-clients/usePersistOAuthClient";
import PageWrapper from "@components/PageWrapper";
import { HelpCards } from "@components/settings/platform/dashboard/HelpCards";
import NoPlatformPlan from "@components/settings/platform/dashboard/NoPlatformPlan";
import { ManagedUserList } from "@components/settings/platform/dashboard/managed-user-list";
import { OAuthClientsList } from "@components/settings/platform/dashboard/oauth-clients-list";
import { useGetUserAttributes } from "@components/settings/platform/hooks/useGetUserAttributes";
import { PlatformPricing } from "@components/settings/platform/pricing/platform-pricing";
const queryClient = new QueryClient();
import PlatformView from "~/settings/platform/platform-view";
export default function Platform() {
const [initialClientId, setInitialClientId] = useState("");
const [initialClientName, setInitialClientName] = useState("");
const Page = new Proxy<{
(): JSX.Element;
PageWrapper?: typeof PageWrapper;
}>(PlatformView, {});
const { data, isLoading: isOAuthClientLoading, refetch: refetchClients } = useOAuthClients();
const {
isLoading: isManagedUserLoading,
data: managedUserData,
refetch: refetchManagedUsers,
} = useGetOAuthClientManagedUsers(initialClientId);
Page.PageWrapper = PageWrapper;
const { isUserLoading, isUserBillingDataLoading, isPlatformUser, isPaidUser, userBillingData, userOrgId } =
useGetUserAttributes();
const { mutateAsync, isPending: isDeleting } = useDeleteOAuthClient({
onSuccess: () => {
showToast("OAuth client deleted successfully", "success");
refetchClients();
refetchManagedUsers();
},
});
const handleDelete = async (id: string) => {
await mutateAsync({ id: id });
};
useEffect(() => {
setInitialClientId(data[0]?.id);
setInitialClientName(data[0]?.name);
}, [data]);
if (isUserLoading || isOAuthClientLoading) return <div className="m-5">Loading...</div>;
if (isUserBillingDataLoading && !userBillingData) {
return <div className="m-5">Loading...</div>;
}
if (isPlatformUser && !isPaidUser) return <PlatformPricing teamId={userOrgId} />;
if (isPlatformUser) {
return (
<QueryClientProvider client={queryClient}>
<div>
<Shell
heading="Platform"
title="Platform"
hideHeadingOnMobile
withoutMain={false}
subtitle="Manage everything related to platform."
isPlatformUser={true}>
<HelpCards />
<OAuthClientsList oauthClients={data} isDeleting={isDeleting} handleDelete={handleDelete} />
<ManagedUserList
oauthClients={data}
managedUsers={managedUserData}
isManagedUserLoading={isManagedUserLoading}
initialClientName={initialClientName}
initialClientId={initialClientId}
handleChange={(id: string, name: string) => {
setInitialClientId(id);
setInitialClientName(name);
refetchManagedUsers();
}}
/>
</Shell>
</div>
</QueryClientProvider>
);
}
return (
<div>
<Shell
// we want to hide org banner and have different sidebar tabs for platform clients
// hence we pass isPlatformUser boolean as prop
isPlatformUser={true}
hideHeadingOnMobile
withoutMain={false}
SidebarContainer={<></>}>
<NoPlatformPlan />
</Shell>
</div>
);
}
Platform.PageWrapper = PageWrapper;
export default Page;
+8 -16
View File
@@ -1,15 +1,14 @@
"use client";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import { CreateANewPlatformForm } from "@calcom/features/ee/platform/components/index";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { WizardLayout, Meta } from "@calcom/ui";
import { Meta } from "@calcom/ui";
import { getServerSideProps } from "@lib/settings/organizations/new/getServerSideProps";
import PageWrapper from "@components/PageWrapper";
const CreateNewOrganizationPage = () => {
import CreateNewOrganizationPage, { LayoutWrapper } from "~/settings/platform/new/create-new-view";
const Page = () => {
const { t } = useLocale();
return (
<LicenseRequired>
@@ -17,21 +16,14 @@ const CreateNewOrganizationPage = () => {
title={t("set_up_your_platform_organization")}
description={t("platform_organization_description")}
/>
<CreateANewPlatformForm />
<CreateNewOrganizationPage />
</LicenseRequired>
);
};
export const LayoutWrapper = (page: React.ReactElement) => {
return (
<WizardLayout currentStep={1} maxSteps={1}>
{page}
</WizardLayout>
);
};
CreateNewOrganizationPage.getLayout = LayoutWrapper;
CreateNewOrganizationPage.PageWrapper = PageWrapper;
Page.getLayout = LayoutWrapper;
Page.PageWrapper = PageWrapper;
export default CreateNewOrganizationPage;
export default Page;
export { getServerSideProps };
@@ -1,134 +1,12 @@
import { useParams } from "next/navigation";
import { useRouter } from "next/navigation";
import Shell from "@calcom/features/shell/Shell";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { PERMISSIONS_GROUPED_MAP } from "@calcom/platform-constants";
import { showToast } from "@calcom/ui";
import { useOAuthClient } from "@lib/hooks/settings/platform/oauth-clients/useOAuthClients";
import { useUpdateOAuthClient } from "@lib/hooks/settings/platform/oauth-clients/usePersistOAuthClient";
import PageWrapper from "@components/PageWrapper";
import NoPlatformPlan from "@components/settings/platform/dashboard/NoPlatformPlan";
import { useGetUserAttributes } from "@components/settings/platform/hooks/useGetUserAttributes";
import type { FormValues } from "@components/settings/platform/oauth-clients/oauth-client-form";
import { OAuthClientForm as EditOAuthClientForm } from "@components/settings/platform/oauth-clients/oauth-client-form";
import {
hasAppsReadPermission,
hasAppsWritePermission,
hasBookingReadPermission,
hasBookingWritePermission,
hasEventTypeReadPermission,
hasEventTypeWritePermission,
hasProfileReadPermission,
hasProfileWritePermission,
hasScheduleReadPermission,
hasScheduleWritePermission,
} from "../../../../../../../../packages/platform/utils/permissions";
import EditView from "~/settings/platform/oauth-clients/[clientId]/edit/edit-view";
export default function EditOAuthClient() {
const router = useRouter();
const params = useParams<{ clientId: string }>();
const clientId = params?.clientId || "";
const Page = new Proxy<{
(): JSX.Element;
PageWrapper?: typeof PageWrapper;
}>(EditView, {});
const { isUserLoading, isPlatformUser, isPaidUser } = useGetUserAttributes();
Page.PageWrapper = PageWrapper;
const { data, isFetched, isFetching, isError, refetch } = useOAuthClient(clientId);
const { mutateAsync: update, isPending: isUpdating } = useUpdateOAuthClient({
onSuccess: () => {
showToast("OAuth client updated successfully", "success");
refetch();
router.push("/settings/platform/");
},
onError: () => {
showToast(ErrorCode.UpdatingOauthClientError, "error");
},
clientId,
});
const onSubmit = (data: FormValues) => {
let userPermissions = 0;
const userRedirectUris = data.redirectUris.map((uri) => uri.uri).filter((uri) => !!uri);
Object.keys(PERMISSIONS_GROUPED_MAP).forEach((key) => {
const entity = key as keyof typeof PERMISSIONS_GROUPED_MAP;
const entityKey = PERMISSIONS_GROUPED_MAP[entity].key;
const read = PERMISSIONS_GROUPED_MAP[entity].read;
const write = PERMISSIONS_GROUPED_MAP[entity].write;
if (data[`${entityKey}Read`]) userPermissions |= read;
if (data[`${entityKey}Write`]) userPermissions |= write;
});
update({
name: data.name,
// logo: data.logo,
redirectUris: userRedirectUris,
bookingRedirectUri: data.bookingRedirectUri,
bookingCancelRedirectUri: data.bookingCancelRedirectUri,
bookingRescheduleRedirectUri: data.bookingRescheduleRedirectUri,
areEmailsEnabled: data.areEmailsEnabled,
});
};
if (isUserLoading) return <div className="m-5">Loading...</div>;
if (isPlatformUser && isPaidUser) {
return (
<div>
<Shell title="OAuth client updation form" isPlatformUser={true}>
<div className="m-2 md:mx-14 md:mx-5">
<div className="border-subtle mx-auto block justify-between rounded-t-lg border px-4 py-6 sm:flex sm:px-6">
<div className="flex w-full flex-col">
<h1 className="font-cal text-emphasis mb-1 text-xl font-semibold leading-5 tracking-wide">
OAuth client updation form
</h1>
<p className="text-default text-sm ltr:mr-4 rtl:ml-4">
This is the form to edit an existing OAuth client
</p>
</div>
</div>
{(!Boolean(clientId) || (isFetched && !data)) && <p>OAuth Client not found.</p>}
{isFetched && !!data && (
<EditOAuthClientForm
defaultValues={{
name: data?.name ?? "",
areEmailsEnabled: data.areEmailsEnabled ?? false,
redirectUris: data?.redirectUris?.map((uri) => ({ uri })) ?? [{ uri: "" }],
bookingRedirectUri: data?.bookingRedirectUri ?? "",
bookingCancelRedirectUri: data?.bookingCancelRedirectUri ?? "",
bookingRescheduleRedirectUri: data?.bookingRescheduleRedirectUri ?? "",
appsRead: hasAppsReadPermission(data?.permissions),
appsWrite: hasAppsWritePermission(data?.permissions),
bookingRead: hasBookingReadPermission(data?.permissions),
bookingWrite: hasBookingWritePermission(data?.permissions),
eventTypeRead: hasEventTypeReadPermission(data?.permissions),
eventTypeWrite: hasEventTypeWritePermission(data?.permissions),
profileRead: hasProfileReadPermission(data?.permissions),
profileWrite: hasProfileWritePermission(data?.permissions),
scheduleRead: hasScheduleReadPermission(data?.permissions),
scheduleWrite: hasScheduleWritePermission(data?.permissions),
}}
onSubmit={onSubmit}
isPending={isUpdating}
/>
)}
{isFetching && <p>Loading...</p>}
{isError && <p>Something went wrong.</p>}
</div>
</Shell>
</div>
);
}
return (
<div>
<Shell isPlatformUser={true} hideHeadingOnMobile withoutMain={false} SidebarContainer={<></>}>
<NoPlatformPlan />
</Shell>
</div>
);
}
EditOAuthClient.PageWrapper = PageWrapper;
export default Page;
@@ -1,123 +1,12 @@
import { useParams } from "next/navigation";
import { useRouter } from "next/navigation";
import Shell from "@calcom/features/shell/Shell";
import { WebhookForm } from "@calcom/features/webhooks/components";
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
import { showToast } from "@calcom/ui";
import {
useOAuthClientWebhooks,
useCreateOAuthClientWebhook,
useUpdateOAuthClientWebhook,
} from "@lib/hooks/settings/platform/oauth-clients/useOAuthClientWebhooks";
import PageWrapper from "@components/PageWrapper";
import NoPlatformPlan from "@components/settings/platform/dashboard/NoPlatformPlan";
import { useGetUserAttributes } from "@components/settings/platform/hooks/useGetUserAttributes";
export default function EditOAuthClientWebhooks() {
const router = useRouter();
const params = useParams<{ clientId: string }>();
const clientId = params?.clientId || "";
import EditWebhooksView from "~/settings/platform/oauth-clients/[clientId]/edit/edit-webhooks-view";
const { isUserLoading, isPlatformUser, isPaidUser } = useGetUserAttributes();
const Page = new Proxy<{
(): JSX.Element;
PageWrapper?: typeof PageWrapper;
}>(EditWebhooksView, {});
const {
data: webhooks,
status: webhooksStatus,
isFetched: isWebhooksFetched,
refetch: refetchWebhooks,
} = useOAuthClientWebhooks(clientId);
const webhookId = webhooks?.[0]?.id ?? "";
const webhook = webhooks?.[0];
const { mutateAsync: createWebhook } = useCreateOAuthClientWebhook(clientId);
const { mutateAsync: updateWebhook } = useUpdateOAuthClientWebhook(clientId);
Page.PageWrapper = PageWrapper;
if (isUserLoading) return <div className="m-5">Loading...</div>;
if (isPlatformUser && isPaidUser) {
return (
<div>
<Shell title="OAuth client updation form" isPlatformUser={true}>
<div className="m-2 md:mx-5">
<div className="border-subtle mx-auto block justify-between rounded-t-lg border px-4 py-6 sm:flex sm:px-6">
<div className="flex w-full flex-col">
<h1 className="font-cal text-emphasis mb-1 text-xl font-semibold leading-5 tracking-wide">
Webhook update form
</h1>
<p className="text-default text-sm ltr:mr-4 rtl:ml-4">Add a webhook to your OAuthClient.</p>
</div>
</div>
{webhooksStatus !== "success" && <p>Error while trying to access webhooks.</p>}
{isWebhooksFetched && webhooksStatus === "success" && (
<WebhookForm
overrideTriggerOptions={[
{ value: WebhookTriggerEvents.BOOKING_CANCELLED, label: "booking_cancelled" },
{ value: WebhookTriggerEvents.BOOKING_CREATED, label: "booking_created" },
{ value: WebhookTriggerEvents.BOOKING_REJECTED, label: "booking_rejected" },
{ value: WebhookTriggerEvents.BOOKING_REQUESTED, label: "booking_requested" },
{ value: WebhookTriggerEvents.BOOKING_RESCHEDULED, label: "booking_rescheduled" },
{ value: WebhookTriggerEvents.BOOKING_NO_SHOW_UPDATED, label: "booking_no_show_updated" },
{ value: WebhookTriggerEvents.MEETING_ENDED, label: "meeting_ended" },
{ value: WebhookTriggerEvents.MEETING_STARTED, label: "meeting_started" },
{ value: WebhookTriggerEvents.RECORDING_READY, label: "recording_ready" },
{
value: WebhookTriggerEvents.RECORDING_TRANSCRIPTION_GENERATED,
label: "recording_transcription_generated",
},
]}
onSubmit={async (data) => {
try {
const body = {
active: data.active,
payloadTemplate: data.payloadTemplate ?? undefined,
subscriberUrl: data.subscriberUrl,
triggers: data.eventTriggers,
secret: data.secret ?? undefined,
};
if (webhook) {
await updateWebhook({
webhookId,
body,
});
showToast("Webhook updated successfully.", "success");
} else {
await createWebhook(body);
showToast("Webhook created successfully.", "success");
}
await refetchWebhooks();
router.push("/settings/platform/");
} catch (err) {
showToast(`Failed to ${webhookId ? "update" : "create"} webhook.`, "error");
}
}}
onCancel={() => {
router.push("/settings/platform/");
}}
noRoutingFormTriggers={true}
webhook={
webhook
? { ...webhook, eventTriggers: webhook.triggers, secret: webhook.secret ?? null }
: undefined
}
/>
)}
</div>
</Shell>
</div>
);
}
return (
<div>
<Shell isPlatformUser={true} hideHeadingOnMobile withoutMain={false} SidebarContainer={<></>}>
<NoPlatformPlan />
</Shell>
</div>
);
}
EditOAuthClientWebhooks.PageWrapper = PageWrapper;
export default Page;
@@ -1,92 +1,12 @@
import { useRouter } from "next/navigation";
import Shell from "@calcom/features/shell/Shell";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { PERMISSIONS_GROUPED_MAP } from "@calcom/platform-constants/permissions";
import { showToast } from "@calcom/ui";
import { useCreateOAuthClient } from "@lib/hooks/settings/platform/oauth-clients/usePersistOAuthClient";
import PageWrapper from "@components/PageWrapper";
import NoPlatformPlan from "@components/settings/platform/dashboard/NoPlatformPlan";
import { useGetUserAttributes } from "@components/settings/platform/hooks/useGetUserAttributes";
import type { FormValues } from "@components/settings/platform/oauth-clients/oauth-client-form";
import { OAuthClientForm } from "@components/settings/platform/oauth-clients/oauth-client-form";
export default function CreateOAuthClient() {
const searchParams = useCompatSearchParams();
const router = useRouter();
const clientId = searchParams?.get("clientId") || "";
import CreateOAuthClientView from "~/settings/platform/oauth-clients/create-new-view";
const { isUserLoading, isPlatformUser, isPaidUser } = useGetUserAttributes();
const Page = new Proxy<{
(): JSX.Element;
PageWrapper?: typeof PageWrapper;
}>(CreateOAuthClientView, {});
const { mutateAsync: save, isPending: isSaving } = useCreateOAuthClient({
onSuccess: () => {
showToast("OAuth client created successfully", "success");
router.push("/settings/platform/");
},
onError: () => {
showToast(ErrorCode.CreatingOauthClientError, "error");
},
});
Page.PageWrapper = PageWrapper;
const onSubmit = (data: FormValues) => {
let userPermissions = 0;
const userRedirectUris = data.redirectUris.map((uri) => uri.uri).filter((uri) => !!uri);
Object.keys(PERMISSIONS_GROUPED_MAP).forEach((key) => {
const entity = key as keyof typeof PERMISSIONS_GROUPED_MAP;
const entityKey = PERMISSIONS_GROUPED_MAP[entity].key;
const read = PERMISSIONS_GROUPED_MAP[entity].read;
const write = PERMISSIONS_GROUPED_MAP[entity].write;
if (data[`${entityKey}Read`]) userPermissions |= read;
if (data[`${entityKey}Write`]) userPermissions |= write;
});
save({
name: data.name,
permissions: userPermissions,
// logo: data.logo,
redirectUris: userRedirectUris,
bookingRedirectUri: data.bookingRedirectUri,
bookingCancelRedirectUri: data.bookingCancelRedirectUri,
bookingRescheduleRedirectUri: data.bookingRescheduleRedirectUri,
areEmailsEnabled: data.areEmailsEnabled,
});
};
if (isUserLoading) return <div className="m-5">Loading...</div>;
if (isPlatformUser && isPaidUser) {
return (
<div>
<Shell title={`OAuth client ${!!clientId ? "updation" : "creation"} form`} isPlatformUser={true}>
<div className="m-2 md:mx-5">
<div className="border-subtle mx-auto block justify-between rounded-t-lg border px-4 py-6 sm:flex sm:px-6">
<div className="flex w-full flex-col">
<h1 className="font-cal text-emphasis mb-1 text-xl font-semibold leading-5 tracking-wide">
OAuth client creation form
</h1>
<p className="text-default text-sm ltr:mr-4 rtl:ml-4">
This is the form to create a new OAuth client
</p>
</div>
</div>
<OAuthClientForm isPending={isSaving} onSubmit={onSubmit} />
</div>
</Shell>
</div>
);
}
return (
<div>
<Shell isPlatformUser={true} hideHeadingOnMobile withoutMain={false} SidebarContainer={<></>}>
<NoPlatformPlan />
</Shell>
</div>
);
}
CreateOAuthClient.PageWrapper = PageWrapper;
export default Page;
@@ -9,6 +9,8 @@ checkRoute () {
checkRoute "$APP_ROUTER_EVENT_TYPES_ENABLED" app/future/event-types
checkRoute "$APP_ROUTER_AVAILABILITY_ENABLED" app/future/availability
checkRoute "$APP_ROUTER_SETTINGS_ADMIN_ENABLED" app/future/settings/admin
checkRoute "$APP_ROUTER_SETTINGS_PLATFORM_ENABLED" app/future/settings/platform
checkRoute "$APP_ROUTER_SETTINGS_ORG_ENABLED" app/future/settings/organizations
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
@@ -1,3 +1,5 @@
"use client";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
@@ -1,3 +1,5 @@
"use client";
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useState } from "react";
@@ -1,3 +1,5 @@
"use client";
import type { SessionContextValue } from "next-auth/react";
import { signIn, useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
@@ -1,3 +1,5 @@
"use client";
import type { SessionContextValue } from "next-auth/react";
import { useSession, signIn } from "next-auth/react";
import { useRouter } from "next/navigation";
+2
View File
@@ -236,6 +236,8 @@
"APP_ROUTER_EVENT_TYPES_ENABLED",
"APP_ROUTER_GETTING_STARTED_STEP_ENABLED",
"APP_ROUTER_SETTINGS_ADMIN_ENABLED",
"APP_ROUTER_SETTINGS_PLATFORM_ENABLED",
"APP_ROUTER_SETTINGS_ORG_ENABLED",
"APP_ROUTER_SETTINGS_TEAMS_ENABLED",
"APP_ROUTER_WORKFLOWS_ENABLED",
"APP_ROUTER_VIDEO_ENABLED",