@@ -182,32 +184,5 @@ export default function MoveUserToOrg() {
);
}
-export async function getServerSideProps(ctx: GetServerSidePropsContext) {
- const session = await getSession(ctx);
- if (!session || !session.user) {
- return {
- redirect: {
- destination: "/login",
- permanent: false,
- },
- };
- }
-
- const isAdmin = session.user.role === UserPermissionRole.ADMIN;
- if (!isAdmin) {
- return {
- redirect: {
- destination: "/",
- permanent: false,
- },
- };
- }
- return {
- props: {
- ...(await serverSideTranslations(ctx.locale || "en", ["common"])),
- },
- };
-}
-
MoveUserToOrg.PageWrapper = PageWrapper;
MoveUserToOrg.getLayout = getLayout;
diff --git a/apps/web/pages/settings/admin/orgMigrations/removeTeamFromOrg.tsx b/apps/web/pages/settings/admin/orgMigrations/removeTeamFromOrg.tsx
index 17a90f115a..2148457abd 100644
--- a/apps/web/pages/settings/admin/orgMigrations/removeTeamFromOrg.tsx
+++ b/apps/web/pages/settings/admin/orgMigrations/removeTeamFromOrg.tsx
@@ -1,21 +1,23 @@
+"use client";
+
import { zodResolver } from "@hookform/resolvers/zod";
-import type { GetServerSidePropsContext } from "next";
-import { getSession } from "next-auth/react";
import type { TFunction } from "next-i18next";
-import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { useState } from "react";
import { useForm } from "react-hook-form";
import z from "zod";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { UserPermissionRole } from "@calcom/prisma/enums";
import { getStringAsNumberRequiredSchema } from "@calcom/prisma/zod-utils";
import { Button, Form, Meta, TextField, showToast } from "@calcom/ui";
+import { getServerSideProps } from "@lib/settings/admin/orgMigrations/removeTeamFromOrg/getServerSideProps";
+
import PageWrapper from "@components/PageWrapper";
import { getLayout } from "./_OrgMigrationLayout";
+export { getServerSideProps };
+
function Wrapper({ children }: { children: React.ReactNode }) {
return (
@@ -111,32 +113,5 @@ export default function RemoveTeamFromOrg() {
);
}
-export async function getServerSideProps(ctx: GetServerSidePropsContext) {
- const session = await getSession(ctx);
- if (!session || !session.user) {
- return {
- redirect: {
- destination: "/login",
- permanent: false,
- },
- };
- }
-
- const isAdmin = session.user.role === UserPermissionRole.ADMIN;
- if (!isAdmin) {
- return {
- redirect: {
- destination: "/",
- permanent: false,
- },
- };
- }
- return {
- props: {
- ...(await serverSideTranslations(ctx.locale || "en", ["common"])),
- },
- };
-}
-
RemoveTeamFromOrg.PageWrapper = PageWrapper;
RemoveTeamFromOrg.getLayout = getLayout;
diff --git a/apps/web/pages/settings/admin/orgMigrations/removeUserFromOrg.tsx b/apps/web/pages/settings/admin/orgMigrations/removeUserFromOrg.tsx
index ef7710ed37..18d18a15d8 100644
--- a/apps/web/pages/settings/admin/orgMigrations/removeUserFromOrg.tsx
+++ b/apps/web/pages/settings/admin/orgMigrations/removeUserFromOrg.tsx
@@ -1,25 +1,30 @@
+"use client";
+
import { zodResolver } from "@hookform/resolvers/zod";
-import type { GetServerSidePropsContext } from "next";
-import { getSession } from "next-auth/react";
import type { TFunction } from "next-i18next";
-import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { useState } from "react";
import { useForm } from "react-hook-form";
import z from "zod";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { UserPermissionRole } from "@calcom/prisma/enums";
import { getStringAsNumberRequiredSchema } from "@calcom/prisma/zod-utils";
import { Button, TextField, Meta, showToast, Form } from "@calcom/ui";
+import { getServerSideProps } from "@lib/settings/admin/orgMigrations/removeUserFromOrg/getServerSideProps";
+
import PageWrapper from "@components/PageWrapper";
import { getLayout } from "./_OrgMigrationLayout";
+export { getServerSideProps };
+
function Wrapper({ children }: { children: React.ReactNode }) {
return (
-
+
{children}
);
@@ -106,32 +111,5 @@ export default function RemoveUserFromOrg() {
);
}
-export async function getServerSideProps(ctx: GetServerSidePropsContext) {
- const session = await getSession(ctx);
- if (!session || !session.user) {
- return {
- redirect: {
- destination: "/login",
- permanent: false,
- },
- };
- }
-
- const isAdmin = session.user.role === UserPermissionRole.ADMIN;
- if (!isAdmin) {
- return {
- redirect: {
- destination: "/",
- permanent: false,
- },
- };
- }
- return {
- props: {
- ...(await serverSideTranslations(ctx.locale || "en", ["common"])),
- },
- };
-}
-
RemoveUserFromOrg.PageWrapper = PageWrapper;
RemoveUserFromOrg.getLayout = getLayout;
diff --git a/apps/web/pages/settings/admin/organizations/[id]/edit.tsx b/apps/web/pages/settings/admin/organizations/[id]/edit.tsx
index 2236d8f850..cb60a65ecd 100644
--- a/apps/web/pages/settings/admin/organizations/[id]/edit.tsx
+++ b/apps/web/pages/settings/admin/organizations/[id]/edit.tsx
@@ -1,9 +1,40 @@
-import OrgEditView from "@calcom/features/ee/organizations/pages/settings/admin/AdminOrgEditPage";
+import { z } from "zod";
+
+import NoSSR from "@calcom/core/components/NoSSR";
+import LicenseRequired from "@calcom/ee/common/components/LicenseRequired";
+import OrgForm from "@calcom/features/ee/organizations/pages/settings/admin/AdminOrgEditPage";
+import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback";
+import { trpc } from "@calcom/trpc/react";
+import { Meta } from "@calcom/ui";
-import type { CalPageWrapper } from "@components/PageWrapper";
import PageWrapper from "@components/PageWrapper";
+import { getLayout } from "@components/auth/layouts/AdminLayout";
-const Page = OrgEditView as CalPageWrapper;
+const paramsSchema = z.object({ id: z.coerce.number() });
+
+const Page = () => {
+ const params = useParamsWithFallback();
+ const parsedParams = paramsSchema.safeParse(params);
+
+ if (!parsedParams.success) return
Invalid id
;
+
+ const [org] = trpc.viewer.organizations.adminGet.useSuspenseQuery({ id: parsedParams.data.id });
+
+ return (
+
+
+
+
+
+
+
+
+ );
+};
Page.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
export default Page;
diff --git a/apps/web/pages/settings/admin/organizations/index.tsx b/apps/web/pages/settings/admin/organizations/index.tsx
index 9c2d7cc1de..b5ff176b3e 100644
--- a/apps/web/pages/settings/admin/organizations/index.tsx
+++ b/apps/web/pages/settings/admin/organizations/index.tsx
@@ -1,9 +1,26 @@
-import AdminOrgsPage from "@calcom/features/ee/organizations/pages/settings/admin/AdminOrgPage";
+import NoSSR from "@calcom/core/components/NoSSR";
+import LicenseRequired from "@calcom/ee/common/components/LicenseRequired";
+import AdminOrgTable from "@calcom/features/ee/organizations/pages/settings/admin/AdminOrgPage";
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { Meta } from "@calcom/ui";
-import type { CalPageWrapper } from "@components/PageWrapper";
import PageWrapper from "@components/PageWrapper";
+import { getLayout } from "@components/auth/layouts/AdminLayout";
-const Page = AdminOrgsPage as CalPageWrapper;
+const Page = () => {
+ const { t } = useLocale();
+ return (
+
+ );
+};
Page.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
export default Page;
diff --git a/apps/web/pages/settings/admin/users/[id]/edit.tsx b/apps/web/pages/settings/admin/users/[id]/edit.tsx
index 0ee1269cca..9b99ded74f 100644
--- a/apps/web/pages/settings/admin/users/[id]/edit.tsx
+++ b/apps/web/pages/settings/admin/users/[id]/edit.tsx
@@ -1,9 +1,41 @@
+import { z } from "zod";
+
+import NoSSR from "@calcom/core/components/NoSSR";
+import LicenseRequired from "@calcom/ee/common/components/LicenseRequired";
import UsersEditView from "@calcom/features/ee/users/pages/users-edit-view";
+import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback";
+import { trpc } from "@calcom/trpc/react";
+import { Meta } from "@calcom/ui";
-import type { CalPageWrapper } from "@components/PageWrapper";
import PageWrapper from "@components/PageWrapper";
+import { getLayout } from "@components/auth/layouts/AdminLayout";
+
+const userIdSchema = z.object({ id: z.coerce.number() });
+
+const Page = () => {
+ const params = useParamsWithFallback();
+ const input = userIdSchema.safeParse(params);
+
+ if (!input.success) return
Invalid input
;
+
+ const [data] = trpc.viewer.users.get.useSuspenseQuery({ userId: input.data.id });
+ const { user } = data;
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ >
+ );
+};
-const Page = UsersEditView as CalPageWrapper;
Page.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
export default Page;
diff --git a/apps/web/pages/settings/admin/users/add.tsx b/apps/web/pages/settings/admin/users/add.tsx
index 293a90214e..ce2911b232 100644
--- a/apps/web/pages/settings/admin/users/add.tsx
+++ b/apps/web/pages/settings/admin/users/add.tsx
@@ -1,9 +1,16 @@
import UsersAddView from "@calcom/features/ee/users/pages/users-add-view";
+import { Meta } from "@calcom/ui";
-import type { CalPageWrapper } from "@components/PageWrapper";
import PageWrapper from "@components/PageWrapper";
+import { getLayout } from "@components/auth/layouts/AdminLayout";
-const Page = UsersAddView as CalPageWrapper;
+const Page = () => (
+ <>
+
+
+ >
+);
Page.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
export default Page;
diff --git a/apps/web/pages/settings/admin/users/index.tsx b/apps/web/pages/settings/admin/users/index.tsx
index 194c966682..8a74ba8a10 100644
--- a/apps/web/pages/settings/admin/users/index.tsx
+++ b/apps/web/pages/settings/admin/users/index.tsx
@@ -1,9 +1,28 @@
import UsersListingView from "@calcom/features/ee/users/pages/users-listing-view";
+import { Meta, Button } from "@calcom/ui";
-import type { CalPageWrapper } from "@components/PageWrapper";
import PageWrapper from "@components/PageWrapper";
+import { getLayout } from "@components/auth/layouts/AdminLayout";
-const Page = UsersListingView as CalPageWrapper;
+const Page = () => {
+ return (
+ <>
+
+ {/* TODO: Add import users functionality */}
+ {/*
*/}
+
+
+ }
+ />
+
+ >
+ );
+};
Page.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
export default Page;
diff --git a/apps/web/pages/settings/billing/index.tsx b/apps/web/pages/settings/billing/index.tsx
index 4ba2f29720..b4f4cc80af 100644
--- a/apps/web/pages/settings/billing/index.tsx
+++ b/apps/web/pages/settings/billing/index.tsx
@@ -1,71 +1,23 @@
-"use client";
-
-import { usePathname } from "next/navigation";
-
-import { useIntercom } from "@calcom/features/ee/support/lib/intercom/useIntercom";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
-import { classNames } from "@calcom/lib";
-import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { Button, Meta } from "@calcom/ui";
+import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-interface CtaRowProps {
- title: string;
- description: string;
- children: React.ReactNode;
- className?: string;
-}
+import BillingView from "~/settings/billing/billing-view";
-const CtaRow = ({ title, description, className, children }: CtaRowProps) => {
- return (
- <>
-
-
-
{title}
-
{description}
-
- {children}
-
- >
- );
-};
-
-const BillingView = () => {
- const pathname = usePathname();
+const Page = () => {
const { t } = useLocale();
- const { open } = useIntercom();
- const returnTo = pathname;
- const billingHref = `/api/integrations/stripepayment/portal?returnTo=${WEBAPP_URL}${returnTo}`;
-
- const onContactSupportClick = async () => {
- await open();
- };
return (
<>
-
-
-
-
-
-
-
-
-
-
-
+
>
);
};
-BillingView.getLayout = getLayout;
-BillingView.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
-export default BillingView;
+export default Page;
diff --git a/apps/web/pages/settings/developer/api-keys.tsx b/apps/web/pages/settings/developer/api-keys.tsx
index b60df18aad..33b8ef2d40 100644
--- a/apps/web/pages/settings/developer/api-keys.tsx
+++ b/apps/web/pages/settings/developer/api-keys.tsx
@@ -1,70 +1,16 @@
-import { useState } from "react";
-
-import type { TApiKeys } from "@calcom/ee/api-keys/components/ApiKeyListItem";
-import LicenseRequired from "@calcom/ee/common/components/LicenseRequired";
-import ApiKeyDialogForm from "@calcom/features/ee/api-keys/components/ApiKeyDialogForm";
-import ApiKeyListItem from "@calcom/features/ee/api-keys/components/ApiKeyListItem";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import { APP_NAME } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { trpc } from "@calcom/trpc/react";
-import {
- Button,
- Dialog,
- DialogContent,
- EmptyScreen,
- Meta,
- SkeletonContainer,
- SkeletonText,
-} from "@calcom/ui";
+import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
- return (
-
-
-
-
-
-
-
- );
-};
+import ApiKeysView from "~/settings/developer/api-keys-view";
+import NewApiKeyButton from "~/settings/developer/components/CreateApiKeyButton";
-const ApiKeysView = () => {
+const Page = () => {
const { t } = useLocale();
- const { data, isPending } = trpc.viewer.apiKeys.list.useQuery();
-
- const [apiKeyModal, setApiKeyModal] = useState(false);
- const [apiKeyToEdit, setApiKeyToEdit] = useState<(TApiKeys & { neverExpires?: boolean }) | undefined>(
- undefined
- );
-
- const NewApiKeyButton = () => {
- return (
-
- );
- };
-
- if (isPending || !data) {
- return (
-
- );
- }
-
return (
<>
{
CTA={
}
borderInShellHeader={true}
/>
-
-
-
- {data?.length ? (
- <>
-
- {data.map((apiKey, index) => (
-
{
- setApiKeyToEdit(apiKey);
- setApiKeyModal(true);
- }}
- />
- ))}
-
- >
- ) : (
-
}
- />
- )}
-
-
-
-
+
>
);
};
-ApiKeysView.getLayout = getLayout;
-ApiKeysView.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
-export default ApiKeysView;
+export default Page;
diff --git a/apps/web/pages/settings/developer/webhooks/[id]/index.tsx b/apps/web/pages/settings/developer/webhooks/[id]/index.tsx
index 9daf00ba80..b9d17fe6d5 100644
--- a/apps/web/pages/settings/developer/webhooks/[id]/index.tsx
+++ b/apps/web/pages/settings/developer/webhooks/[id]/index.tsx
@@ -1,9 +1,43 @@
-import WebhookEditView from "@calcom/features/webhooks/pages/webhook-edit-view";
+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 type { CalPageWrapper } from "@components/PageWrapper";
import PageWrapper from "@components/PageWrapper";
-const Page = WebhookEditView as CalPageWrapper;
+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
;
+
+ // I think we should do SSR for this page
+ return (
+ <>
+
+
+ >
+ );
+};
+
+Page.getLayout = getLayout;
Page.PageWrapper = PageWrapper;
export default Page;
diff --git a/apps/web/pages/settings/developer/webhooks/index.tsx b/apps/web/pages/settings/developer/webhooks/index.tsx
index ad42580c23..e23bb64712 100644
--- a/apps/web/pages/settings/developer/webhooks/index.tsx
+++ b/apps/web/pages/settings/developer/webhooks/index.tsx
@@ -1,9 +1,38 @@
-import WebhooksView from "@calcom/features/webhooks/pages/webhooks-view";
+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 type { CalPageWrapper } from "@components/PageWrapper";
import PageWrapper from "@components/PageWrapper";
-const Page = WebhooksView as CalPageWrapper;
+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 (
+ <>
+
0 ?
: <>>}
+ borderInShellHeader={(data && data.profiles.length === 1) || !data?.webhookGroups?.length}
+ />
+
+ >
+ );
+};
+Page.getLayout = getLayout;
Page.PageWrapper = PageWrapper;
export default Page;
diff --git a/apps/web/pages/settings/developer/webhooks/new.tsx b/apps/web/pages/settings/developer/webhooks/new.tsx
index df39aea3f9..981ddd73e7 100644
--- a/apps/web/pages/settings/developer/webhooks/new.tsx
+++ b/apps/web/pages/settings/developer/webhooks/new.tsx
@@ -1,9 +1,27 @@
+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 type { CalPageWrapper } from "@components/PageWrapper";
import PageWrapper from "@components/PageWrapper";
-const Page = WebhookNewView as CalPageWrapper;
+const Page = () => {
+ const { t } = useLocale();
+ return (
+ <>
+
+
+ >
+ );
+};
+
Page.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
export default Page;
diff --git a/apps/web/pages/settings/my-account/appearance.tsx b/apps/web/pages/settings/my-account/appearance.tsx
index b290489c0c..9fbe4f4adf 100644
--- a/apps/web/pages/settings/my-account/appearance.tsx
+++ b/apps/web/pages/settings/my-account/appearance.tsx
@@ -1,452 +1,23 @@
-"use client";
-
-import { useSession } from "next-auth/react";
-import { useState } from "react";
-import { Controller, useForm } from "react-hook-form";
-import type { z } from "zod";
-
-import { BookerLayoutSelector } from "@calcom/features/settings/BookerLayoutSelector";
-import SectionBottomActions from "@calcom/features/settings/SectionBottomActions";
-import ThemeLabel from "@calcom/features/settings/ThemeLabel";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
-import { APP_NAME } from "@calcom/lib/constants";
-import { DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR } from "@calcom/lib/constants";
-import { checkWCAGContrastColor } from "@calcom/lib/getBrandColours";
-import useGetBrandingColours from "@calcom/lib/getBrandColours";
-import { useHasPaidPlan } from "@calcom/lib/hooks/useHasPaidPlan";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import useTheme from "@calcom/lib/hooks/useTheme";
-import { validateBookerLayouts } from "@calcom/lib/validateBookerLayouts";
-import type { userMetadata } from "@calcom/prisma/zod-utils";
-import { trpc } from "@calcom/trpc/react";
-import type { RouterOutputs } from "@calcom/trpc/react";
-import {
- Alert,
- Button,
- ColorPicker,
- Form,
- Meta,
- showToast,
- SkeletonButton,
- SkeletonContainer,
- SkeletonText,
- SettingsToggle,
- UpgradeTeamsBadge,
- useCalcomTheme,
-} from "@calcom/ui";
+import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
- return (
-
-
-
-
-
-
-
-
-
-
-
+import AppearancePage from "~/settings/my-account/appearance-view";
-
-
-
-
-
-
-
-
- );
-};
-
-const useBrandColors = (
- currentTheme: string | null,
- {
- brandColor,
- darkBrandColor,
- }: {
- brandColor?: string | null;
- darkBrandColor?: string | null;
- }
-): void => {
- const brandTheme = useGetBrandingColours({
- lightVal: brandColor,
- darkVal: darkBrandColor,
- });
- const selectedTheme = currentTheme ? brandTheme[currentTheme as "light" | "dark"] : {};
- useCalcomTheme({
- root: selectedTheme,
- });
-};
-
-const AppearanceView = ({
- user,
- hasPaidPlan,
-}: {
- user: RouterOutputs["viewer"]["me"];
- hasPaidPlan: boolean;
-}) => {
+const Page = () => {
const { t } = useLocale();
- const utils = trpc.useUtils();
- const session = useSession();
- const isApartOfOrganization = session.data?.user.org?.id;
- const [darkModeError, setDarkModeError] = useState(false);
- const [lightModeError, setLightModeError] = useState(false);
- const [isCustomBrandColorChecked, setIsCustomBranColorChecked] = useState(
- user?.brandColor !== DEFAULT_LIGHT_BRAND_COLOR || user?.darkBrandColor !== DEFAULT_DARK_BRAND_COLOR
- );
- const [hideBrandingValue, setHideBrandingValue] = useState(user?.hideBranding ?? false);
- useTheme(user?.appTheme);
- useBrandColors(user?.appTheme ?? null, {
- brandColor: user?.brandColor,
- darkBrandColor: user?.darkBrandColor,
- });
-
- const userAppThemeFormMethods = useForm({
- defaultValues: {
- appTheme: user.appTheme,
- },
- });
-
- const {
- formState: { isSubmitting: isUserAppThemeSubmitting, isDirty: isUserAppThemeDirty },
- reset: resetUserAppThemeReset,
- } = userAppThemeFormMethods;
-
- const userThemeFormMethods = useForm({
- defaultValues: {
- theme: user.theme,
- },
- });
-
- const {
- formState: { isSubmitting: isUserThemeSubmitting, isDirty: isUserThemeDirty },
- reset: resetUserThemeReset,
- } = userThemeFormMethods;
-
- const bookerLayoutFormMethods = useForm({
- defaultValues: {
- metadata: user.metadata as z.infer
,
- },
- });
-
- const {
- formState: { isSubmitting: isBookerLayoutFormSubmitting, isDirty: isBookerLayoutFormDirty },
- reset: resetBookerLayoutThemeReset,
- } = bookerLayoutFormMethods;
-
- const DEFAULT_BRAND_COLOURS = {
- light: user.brandColor ?? DEFAULT_LIGHT_BRAND_COLOR,
- dark: user.darkBrandColor ?? DEFAULT_DARK_BRAND_COLOR,
- };
-
- const brandColorsFormMethods = useForm({
- defaultValues: {
- brandColor: DEFAULT_BRAND_COLOURS.light,
- darkBrandColor: DEFAULT_BRAND_COLOURS.dark,
- },
- });
-
- const {
- formState: { isSubmitting: isBrandColorsFormSubmitting, isDirty: isBrandColorsFormDirty },
- reset: resetBrandColorsThemeReset,
- } = brandColorsFormMethods;
-
- const selectedTheme = userThemeFormMethods.watch("theme");
- const selectedThemeIsDark =
- selectedTheme === "dark" ||
- (selectedTheme === "" &&
- typeof document !== "undefined" &&
- document.documentElement.classList.contains("dark"));
-
- const mutation = trpc.viewer.updateProfile.useMutation({
- onSuccess: async (data) => {
- await utils.viewer.me.invalidate();
- showToast(t("settings_updated_successfully"), "success");
- resetBrandColorsThemeReset({ brandColor: data.brandColor, darkBrandColor: data.darkBrandColor });
- resetBookerLayoutThemeReset({ metadata: data.metadata });
- resetUserThemeReset({ theme: data.theme });
- resetUserAppThemeReset({ appTheme: data.appTheme });
- },
- onError: (error) => {
- if (error.message) {
- showToast(error.message, "error");
- } else {
- showToast(t("error_updating_settings"), "error");
- }
- },
- onSettled: async () => {
- await utils.viewer.me.invalidate();
- },
- });
return (
-
+ <>
-
-
-
{t("app_theme")}
-
{t("app_theme_applies_note")}
-
-
-
-
- {isApartOfOrganization ? null : (
- <>
-
-
-
{t("theme")}
-
{t("theme_applies_note")}
-
-
-
-
-
-
-
-
- {/* TODO future PR to preview brandColors */}
- {/*
*/}
-
-
}
- onCheckedChange={(checked) => {
- setHideBrandingValue(checked);
- mutation.mutate({ hideBranding: checked });
- }}
- switchContainerClassName="mt-6"
- />
- >
- )}
-
+
+ >
);
};
-const AppearanceViewWrapper = () => {
- const { data: user, isPending } = trpc.viewer.me.useQuery();
- const { isPending: isTeamPlanStatusLoading, hasPaidPlan } = useHasPaidPlan();
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
- const { t } = useLocale();
-
- if (isPending || isTeamPlanStatusLoading || !user)
- return ;
-
- return ;
-};
-
-AppearanceViewWrapper.getLayout = getLayout;
-AppearanceViewWrapper.PageWrapper = PageWrapper;
-
-export default AppearanceViewWrapper;
+export default Page;
diff --git a/apps/web/pages/settings/my-account/calendars.tsx b/apps/web/pages/settings/my-account/calendars.tsx
index 5cfe5e7d0c..2dfa7cb295 100644
--- a/apps/web/pages/settings/my-account/calendars.tsx
+++ b/apps/web/pages/settings/my-account/calendars.tsx
@@ -1,42 +1,13 @@
-"use client";
-
-import { Fragment } from "react";
-
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { Button, Meta, SkeletonButton, SkeletonContainer, SkeletonText } from "@calcom/ui";
+import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
import { CalendarListContainer } from "@components/apps/CalendarListContainer";
-const SkeletonLoader = () => {
- return (
-
-
-
-
-
-
+import AddCalendarButton from "~/settings/my-account/components/AddCalendarButton";
-
-
-
- );
-};
-
-const AddCalendarButton = () => {
- const { t } = useLocale();
-
- return (
- <>
-
- >
- );
-};
-
-const CalendarsView = () => {
+const Page = () => {
const { t } = useLocale();
return (
@@ -54,7 +25,7 @@ const CalendarsView = () => {
);
};
-CalendarsView.getLayout = getLayout;
-CalendarsView.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
-export default CalendarsView;
+export default Page;
diff --git a/apps/web/pages/settings/my-account/conferencing.tsx b/apps/web/pages/settings/my-account/conferencing.tsx
index 4fe4bb0d54..d4f749cff6 100644
--- a/apps/web/pages/settings/my-account/conferencing.tsx
+++ b/apps/web/pages/settings/my-account/conferencing.tsx
@@ -1,125 +1,36 @@
-"use client";
-
-import { useReducer } from "react";
-
-import DisconnectIntegrationModal from "@calcom/features/apps/components/DisconnectIntegrationModal";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { trpc } from "@calcom/trpc/react";
-import { Button, EmptyScreen, Meta, SkeletonContainer, SkeletonText } from "@calcom/ui";
-
-import { QueryCell } from "@lib/QueryCell";
+import { Button, Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-import { AppList } from "@components/apps/AppList";
-const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
- return (
-
-
-
-
-
-
-
- );
-};
+import ConferencingView from "~/settings/my-account/conferencing-view";
-const AddConferencingButton = () => {
+const Page = () => {
const { t } = useLocale();
- return (
-
- );
-};
-
-type ModalState = {
- isOpen: boolean;
- credentialId: null | number;
-};
-
-const ConferencingLayout = () => {
- const { t } = useLocale();
-
- const [modal, updateModal] = useReducer(
- (data: ModalState, partialData: Partial) => ({ ...data, ...partialData }),
- {
- isOpen: false,
- credentialId: null,
- }
- );
-
- const query = trpc.viewer.integrations.useQuery({
- variant: "conferencing",
- onlyInstalled: true,
- });
-
- const handleModelClose = () => {
- updateModal({ isOpen: false, credentialId: null });
- };
-
- const handleDisconnect = (credentialId: number) => {
- updateModal({ isOpen: true, credentialId });
+ const AddConferencingButton = () => {
+ return (
+
+ );
};
return (
<>
-
-
}
- borderInShellHeader={true}
- />
-
- }
- success={({ data }) => {
- console.log(data);
- if (!data.items.length) {
- return (
-
- {t("connect_conference_apps")}
-
- }
- />
- );
- }
- return (
-
- );
- }}
- />
-
- }
+ borderInShellHeader={true}
/>
+
>
);
};
-ConferencingLayout.getLayout = getLayout;
-ConferencingLayout.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
-export default ConferencingLayout;
+export default Page;
diff --git a/apps/web/pages/settings/my-account/general.tsx b/apps/web/pages/settings/my-account/general.tsx
index 9bb18622a9..37eb99532c 100644
--- a/apps/web/pages/settings/my-account/general.tsx
+++ b/apps/web/pages/settings/my-account/general.tsx
@@ -1,387 +1,23 @@
-"use client";
-
-import { useSession } from "next-auth/react";
-import { useState } from "react";
-import { Controller, useForm } from "react-hook-form";
-
-import SectionBottomActions from "@calcom/features/settings/SectionBottomActions";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
-import { classNames } from "@calcom/lib";
-import { formatLocalizedDateTime } from "@calcom/lib/date-fns";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { localeOptions } from "@calcom/lib/i18n";
-import { nameOfDay } from "@calcom/lib/weekday";
-import type { RouterOutputs } from "@calcom/trpc/react";
-import { trpc } from "@calcom/trpc/react";
-import {
- Button,
- Form,
- Label,
- Meta,
- Select,
- showToast,
- SkeletonButton,
- SkeletonContainer,
- SkeletonText,
- TimezoneSelect,
- SettingsToggle,
-} from "@calcom/ui";
+import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-import TravelScheduleModal from "@components/settings/TravelScheduleModal";
-export type FormValues = {
- locale: {
- value: string;
- label: string;
- };
- timeZone: string;
- timeFormat: {
- value: number;
- label: string | number;
- };
- weekStart: {
- value: string;
- label: string;
- };
- travelSchedules: {
- id?: number;
- startDate: Date;
- endDate?: Date;
- timeZone: string;
- }[];
-};
+import GeneralQueryView from "~/settings/my-account/general-view";
-const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
- return (
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-interface GeneralViewProps {
- localeProp: string;
- user: RouterOutputs["viewer"]["me"];
- travelSchedules: RouterOutputs["viewer"]["getTravelSchedules"];
-}
-
-const GeneralQueryView = () => {
+const Page = () => {
const { t } = useLocale();
- const { data: user, isPending } = trpc.viewer.me.useQuery();
-
- const { data: travelSchedules, isPending: isPendingTravelSchedules } =
- trpc.viewer.getTravelSchedules.useQuery();
-
- if (isPending || isPendingTravelSchedules)
- return ;
- if (!user) {
- throw new Error(t("something_went_wrong"));
- }
- return ;
-};
-
-const GeneralView = ({ localeProp, user, travelSchedules }: GeneralViewProps) => {
- const utils = trpc.useContext();
- const {
- t,
- i18n: { language },
- } = useLocale();
- const { update } = useSession();
- const [isUpdateBtnLoading, setIsUpdateBtnLoading] = useState(false);
- const [isTZScheduleOpen, setIsTZScheduleOpen] = useState(false);
-
- const mutation = trpc.viewer.updateProfile.useMutation({
- onSuccess: async (res) => {
- await utils.viewer.me.invalidate();
- reset(getValues());
- showToast(t("settings_updated_successfully"), "success");
- await update(res);
-
- if (res.locale) {
- window.calNewLocale = res.locale;
- }
- },
- onError: () => {
- showToast(t("error_updating_settings"), "error");
- },
- onSettled: async () => {
- await utils.viewer.me.invalidate();
- setIsUpdateBtnLoading(false);
- },
- });
-
- const timeFormatOptions = [
- { value: 12, label: t("12_hour") },
- { value: 24, label: t("24_hour") },
- ];
-
- const weekStartOptions = [
- { value: "Sunday", label: nameOfDay(localeProp, 0) },
- { value: "Monday", label: nameOfDay(localeProp, 1) },
- { value: "Tuesday", label: nameOfDay(localeProp, 2) },
- { value: "Wednesday", label: nameOfDay(localeProp, 3) },
- { value: "Thursday", label: nameOfDay(localeProp, 4) },
- { value: "Friday", label: nameOfDay(localeProp, 5) },
- { value: "Saturday", label: nameOfDay(localeProp, 6) },
- ];
-
- const formMethods = useForm({
- defaultValues: {
- locale: {
- value: localeProp || "",
- label: localeOptions.find((option) => option.value === localeProp)?.label || "",
- },
- timeZone: user.timeZone || "",
- timeFormat: {
- value: user.timeFormat || 12,
- label: timeFormatOptions.find((option) => option.value === user.timeFormat)?.label || 12,
- },
- weekStart: {
- value: user.weekStart,
- label: nameOfDay(localeProp, user.weekStart === "Sunday" ? 0 : 1),
- },
- travelSchedules:
- travelSchedules.map((schedule) => {
- return {
- id: schedule.id,
- startDate: schedule.startDate,
- endDate: schedule.endDate ?? undefined,
- timeZone: schedule.timeZone,
- };
- }) || [],
- },
- });
- const {
- formState: { isDirty, isSubmitting },
- reset,
- getValues,
- } = formMethods;
- const isDisabled = isSubmitting || !isDirty;
-
- const [isAllowDynamicBookingChecked, setIsAllowDynamicBookingChecked] = useState(
- !!user.allowDynamicBooking
- );
- const [isAllowSEOIndexingChecked, setIsAllowSEOIndexingChecked] = useState(!!user.allowSEOIndexing);
- const [isReceiveMonthlyDigestEmailChecked, setIsReceiveMonthlyDigestEmailChecked] = useState(
- !!user.receiveMonthlyDigestEmail
- );
-
- const watchedTzSchedules = formMethods.watch("travelSchedules");
-
return (
-
-
-
-
{
- setIsAllowDynamicBookingChecked(checked);
- mutation.mutate({ allowDynamicBooking: checked });
- }}
- switchContainerClassName="mt-6"
- />
-
- {
- setIsAllowSEOIndexingChecked(checked);
- mutation.mutate({ allowSEOIndexing: checked });
- }}
- switchContainerClassName="mt-6"
- />
-
- {
- setIsReceiveMonthlyDigestEmailChecked(checked);
- mutation.mutate({ receiveMonthlyDigestEmail: checked });
- }}
- switchContainerClassName="mt-6"
- />
- setIsTZScheduleOpen(false)}
- setValue={formMethods.setValue}
- existingSchedules={formMethods.getValues("travelSchedules") ?? []}
- />
-
+ <>
+
+
+ >
);
};
-GeneralQueryView.getLayout = getLayout;
-GeneralQueryView.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
-export default GeneralQueryView;
+export default Page;
diff --git a/apps/web/pages/settings/my-account/out-of-office/index.tsx b/apps/web/pages/settings/my-account/out-of-office/index.tsx
index f2b845c8f4..89dbb3fdb6 100644
--- a/apps/web/pages/settings/my-account/out-of-office/index.tsx
+++ b/apps/web/pages/settings/my-account/out-of-office/index.tsx
@@ -1,81 +1,31 @@
-import { useSearchParams } from "next/navigation";
-import { useEffect, useState } from "react";
-
-import { CreateOrEditOutOfOfficeEntryModal } from "@calcom/features/settings/CreateOrEditOutOfOfficeModal";
-import { OutOfOfficeEntriesList } from "@calcom/features/settings/OutOfOfficeEntriesList";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
+import CreateNewOutOfOfficeEntryButton from "@calcom/features/settings/outOfOffice/CreateNewOutOfOfficeEntryButton";
+import { OutOfOfficeEntriesList } from "@calcom/features/settings/outOfOffice/OutOfOfficeEntriesList";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
-import { Button, Icon, Meta, SkeletonText } from "@calcom/ui";
+import { Meta, SkeletonText } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-export type BookingRedirectForm = {
- dateRange: { startDate: Date; endDate: Date };
- offset: number;
- toTeamUserId: number | null;
- reasonId: number;
- notes?: string;
- uuid?: string | null;
-};
-
-const OutOfOfficePage = () => {
+const Page = () => {
const { t } = useLocale();
- const params = useSearchParams();
- const openModalOnStart = !!params?.get("open");
- useEffect(() => {
- if (openModalOnStart) {
- setOpenModal(true);
- }
- }, [openModalOnStart]);
-
- const [openModal, setOpenModal] = useState(false);
- const [currentlyEditingOutOfOfficeEntry, setCurrentlyEditingOutOfOfficeEntry] =
- useState(null);
-
- const editOutOfOfficeEntry = (entry: BookingRedirectForm) => {
- setCurrentlyEditingOutOfOfficeEntry(entry);
- setOpenModal(true);
- };
-
const { isPending } = trpc.viewer.outOfOfficeReasonList.useQuery();
+
return (
<>
- ) : (
-
- )
- }
+ CTA={isPending ? : }
/>
- {openModal && (
- {
- setOpenModal(false);
- setCurrentlyEditingOutOfOfficeEntry(null);
- }}
- currentlyEditingOutOfOfficeEntry={currentlyEditingOutOfOfficeEntry}
- />
- )}
-
+
>
);
};
-OutOfOfficePage.getLayout = getLayout;
-OutOfOfficePage.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
-export default OutOfOfficePage;
+export default Page;
diff --git a/apps/web/pages/settings/my-account/profile.tsx b/apps/web/pages/settings/my-account/profile.tsx
index 1e17681c78..9539cec4b4 100644
--- a/apps/web/pages/settings/my-account/profile.tsx
+++ b/apps/web/pages/settings/my-account/profile.tsx
@@ -1,278 +1,14 @@
-"use client";
-
-import { zodResolver } from "@hookform/resolvers/zod";
-// eslint-disable-next-line no-restricted-imports
-import { get, pick } from "lodash";
-import { signOut, useSession } from "next-auth/react";
-import type { BaseSyntheticEvent } from "react";
-import React, { useRef, useState } from "react";
-import { Controller, useFieldArray, useForm } from "react-hook-form";
-import { z } from "zod";
-
-import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode";
-import SectionBottomActions from "@calcom/features/settings/SectionBottomActions";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
-import { APP_NAME, FULL_NAME_LENGTH_MAX_LIMIT } from "@calcom/lib/constants";
-import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
+import { APP_NAME } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { md } from "@calcom/lib/markdownIt";
-import turndown from "@calcom/lib/turndownService";
-import { IdentityProvider } from "@calcom/prisma/enums";
-import type { TRPCClientErrorLike } from "@calcom/trpc/client";
-import type { RouterOutputs } from "@calcom/trpc/react";
-import { trpc } from "@calcom/trpc/react";
-import type { AppRouter } from "@calcom/trpc/server/routers/_app";
-import {
- Alert,
- Button,
- Dialog,
- DialogClose,
- DialogContent,
- DialogFooter,
- DialogTrigger,
- Editor,
- Form,
- ImageUploader,
- Label,
- Meta,
- PasswordField,
- showToast,
- SkeletonAvatar,
- SkeletonButton,
- SkeletonContainer,
- SkeletonText,
- TextField,
- UserAvatar,
-} from "@calcom/ui";
+import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-import TwoFactor from "@components/auth/TwoFactor";
-import CustomEmailTextField from "@components/settings/CustomEmailTextField";
-import SecondaryEmailConfirmModal from "@components/settings/SecondaryEmailConfirmModal";
-import SecondaryEmailModal from "@components/settings/SecondaryEmailModal";
-import { UsernameAvailabilityField } from "@components/ui/UsernameAvailability";
-const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
- return (
-
-
-
-
-
-
-
-
-
-
+import ProfileView from "~/settings/my-account/profile-view";
-
-
-
- );
-};
-
-interface DeleteAccountValues {
- totpCode: string;
-}
-
-type Email = {
- id: number;
- email: string;
- emailVerified: string | null;
- emailPrimary: boolean;
-};
-
-export type FormValues = {
- username: string;
- avatarUrl: string | null;
- name: string;
- email: string;
- bio: string;
- secondaryEmails: Email[];
-};
-
-const ProfileView = () => {
+const Page = () => {
const { t } = useLocale();
- const utils = trpc.useUtils();
- const { update } = useSession();
- const { data: user, isPending } = trpc.viewer.me.useQuery({ includePasswordAdded: true });
-
- const updateProfileMutation = trpc.viewer.updateProfile.useMutation({
- onSuccess: async (res) => {
- await update(res);
- utils.viewer.me.invalidate();
- utils.viewer.shouldVerifyEmail.invalidate();
-
- if (res.hasEmailBeenChanged && res.sendEmailVerification) {
- showToast(t("change_of_email_toast", { email: tempFormValues?.email }), "success");
- } else {
- showToast(t("settings_updated_successfully"), "success");
- }
-
- setTempFormValues(null);
- },
- onError: (e) => {
- switch (e.message) {
- // TODO: Add error codes.
- case "email_already_used":
- {
- showToast(t(e.message), "error");
- }
- return;
- default:
- showToast(t("error_updating_settings"), "error");
- }
- },
- });
- const unlinkConnectedAccountMutation = trpc.viewer.unlinkConnectedAccount.useMutation({
- onSuccess: async (res) => {
- showToast(t(res.message), "success");
- utils.viewer.me.invalidate();
- },
- onError: (e) => {
- showToast(t(e.message), "error");
- },
- });
-
- const addSecondaryEmailMutation = trpc.viewer.addSecondaryEmail.useMutation({
- onSuccess: (res) => {
- setShowSecondaryEmailModalOpen(false);
- setNewlyAddedSecondaryEmail(res?.data?.email);
- utils.viewer.me.invalidate();
- },
- onError: (error) => {
- setSecondaryEmailAddErrorMessage(error?.message || "");
- },
- });
-
- const resendVerifyEmailMutation = trpc.viewer.auth.resendVerifyEmail.useMutation();
-
- const [confirmPasswordOpen, setConfirmPasswordOpen] = useState(false);
- const [tempFormValues, setTempFormValues] = useState(null);
- const [confirmPasswordErrorMessage, setConfirmPasswordDeleteErrorMessage] = useState("");
- const [showCreateAccountPasswordDialog, setShowCreateAccountPasswordDialog] = useState(false);
- const [showAccountDisconnectWarning, setShowAccountDisconnectWarning] = useState(false);
- const [showSecondaryEmailModalOpen, setShowSecondaryEmailModalOpen] = useState(false);
- const [secondaryEmailAddErrorMessage, setSecondaryEmailAddErrorMessage] = useState("");
- const [newlyAddedSecondaryEmail, setNewlyAddedSecondaryEmail] = useState(undefined);
-
- const [deleteAccountOpen, setDeleteAccountOpen] = useState(false);
- const [hasDeleteErrors, setHasDeleteErrors] = useState(false);
- const [deleteErrorMessage, setDeleteErrorMessage] = useState("");
- const form = useForm();
-
- const onDeleteMeSuccessMutation = async () => {
- await utils.viewer.me.invalidate();
- showToast(t("Your account was deleted"), "success");
-
- setHasDeleteErrors(false); // dismiss any open errors
- if (process.env.NEXT_PUBLIC_WEBAPP_URL === "https://app.cal.com") {
- signOut({ callbackUrl: "/auth/logout?survey=true" });
- } else {
- signOut({ callbackUrl: "/auth/logout" });
- }
- };
-
- const confirmPasswordMutation = trpc.viewer.auth.verifyPassword.useMutation({
- onSuccess() {
- if (tempFormValues) updateProfileMutation.mutate(tempFormValues);
- setConfirmPasswordOpen(false);
- },
- onError() {
- setConfirmPasswordDeleteErrorMessage(t("incorrect_password"));
- },
- });
-
- const onDeleteMeErrorMutation = (error: TRPCClientErrorLike) => {
- setHasDeleteErrors(true);
- setDeleteErrorMessage(errorMessages[error.message]);
- };
- const deleteMeMutation = trpc.viewer.deleteMe.useMutation({
- onSuccess: onDeleteMeSuccessMutation,
- onError: onDeleteMeErrorMutation,
- async onSettled() {
- await utils.viewer.me.invalidate();
- },
- });
- const deleteMeWithoutPasswordMutation = trpc.viewer.deleteMeWithoutPassword.useMutation({
- onSuccess: onDeleteMeSuccessMutation,
- onError: onDeleteMeErrorMutation,
- async onSettled() {
- await utils.viewer.me.invalidate();
- },
- });
-
- const isCALIdentityProvider = user?.identityProvider === IdentityProvider.CAL;
-
- const onConfirmPassword = (e: Event | React.MouseEvent) => {
- e.preventDefault();
-
- const password = passwordRef.current.value;
- confirmPasswordMutation.mutate({ passwordInput: password });
- };
-
- const onConfirmButton = (e: Event | React.MouseEvent) => {
- e.preventDefault();
- if (isCALIdentityProvider) {
- const totpCode = form.getValues("totpCode");
- const password = passwordRef.current.value;
- deleteMeMutation.mutate({ password, totpCode });
- } else {
- deleteMeWithoutPasswordMutation.mutate();
- }
- };
-
- const onConfirm = ({ totpCode }: DeleteAccountValues, e: BaseSyntheticEvent | undefined) => {
- e?.preventDefault();
- if (isCALIdentityProvider) {
- const password = passwordRef.current.value;
- deleteMeMutation.mutate({ password, totpCode });
- } else {
- deleteMeWithoutPasswordMutation.mutate();
- }
- };
-
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const passwordRef = useRef(null!);
-
- const errorMessages: { [key: string]: string } = {
- [ErrorCode.SecondFactorRequired]: t("2fa_enabled_instructions"),
- [ErrorCode.IncorrectPassword]: `${t("incorrect_password")} ${t("please_try_again")}`,
- [ErrorCode.UserNotFound]: t("no_account_exists"),
- [ErrorCode.IncorrectTwoFactorCode]: `${t("incorrect_2fa_code")} ${t("please_try_again")}`,
- [ErrorCode.InternalServerError]: `${t("something_went_wrong")} ${t("please_try_again_and_contact_us")}`,
- [ErrorCode.ThirdPartyIdentityProviderEnabled]: t("account_created_with_identity_provider"),
- };
-
- if (isPending || !user) {
- return (
-
- );
- }
-
- const userEmail = user.email || "";
- const defaultValues = {
- username: user.username || "",
- avatarUrl: user.avatarUrl,
- name: user.name || "",
- email: userEmail,
- bio: user.bio || "",
- // We add the primary email as the first item in the list
- secondaryEmails: [
- {
- id: 0,
- email: userEmail,
- emailVerified: user.emailVerified?.toString() || null,
- emailPrimary: true,
- },
- ...(user.secondaryEmails || []).map((secondaryEmail) => ({
- ...secondaryEmail,
- emailVerified: secondaryEmail.emailVerified?.toString() || null,
- emailPrimary: false,
- })),
- ],
- };
-
return (
<>
{
description={t("profile_description", { appName: APP_NAME })}
borderInShellHeader={true}
/>
- {
- if (values.email !== user.email && isCALIdentityProvider) {
- setTempFormValues(values);
- setConfirmPasswordOpen(true);
- } else {
- updateProfileMutation.mutate(values);
- }
- }}
- handleAddSecondaryEmail={() => setShowSecondaryEmailModalOpen(true)}
- handleResendVerifyEmail={(email) => {
- resendVerifyEmailMutation.mutate({ email });
- showToast(t("email_sent"), "success");
- }}
- handleAccountDisconnect={(values) => {
- if (isCALIdentityProvider) return;
- if (user?.passwordAdded) {
- setTempFormValues(values);
- setShowAccountDisconnectWarning(true);
- return;
- }
- setShowCreateAccountPasswordDialog(true);
- }}
- extraField={
-
- {
- showToast(t("settings_updated_successfully"), "success");
- await utils.viewer.me.invalidate();
- }}
- onErrorMutation={() => {
- showToast(t("error_updating_settings"), "error");
- }}
- />
-
- }
- isCALIdentityProvider={isCALIdentityProvider}
- />
-
-
-
-
{t("account_deletion_cannot_be_undone")}
-
- {/* Delete account Dialog */}
-
-
- {/* If changing email, confirm password */}
-
-
-
-
-
-
- {showSecondaryEmailModalOpen && (
- {
- setSecondaryEmailAddErrorMessage("");
- addSecondaryEmailMutation.mutate(values);
- }}
- onCancel={() => {
- setSecondaryEmailAddErrorMessage("");
- setShowSecondaryEmailModalOpen(false);
- }}
- clearErrorMessage={() => {
- addSecondaryEmailMutation.reset();
- setSecondaryEmailAddErrorMessage("");
- }}
- />
- )}
- {!!newlyAddedSecondaryEmail && (
- setNewlyAddedSecondaryEmail(undefined)}
- />
- )}
+
>
);
};
-type SecondaryEmailApiPayload = {
- id: number;
- email: string;
- isDeleted: boolean;
-};
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
-type ExtendedFormValues = Omit & {
- secondaryEmails: SecondaryEmailApiPayload[];
-};
-
-const ProfileForm = ({
- defaultValues,
- onSubmit,
- handleAddSecondaryEmail,
- handleResendVerifyEmail,
- handleAccountDisconnect,
- extraField,
- isPending = false,
- isFallbackImg,
- user,
- userOrganization,
- isCALIdentityProvider,
-}: {
- defaultValues: FormValues;
- onSubmit: (values: ExtendedFormValues) => void;
- handleAddSecondaryEmail: () => void;
- handleResendVerifyEmail: (email: string) => void;
- handleAccountDisconnect: (values: ExtendedFormValues) => void;
- extraField?: React.ReactNode;
- isPending: boolean;
- isFallbackImg: boolean;
- user: RouterOutputs["viewer"]["me"];
- userOrganization: RouterOutputs["viewer"]["me"]["organization"];
- isCALIdentityProvider: boolean;
-}) => {
- const { t } = useLocale();
- const [firstRender, setFirstRender] = useState(true);
-
- const profileFormSchema = z.object({
- username: z.string(),
- avatarUrl: z.string().nullable(),
- name: z
- .string()
- .trim()
- .min(1, t("you_need_to_add_a_name"))
- .max(FULL_NAME_LENGTH_MAX_LIMIT, {
- message: t("max_limit_allowed_hint", { limit: FULL_NAME_LENGTH_MAX_LIMIT }),
- }),
- email: z.string().email(),
- bio: z.string(),
- secondaryEmails: z.array(
- z.object({
- id: z.number(),
- email: z.string().email(),
- emailVerified: z.union([z.string(), z.null()]).optional(),
- emailPrimary: z.boolean().optional(),
- })
- ),
- });
-
- const formMethods = useForm({
- defaultValues,
- resolver: zodResolver(profileFormSchema),
- });
-
- const {
- fields: secondaryEmailFields,
- remove: deleteSecondaryEmail,
- replace: updateAllSecondaryEmailFields,
- } = useFieldArray({
- control: formMethods.control,
- name: "secondaryEmails",
- keyName: "itemId",
- });
-
- const getUpdatedFormValues = (values: FormValues) => {
- const changedFields = formMethods.formState.dirtyFields?.secondaryEmails || [];
- const updatedValues: FormValues = {
- ...values,
- };
-
- // If the primary email is changed, we will need to update
- const primaryEmailIndex = updatedValues.secondaryEmails.findIndex(
- (secondaryEmail) => secondaryEmail.emailPrimary
- );
- if (primaryEmailIndex >= 0) {
- // Add the new updated value as primary email
- updatedValues.email = updatedValues.secondaryEmails[primaryEmailIndex].email;
- }
-
- // We will only send the emails which have already changed
- const updatedEmails: Email[] = [];
- changedFields.map((field, index) => {
- // If the email changed and if its only secondary email, we add it for updation, the first
- // item in the list is always primary email
- if (field?.email && updatedValues.secondaryEmails[index]?.id) {
- updatedEmails.push(updatedValues.secondaryEmails[index]);
- }
- });
-
- const deletedEmails = (user?.secondaryEmails || []).filter(
- (secondaryEmail) => !updatedValues.secondaryEmails.find((val) => val.id && val.id === secondaryEmail.id)
- );
- const secondaryEmails = [
- ...updatedEmails.map((email) => ({ ...email, isDeleted: false })),
- ...deletedEmails.map((email) => ({ ...email, isDeleted: true })),
- ].map((secondaryEmail) => pick(secondaryEmail, ["id", "email", "isDeleted"]));
-
- return {
- ...updatedValues,
- secondaryEmails,
- };
- };
-
- const handleFormSubmit = (values: FormValues) => {
- onSubmit(getUpdatedFormValues(values));
- };
-
- const onDisconnect = () => {
- handleAccountDisconnect(getUpdatedFormValues(formMethods.getValues()));
- };
-
- const {
- formState: { isSubmitting, isDirty },
- } = formMethods;
-
- const isDisabled = isSubmitting || !isDirty;
- return (
-
- );
-};
-
-ProfileView.getLayout = getLayout;
-ProfileView.PageWrapper = PageWrapper;
-
-export default ProfileView;
+export default Page;
diff --git a/apps/web/pages/settings/organizations/admin-api.tsx b/apps/web/pages/settings/organizations/admin-api.tsx
index 1b59b2a7e5..08a766232d 100644
--- a/apps/web/pages/settings/organizations/admin-api.tsx
+++ b/apps/web/pages/settings/organizations/admin-api.tsx
@@ -1,9 +1,25 @@
import AdminAPIView from "@calcom/features/ee/organizations/pages/settings/admin-api";
+import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { Meta } from "@calcom/ui";
-import type { CalPageWrapper } from "@components/PageWrapper";
import PageWrapper from "@components/PageWrapper";
-const Page = AdminAPIView as CalPageWrapper;
+const Page = () => {
+ const { t } = useLocale();
+ return (
+ <>
+
+
+ >
+ );
+};
+
Page.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
export default Page;
diff --git a/apps/web/pages/settings/security/impersonation.tsx b/apps/web/pages/settings/security/impersonation.tsx
index b38d09bf4a..b8a365fdb1 100644
--- a/apps/web/pages/settings/security/impersonation.tsx
+++ b/apps/web/pages/settings/security/impersonation.tsx
@@ -1,57 +1,13 @@
-"use client";
-
-import { useState } from "react";
-
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { trpc } from "@calcom/trpc/react";
-import type { RouterOutputs } from "@calcom/trpc/react";
-import { Meta, showToast, SettingsToggle, SkeletonContainer, SkeletonText } from "@calcom/ui";
+import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
- return (
-
-
-
-
-
-
- );
-};
+import ProfileImpersonationViewWrapper from "~/settings/security/impersonation-view";
-const ProfileImpersonationView = ({ user }: { user: RouterOutputs["viewer"]["me"] }) => {
+const Page = () => {
const { t } = useLocale();
- const utils = trpc.useUtils();
- const [disableImpersonation, setDisableImpersonation] = useState(
- user?.disableImpersonation
- );
-
- const mutation = trpc.viewer.updateProfile.useMutation({
- onSuccess: () => {
- showToast(t("profile_updated_successfully"), "success");
- },
- onSettled: () => {
- utils.viewer.me.invalidate();
- },
- onMutate: async ({ disableImpersonation }) => {
- await utils.viewer.me.cancel();
- const previousValue = utils.viewer.me.getData();
-
- setDisableImpersonation(disableImpersonation);
-
- return { previousValue };
- },
- onError: (error, variables, context) => {
- if (context?.previousValue) {
- utils.viewer.me.setData(undefined, context.previousValue);
- setDisableImpersonation(context.previousValue?.disableImpersonation);
- }
- showToast(`${t("error")}, ${error.message}`, "error");
- },
- });
-
return (
<>
-
- {
- mutation.mutate({ disableImpersonation: !checked });
- }}
- switchContainerClassName="rounded-t-none border-t-0"
- disabled={mutation.isPending}
- />
-
+
>
);
};
-const ProfileImpersonationViewWrapper = () => {
- const { data: user, isPending } = trpc.viewer.me.useQuery();
- const { t } = useLocale();
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
- if (isPending || !user)
- return ;
-
- return ;
-};
-
-ProfileImpersonationViewWrapper.getLayout = getLayout;
-ProfileImpersonationViewWrapper.PageWrapper = PageWrapper;
-
-export default ProfileImpersonationViewWrapper;
+export default Page;
diff --git a/apps/web/pages/settings/security/password.tsx b/apps/web/pages/settings/security/password.tsx
index 48f99f3f48..ca051d7bae 100644
--- a/apps/web/pages/settings/security/password.tsx
+++ b/apps/web/pages/settings/security/password.tsx
@@ -1,325 +1,23 @@
-"use client";
-
-import { signOut, useSession } from "next-auth/react";
-import { useState } from "react";
-import { useForm } from "react-hook-form";
-
-import { identityProviderNameMap } from "@calcom/features/auth/lib/identityProviderNameMap";
-import SectionBottomActions from "@calcom/features/settings/SectionBottomActions";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
-import { classNames } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { IdentityProvider } from "@calcom/prisma/enums";
-import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils";
-import type { RouterOutputs } from "@calcom/trpc/react";
-import { trpc } from "@calcom/trpc/react";
-import {
- Alert,
- Button,
- Form,
- Meta,
- PasswordField,
- Select,
- SettingsToggle,
- showToast,
- SkeletonButton,
- SkeletonContainer,
- SkeletonText,
-} from "@calcom/ui";
+import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-type ChangePasswordSessionFormValues = {
- oldPassword: string;
- newPassword: string;
- sessionTimeout?: number;
- apiError: string;
-};
+import PasswordViewWrapper from "~/settings/security/password-view";
-interface PasswordViewProps {
- user: RouterOutputs["viewer"]["me"];
-}
-
-const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-const PasswordView = ({ user }: PasswordViewProps) => {
- const { data } = useSession();
+const Page = () => {
const { t } = useLocale();
- const utils = trpc.useUtils();
- const metadata = userMetadataSchema.safeParse(user?.metadata);
- const initialSessionTimeout = metadata.success ? metadata.data?.sessionTimeout : undefined;
-
- const [sessionTimeout, setSessionTimeout] = useState(initialSessionTimeout);
-
- const sessionMutation = trpc.viewer.updateProfile.useMutation({
- onSuccess: (data) => {
- showToast(t("session_timeout_changed"), "success");
- formMethods.reset(formMethods.getValues());
- setSessionTimeout(data.metadata?.sessionTimeout);
- },
- onSettled: () => {
- utils.viewer.me.invalidate();
- },
- onMutate: async () => {
- await utils.viewer.me.cancel();
- const previousValue = await utils.viewer.me.getData();
- const previousMetadata = userMetadataSchema.safeParse(previousValue?.metadata);
-
- if (previousValue && sessionTimeout && previousMetadata.success) {
- utils.viewer.me.setData(undefined, {
- ...previousValue,
- metadata: { ...previousMetadata?.data, sessionTimeout: sessionTimeout },
- });
- return { previousValue };
- }
- },
- onError: (error, _, context) => {
- if (context?.previousValue) {
- utils.viewer.me.setData(undefined, context.previousValue);
- }
- showToast(`${t("session_timeout_change_error")}, ${error.message}`, "error");
- },
- });
- const passwordMutation = trpc.viewer.auth.changePassword.useMutation({
- onSuccess: () => {
- showToast(t("password_has_been_changed"), "success");
- formMethods.resetField("oldPassword");
- formMethods.resetField("newPassword");
-
- if (data?.user.role === "INACTIVE_ADMIN") {
- /*
- AdminPasswordBanner component relies on the role returned from the session.
- Next-Auth doesn't provide a way to revalidate the session cookie,
- so this a workaround to hide the banner after updating the password.
- discussion: https://github.com/nextauthjs/next-auth/discussions/4229
- */
- signOut({ callbackUrl: "/auth/login" });
- }
- },
- onError: (error) => {
- showToast(`${t("error_updating_password")}, ${t(error.message)}`, "error");
-
- formMethods.setError("apiError", {
- message: t(error.message),
- type: "custom",
- });
- },
- });
-
- const createAccountPasswordMutation = trpc.viewer.auth.createAccountPassword.useMutation({
- onSuccess: () => {
- showToast(t("password_reset_email", { email: user.email }), "success");
- },
- onError: (error) => {
- showToast(`${t("error_creating_account_password")}, ${t(error.message)}`, "error");
- },
- });
-
- const formMethods = useForm({
- defaultValues: {
- oldPassword: "",
- newPassword: "",
- },
- });
-
- const handleSubmit = (values: ChangePasswordSessionFormValues) => {
- const { oldPassword, newPassword } = values;
-
- if (!oldPassword.length) {
- formMethods.setError(
- "oldPassword",
- { type: "required", message: t("error_required_field") },
- { shouldFocus: true }
- );
- }
-
- if (!newPassword.length) {
- formMethods.setError(
- "newPassword",
- { type: "required", message: t("error_required_field") },
- { shouldFocus: true }
- );
- }
-
- if (oldPassword && newPassword) {
- passwordMutation.mutate({ oldPassword, newPassword });
- }
- };
-
- const timeoutOptions = [5, 10, 15].map((mins) => ({
- label: t("multiple_duration_mins", { count: mins }),
- value: mins,
- }));
-
- const isDisabled = formMethods.formState.isSubmitting || !formMethods.formState.isDirty;
-
- const passwordMinLength = data?.user.role === "USER" ? 7 : 15;
- const isUser = data?.user.role === "USER";
return (
<>
- {user && user.identityProvider !== IdentityProvider.CAL && !user.passwordAdded ? (
-
-
-
- {t("account_managed_by_identity_provider", {
- provider: identityProviderNameMap[user.identityProvider],
- })}
-
-
-
- {t("account_managed_by_identity_provider_description", {
- provider: identityProviderNameMap[user.identityProvider],
- })}
-
-
-
-
- ) : (
-
- )}
+
>
);
};
-const PasswordViewWrapper = () => {
- const { data: user, isPending } = trpc.viewer.me.useQuery({ includePasswordAdded: true });
- const { t } = useLocale();
- if (isPending || !user)
- return ;
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
- return ;
-};
-
-PasswordViewWrapper.getLayout = getLayout;
-PasswordViewWrapper.PageWrapper = PageWrapper;
-
-export default PasswordViewWrapper;
+export default Page;
diff --git a/apps/web/pages/settings/security/sso.tsx b/apps/web/pages/settings/security/sso.tsx
index e20f72e7cd..93cfa3bfda 100644
--- a/apps/web/pages/settings/security/sso.tsx
+++ b/apps/web/pages/settings/security/sso.tsx
@@ -1,9 +1,25 @@
import UserSSOView from "@calcom/features/ee/sso/page/user-sso-view";
+import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { Meta } from "@calcom/ui";
-import type { CalPageWrapper } from "@components/PageWrapper";
import PageWrapper from "@components/PageWrapper";
-const Page = UserSSOView as CalPageWrapper;
+const Page = () => {
+ const { t } = useLocale();
+
+ return (
+ <>
+
+
+ >
+ );
+};
Page.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
export default Page;
diff --git a/apps/web/pages/settings/security/two-factor-auth.tsx b/apps/web/pages/settings/security/two-factor-auth.tsx
index 30c4adb0cf..d098f2b272 100644
--- a/apps/web/pages/settings/security/two-factor-auth.tsx
+++ b/apps/web/pages/settings/security/two-factor-auth.tsx
@@ -1,102 +1,22 @@
-"use client";
-
-import { useState } from "react";
-
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { trpc } from "@calcom/trpc/react";
-import {
- Badge,
- Meta,
- SkeletonButton,
- SkeletonContainer,
- SkeletonText,
- Alert,
- SettingsToggle,
-} from "@calcom/ui";
+import { Meta } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-import DisableTwoFactorModal from "@components/settings/DisableTwoFactorModal";
-import EnableTwoFactorModal from "@components/settings/EnableTwoFactorModal";
-const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
- return (
-
-
-
-
- );
-};
-
-const TwoFactorAuthView = () => {
- const utils = trpc.useUtils();
+import TwoFactorAuthView from "~/settings/security/two-factor-auth-view";
+const Page = () => {
const { t } = useLocale();
- const { data: user, isPending } = trpc.viewer.me.useQuery({ includePasswordAdded: true });
-
- const [enableModalOpen, setEnableModalOpen] = useState(false);
- const [disableModalOpen, setDisableModalOpen] = useState(false);
-
- if (isPending)
- return ;
-
- const isCalProvider = user?.identityProvider === "CAL";
- const canSetupTwoFactor = !isCalProvider && !user?.twoFactorEnabled && !user?.passwordAdded;
return (
<>
- {canSetupTwoFactor && }
-
- user?.twoFactorEnabled ? setDisableModalOpen(true) : setEnableModalOpen(true)
- }
- Badge={
-
- {user?.twoFactorEnabled ? t("enabled") : t("disabled")}
-
- }
- switchContainerClassName="rounded-t-none border-t-0"
- />
-
- setEnableModalOpen(!enableModalOpen)}
- onEnable={() => {
- setEnableModalOpen(false);
- utils.viewer.me.invalidate();
- }}
- onCancel={() => {
- setEnableModalOpen(false);
- }}
- />
-
- setDisableModalOpen(!disableModalOpen)}
- onDisable={() => {
- setDisableModalOpen(false);
- utils.viewer.me.invalidate();
- }}
- onCancel={() => {
- setDisableModalOpen(false);
- }}
- />
+
>
);
};
-TwoFactorAuthView.getLayout = getLayout;
-TwoFactorAuthView.PageWrapper = PageWrapper;
+Page.getLayout = getLayout;
+Page.PageWrapper = PageWrapper;
-export default TwoFactorAuthView;
+export default Page;
diff --git a/packages/features/apps/AdminAppsList.tsx b/packages/features/apps/AdminAppsList.tsx
index b40c1ec91e..34749b1cd6 100644
--- a/packages/features/apps/AdminAppsList.tsx
+++ b/packages/features/apps/AdminAppsList.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { zodResolver } from "@hookform/resolvers/zod";
// eslint-disable-next-line no-restricted-imports
import { noop } from "lodash";
diff --git a/packages/features/ee/common/components/LicenseRequired.tsx b/packages/features/ee/common/components/LicenseRequired.tsx
index e48af64963..4b8d23c73d 100644
--- a/packages/features/ee/common/components/LicenseRequired.tsx
+++ b/packages/features/ee/common/components/LicenseRequired.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { useSession } from "next-auth/react";
import type { AriaRole, ComponentType } from "react";
import React, { Fragment, useEffect } from "react";
@@ -70,9 +72,11 @@ export const withLicenseRequired =
// eslint-disable-next-line react/display-name
(hocProps: T) =>
(
-
-
-
+
+
+
+
+
);
export default LicenseRequired;
diff --git a/packages/features/ee/organizations/pages/settings/admin-api.tsx b/packages/features/ee/organizations/pages/settings/admin-api.tsx
index 6264d1506d..9c776bb06d 100644
--- a/packages/features/ee/organizations/pages/settings/admin-api.tsx
+++ b/packages/features/ee/organizations/pages/settings/admin-api.tsx
@@ -1,12 +1,11 @@
"use client";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
-import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import { UpgradeTip } from "@calcom/features/tips";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { Button, Icon, Meta, ButtonGroup } from "@calcom/ui";
+import { Button, Icon, ButtonGroup } from "@calcom/ui";
-const AdminAPIViewWrapper = () => {
+export const AdminAPIView = () => {
const { t } = useLocale();
const features = [
@@ -28,11 +27,6 @@ const AdminAPIViewWrapper = () => {
];
return (
-
{
);
};
-AdminAPIViewWrapper.getLayout = getLayout;
-
-export default AdminAPIViewWrapper;
+export default AdminAPIView;
diff --git a/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx b/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx
index 3a22b2af31..6ead4803ae 100644
--- a/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx
+++ b/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx
@@ -1,44 +1,14 @@
+"use client";
+
import type { Team } from "@prisma/client";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
-import { z } from "zod";
+import type { z } from "zod";
-import NoSSR from "@calcom/core/components/NoSSR";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback";
import type { orgSettingsSchema } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
-import { Button, Form, Meta, TextField, showToast } from "@calcom/ui";
-
-import { getLayout } from "../../../../../settings/layouts/SettingsLayout";
-import LicenseRequired from "../../../../common/components/LicenseRequired";
-
-const paramsSchema = z.object({ id: z.coerce.number() });
-
-const OrgEditPage = () => {
- const params = useParamsWithFallback();
- const parsedParams = paramsSchema.safeParse(params);
-
- if (!parsedParams.success) return Invalid id
;
-
- return ;
-};
-
-const OrgEditView = ({ orgId }: { orgId: number }) => {
- const [org] = trpc.viewer.organizations.adminGet.useSuspenseQuery({ id: orgId });
-
- return (
-
-
-
-
-
-
- );
-};
+import { Button, Form, TextField, showToast } from "@calcom/ui";
type FormValues = {
name: Team["name"];
@@ -46,7 +16,7 @@ type FormValues = {
organizationSettings: z.infer;
};
-const OrgForm = ({
+export const OrgForm = ({
org,
}: {
org: FormValues & {
@@ -108,6 +78,4 @@ const OrgForm = ({
);
};
-OrgEditPage.getLayout = getLayout;
-
-export default OrgEditPage;
+export default OrgForm;
diff --git a/packages/features/ee/organizations/pages/settings/admin/AdminOrgPage.tsx b/packages/features/ee/organizations/pages/settings/admin/AdminOrgPage.tsx
index 0e42e9230b..d931de362a 100644
--- a/packages/features/ee/organizations/pages/settings/admin/AdminOrgPage.tsx
+++ b/packages/features/ee/organizations/pages/settings/admin/AdminOrgPage.tsx
@@ -3,39 +3,18 @@
import { Trans } from "next-i18next";
import { useState } from "react";
-import NoSSR from "@calcom/core/components/NoSSR";
-import LicenseRequired from "@calcom/ee/common/components/LicenseRequired";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
-import {
- Badge,
- ConfirmationDialogContent,
- Dialog,
- DropdownActions,
- Meta,
- showToast,
- Table,
-} from "@calcom/ui";
+import { Badge, ConfirmationDialogContent, Dialog, DropdownActions, showToast, Table } from "@calcom/ui";
-import { getLayout } from "../../../../../settings/layouts/SettingsLayout";
import { subdomainSuffix } from "../../../../organizations/lib/orgDomains";
const { Body, Cell, ColumnTitle, Header, Row } = Table;
-function AdminOrgTable() {
+export function AdminOrgTable() {
const { t } = useLocale();
const utils = trpc.useUtils();
const [data] = trpc.viewer.organizations.adminGetAll.useSuspenseQuery();
- const verifyMutation = trpc.viewer.organizations.adminVerify.useMutation({
- onSuccess: async (_data, variables) => {
- showToast(t("org_has_been_processed"), "success");
- await invalidateQueries(utils, variables);
- },
- onError: (err) => {
- console.error(err.message);
- showToast(t("org_error_processing"), "error");
- },
- });
const updateMutation = trpc.viewer.organizations.adminUpdate.useMutation({
onSuccess: async (_data, variables) => {
showToast(t("org_has_been_processed"), "success");
@@ -240,21 +219,7 @@ function AdminOrgTable() {
);
}
-const AdminOrgList = () => {
- const { t } = useLocale();
- return (
-
-
-
-
-
-
- );
-};
-
-AdminOrgList.getLayout = getLayout;
-
-export default AdminOrgList;
+export default AdminOrgTable;
const DeleteOrgDialog = ({
org,
diff --git a/packages/features/ee/sso/page/user-sso-view.tsx b/packages/features/ee/sso/page/user-sso-view.tsx
index 84b3d6bf8c..11566247ec 100644
--- a/packages/features/ee/sso/page/user-sso-view.tsx
+++ b/packages/features/ee/sso/page/user-sso-view.tsx
@@ -5,9 +5,7 @@ import { useEffect } from "react";
import { HOSTED_CAL_FEATURES } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { Meta } from "@calcom/ui";
-import { getLayout } from "../../../settings/layouts/SettingsLayout";
import SSOConfiguration from "../components/SSOConfiguration";
const SAMLSSO = () => {
@@ -22,16 +20,9 @@ const SAMLSSO = () => {
return (
-
);
};
-SAMLSSO.getLayout = getLayout;
-
export default SAMLSSO;
diff --git a/packages/features/ee/users/components/UsersTable.tsx b/packages/features/ee/users/components/UsersTable.tsx
index fc8bc52545..291d95008f 100644
--- a/packages/features/ee/users/components/UsersTable.tsx
+++ b/packages/features/ee/users/components/UsersTable.tsx
@@ -150,7 +150,7 @@ function UsersTableBare() {
const [userToDelete, setUserToDelete] = useState(null);
return (
- <>
+
)}
- >
+
);
}
diff --git a/packages/features/ee/users/pages/users-add-view.tsx b/packages/features/ee/users/pages/users-add-view.tsx
index 9ebc493b05..01d67ad759 100644
--- a/packages/features/ee/users/pages/users-add-view.tsx
+++ b/packages/features/ee/users/pages/users-add-view.tsx
@@ -4,9 +4,8 @@ import { usePathname, useRouter } from "next/navigation";
import { getParserWithGeneric } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
-import { Meta, showToast } from "@calcom/ui";
+import { showToast } from "@calcom/ui";
-import { getLayout } from "../../../settings/layouts/SettingsLayout";
import LicenseRequired from "../../common/components/LicenseRequired";
import { UserForm } from "../components/UserForm";
import { userBodySchema } from "../schemas/userBodySchema";
@@ -30,20 +29,19 @@ const UsersAddView = () => {
},
});
return (
-
-
- {
- const parser = getParserWithGeneric(userBodySchema);
- const parsedValues = parser(values);
- mutation.mutate(parsedValues);
- }}
- />
-
+
+
+ {
+ const parser = getParserWithGeneric(userBodySchema);
+ const parsedValues = parser(values);
+ mutation.mutate(parsedValues);
+ }}
+ />
+
+
);
};
-UsersAddView.getLayout = getLayout;
-
export default UsersAddView;
diff --git a/packages/features/ee/users/pages/users-edit-view.tsx b/packages/features/ee/users/pages/users-edit-view.tsx
index 00ed0d7b92..c214607493 100644
--- a/packages/features/ee/users/pages/users-edit-view.tsx
+++ b/packages/features/ee/users/pages/users-edit-view.tsx
@@ -7,13 +7,14 @@ import NoSSR from "@calcom/core/components/NoSSR";
import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback";
import { getParserWithGeneric } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
-import { Meta, showToast } from "@calcom/ui";
+import { showToast } from "@calcom/ui";
-import { getLayout } from "../../../settings/layouts/SettingsLayout";
import LicenseRequired from "../../common/components/LicenseRequired";
import { UserForm } from "../components/UserForm";
import { userBodySchema } from "../schemas/userBodySchema";
+import type { UserAdminRouterOutputs } from "../server/trpc-router";
+type User = UserAdminRouterOutputs["get"]["user"];
const userIdSchema = z.object({ id: z.coerce.number() });
const UsersEditPage = () => {
@@ -22,14 +23,22 @@ const UsersEditPage = () => {
if (!input.success) return Invalid input
;
- return ;
+ const [data] = trpc.viewer.users.get.useSuspenseQuery({ userId: input.data.id });
+ const { user } = data;
+
+ return (
+
+
+
+
+
+ );
};
-const UsersEditView = ({ userId }: { userId: number }) => {
+export const UsersEditView = ({ user }: { user: User }) => {
const pathname = usePathname();
const router = useRouter();
- const [data] = trpc.viewer.users.get.useSuspenseQuery({ userId });
- const { user } = data;
+
const utils = trpc.useUtils();
const mutation = trpc.viewer.users.update.useMutation({
onSuccess: async () => {
@@ -43,29 +52,22 @@ const UsersEditView = ({ userId }: { userId: number }) => {
},
});
return (
-
-
-
- {
- const parser = getParserWithGeneric(userBodySchema);
- const parsedValues = parser(values);
- const data: Partial = {
- ...parsedValues,
- userId: user.id,
- };
- // Don't send username if it's the same as the current one
- if (user.username === data.username) delete data.username;
- mutation.mutate(data);
- }}
- defaultValues={user}
- />
-
-
+ {
+ const parser = getParserWithGeneric(userBodySchema);
+ const parsedValues = parser(values);
+ const data: Partial = {
+ ...parsedValues,
+ userId: user.id,
+ };
+ // Don't send username if it's the same as the current one
+ if (user.username === data.username) delete data.username;
+ mutation.mutate(data);
+ }}
+ defaultValues={user}
+ />
);
};
-UsersEditPage.getLayout = getLayout;
-
-export default UsersEditPage;
+export default UsersEditView;
diff --git a/packages/features/ee/users/pages/users-listing-view.tsx b/packages/features/ee/users/pages/users-listing-view.tsx
index 51b8dfa229..ee9fd5038e 100644
--- a/packages/features/ee/users/pages/users-listing-view.tsx
+++ b/packages/features/ee/users/pages/users-listing-view.tsx
@@ -1,7 +1,6 @@
"use client";
import NoSSR from "@calcom/core/components/NoSSR";
-import { Button, Meta } from "@calcom/ui";
import { getLayout } from "../../../settings/layouts/SettingsLayout";
import { UsersTable } from "../components/UsersTable";
@@ -9,17 +8,6 @@ import { UsersTable } from "../components/UsersTable";
const DeploymentUsersListPage = () => {
return (
<>
-
- {/* TODO: Add import users functionality */}
- {/* */}
-
-
- }
- />
diff --git a/packages/features/flags/pages/flag-listing-view.tsx b/packages/features/flags/pages/flag-listing-view.tsx
index 1bd0c3443e..23f08ed768 100644
--- a/packages/features/flags/pages/flag-listing-view.tsx
+++ b/packages/features/flags/pages/flag-listing-view.tsx
@@ -1,7 +1,9 @@
+"use client";
+
import { Suspense } from "react";
import NoSSR from "@calcom/core/components/NoSSR";
-import { Meta, SkeletonText, SkeletonContainer } from "@calcom/ui";
+import { SkeletonText, SkeletonContainer } from "@calcom/ui";
import { FlagAdminList } from "../components/FlagAdminList";
@@ -18,13 +20,10 @@ const SkeletonLoader = () => {
export const FlagListingView = () => {
return (
- <>
-
-
- }>
-
-
-
- >
+
+ }>
+
+
+
);
};
diff --git a/packages/features/settings/appDir/SettingsHeader.tsx b/packages/features/settings/appDir/SettingsHeader.tsx
new file mode 100644
index 0000000000..8299b0d7fc
--- /dev/null
+++ b/packages/features/settings/appDir/SettingsHeader.tsx
@@ -0,0 +1,57 @@
+import React, { Suspense } from "react";
+
+import { classNames } from "@calcom/lib";
+import { Icon } from "@calcom/ui";
+
+interface HeaderProps {
+ children: React.ReactNode;
+ title?: string;
+ description?: string;
+ CTA?: React.ReactNode;
+ borderInShellHeader?: boolean;
+ backButton?: boolean;
+}
+
+export default function Header({
+ children,
+ title,
+ description,
+ CTA,
+ borderInShellHeader,
+ backButton,
+}: HeaderProps) {
+ return (
+
+
+
+ {backButton && (
+
+
+
+ )}
+
+ {title ? (
+
+ {title}
+
+ ) : (
+
+ )}
+ {description ? (
+
{description}
+ ) : (
+
+ )}
+
+
{CTA}
+
+
+
}>{children}
+
+ );
+}
diff --git a/packages/features/settings/layouts/SettingsLayoutAppDir.tsx b/packages/features/settings/appDir/SettingsLayoutAppDir.tsx
similarity index 62%
rename from packages/features/settings/layouts/SettingsLayoutAppDir.tsx
rename to packages/features/settings/appDir/SettingsLayoutAppDir.tsx
index c20fdbfd70..d96b7f7972 100644
--- a/packages/features/settings/layouts/SettingsLayoutAppDir.tsx
+++ b/packages/features/settings/appDir/SettingsLayoutAppDir.tsx
@@ -5,7 +5,7 @@ import { useSession } from "next-auth/react";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import type { ComponentProps } from "react";
-import React, { Suspense, useEffect, useState } from "react";
+import React, { useEffect, useState, useMemo } from "react";
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import Shell from "@calcom/features/shell/Shell";
@@ -18,7 +18,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { IdentityProvider, MembershipRole, UserPermissionRole } from "@calcom/prisma/enums";
import { trpc } from "@calcom/trpc/react";
import type { VerticalTabItemProps } from "@calcom/ui";
-import { Badge, Button, ErrorBoundary, Icon, Skeleton, useMeta, VerticalTabItem } from "@calcom/ui";
+import { Badge, Button, ErrorBoundary, Icon, Skeleton, VerticalTabItem } from "@calcom/ui";
const tabs: VerticalTabItemProps[] = [
{
@@ -31,6 +31,7 @@ const tabs: VerticalTabItemProps[] = [
{ name: "calendars", href: "/settings/my-account/calendars" },
{ name: "conferencing", href: "/settings/my-account/conferencing" },
{ name: "appearance", href: "/settings/my-account/appearance" },
+ { name: "out_of_office", href: "/settings/my-account/out-of-office" },
// TODO
// { name: "referrals", href: "/settings/my-account/referrals" },
],
@@ -59,6 +60,7 @@ const tabs: VerticalTabItemProps[] = [
//
{ name: "webhooks", href: "/settings/developer/webhooks" },
{ name: "api_keys", href: "/settings/developer/api-keys" },
+ { name: "admin_api", href: "/settings/organizations/admin-api" },
// TODO: Add profile level for embeds
// { name: "embeds", href: "/v2/settings/developer/embeds" },
],
@@ -80,13 +82,26 @@ const tabs: VerticalTabItemProps[] = [
href: "/settings/organizations/members",
},
{
- name: "appearance",
- href: "/settings/organizations/appearance",
+ name: "privacy",
+ href: "/settings/organizations/privacy",
},
{
name: "billing",
href: "/settings/organizations/billing",
},
+ { name: "OAuth Clients", href: "/settings/organizations/platform/oauth-clients" },
+ {
+ name: "SSO",
+ href: "/settings/organizations/sso",
+ },
+ {
+ name: "directory_sync",
+ href: "/settings/organizations/dsync",
+ },
+ {
+ name: "admin_api",
+ href: "https://cal.com/docs/enterprise-features/api/api-reference/bookings#admin-access",
+ },
],
},
{
@@ -95,6 +110,12 @@ const tabs: VerticalTabItemProps[] = [
icon: "users",
children: [],
},
+ {
+ name: "other_teams",
+ href: "/settings/organizations/teams/other",
+ icon: "users",
+ children: [],
+ },
{
name: "admin",
href: "/settings/admin",
@@ -114,57 +135,92 @@ const tabs: VerticalTabItemProps[] = [
];
tabs.find((tab) => {
- // Add "SAML SSO" to the tab
if (tab.name === "security" && !HOSTED_CAL_FEATURES) {
tab.children?.push({ name: "sso_configuration", href: "/settings/security/sso" });
+ // TODO: Enable dsync for self hosters
+ // tab.children?.push({ name: "directory_sync", href: "/settings/security/dsync" });
}
});
// The following keys are assigned to admin only
const adminRequiredKeys = ["admin"];
const organizationRequiredKeys = ["organization"];
+const organizationAdminKeys = ["privacy", "billing", "OAuth Clients", "SSO", "directory_sync"];
const useTabs = () => {
const session = useSession();
- const { data: user } = trpc.viewer.me.useQuery();
+ const { data: user } = trpc.viewer.me.useQuery({ includePasswordAdded: true });
const orgBranding = useOrgBranding();
-
const isAdmin = session.data?.user.role === UserPermissionRole.ADMIN;
+ const isOrgAdminOrOwner =
+ orgBranding?.role === MembershipRole.ADMIN || orgBranding?.role === MembershipRole.OWNER;
- tabs.map((tab) => {
- if (tab.href === "/settings/my-account") {
- tab.name = user?.name || "my_account";
- tab.icon = undefined;
- tab.avatar = getUserAvatarUrl(user);
- } else if (tab.href === "/settings/organizations") {
- tab.name = orgBranding?.name || "organization";
- tab.avatar = `${orgBranding?.fullDomain}/org/${orgBranding?.slug}/avatar.png`;
- } else if (
- tab.href === "/settings/security" &&
- user?.identityProvider === IdentityProvider.GOOGLE &&
- !user?.twoFactorEnabled
- ) {
- tab.children = tab?.children?.filter(
- (childTab) => childTab.href !== "/settings/security/two-factor-auth"
- );
- }
- return tab;
- });
+ const processTabsMemod = useMemo(() => {
+ const processedTabs = tabs.map((tab) => {
+ if (tab.href === "/settings/my-account") {
+ return {
+ ...tab,
+ name: user?.name || "my_account",
+ icon: undefined,
+ avatar: getUserAvatarUrl(user),
+ };
+ } else if (tab.href === "/settings/organizations") {
+ const newArray = (tab?.children ?? []).filter(
+ (child) => isOrgAdminOrOwner || !organizationAdminKeys.includes(child.name)
+ );
- // check if name is in adminRequiredKeys
- return tabs.filter((tab) => {
- if (organizationRequiredKeys.includes(tab.name)) return !!session.data?.user?.org;
+ // TODO: figure out feature flag as it doesnt cause a re-render of the component when loaded.
+ // You have to refresh the page to see the changes.
+ if (true) {
+ newArray.splice(4, 0, {
+ name: "attributes",
+ href: "/settings/organizations/attributes",
+ });
+ }
- if (isAdmin) return true;
- return !adminRequiredKeys.includes(tab.name);
- });
+ return {
+ ...tab,
+ children: newArray,
+ name: orgBranding?.name || "organization",
+ avatar: getPlaceholderAvatar(orgBranding?.logoUrl, orgBranding?.name),
+ };
+ } else if (
+ tab.href === "/settings/security" &&
+ user?.identityProvider === IdentityProvider.GOOGLE &&
+ !user?.twoFactorEnabled &&
+ !user?.passwordAdded
+ ) {
+ const filtered = tab?.children?.filter(
+ (childTab) => childTab.href !== "/settings/security/two-factor-auth"
+ );
+ return { ...tab, children: filtered };
+ } else if (tab.href === "/settings/developer") {
+ const filtered = tab?.children?.filter(
+ (childTab) => isOrgAdminOrOwner || childTab.name !== "admin_api"
+ );
+ return { ...tab, children: filtered };
+ }
+ return tab;
+ });
+
+ // check if name is in adminRequiredKeys
+ return processedTabs.filter((tab) => {
+ if (organizationRequiredKeys.includes(tab.name)) return !!orgBranding;
+ if (tab.name === "other_teams" && !isOrgAdminOrOwner) return false;
+
+ if (isAdmin) return true;
+ return !adminRequiredKeys.includes(tab.name);
+ });
+ }, [isAdmin, orgBranding, isOrgAdminOrOwner, user]);
+
+ return processTabsMemod;
};
const BackButtonInSidebar = ({ name }: { name: string }) => {
return (
{
- const searchParams = useCompatSearchParams();
+const TeamListCollapsible = () => {
+ const { data: teams } = trpc.viewer.teams.list.useQuery();
const { t } = useLocale();
- const tabsWithPermissions = useTabs();
const [teamMenuState, setTeamMenuState] =
useState<{ teamId: number | undefined; teamMenuOpen: boolean }[]>();
- const [otherTeamMenuState, setOtherTeamMenuState] = useState<
- {
- teamId: number | undefined;
- teamMenuOpen: boolean;
- }[]
- >();
- const { data: teams } = trpc.viewer.teams.list.useQuery();
- const session = useSession();
- const { data: currentOrg } = trpc.viewer.organizations.listCurrent.useQuery(undefined, {
- enabled: !!session.data?.user?.org,
- });
-
- const { data: otherTeams } = trpc.viewer.organizations.listOtherTeams.useQuery();
-
+ const searchParams = useCompatSearchParams();
useEffect(() => {
if (teams) {
const teamStates = teams?.map((team) => ({
@@ -223,6 +261,147 @@ const SettingsSidebarContainer = ({
}
}, [searchParams?.get("id"), teams]);
+ return (
+ <>
+ {teams &&
+ teamMenuState &&
+ teams.map((team, index: number) => {
+ if (!teamMenuState[index]) {
+ return null;
+ }
+ if (teamMenuState.some((teamState) => teamState.teamId === team.id))
+ return (
+
+ setTeamMenuState([
+ ...teamMenuState,
+ (teamMenuState[index] = {
+ ...teamMenuState[index],
+ teamMenuOpen: !teamMenuState[index].teamMenuOpen,
+ }),
+ ])
+ }>
+
+
+ setTeamMenuState([
+ ...teamMenuState,
+ (teamMenuState[index] = {
+ ...teamMenuState[index],
+ teamMenuOpen: !teamMenuState[index].teamMenuOpen,
+ }),
+ ])
+ }>
+
+ {teamMenuState[index].teamMenuOpen ? (
+
+ ) : (
+
+ )}
+
+ {!team.parentId && (
+

+ )}
+
{team.name}
+ {!team.accepted && (
+
+ Inv.
+
+ )}
+
+
+
+ {team.accepted && (
+
+ )}
+
+
+ {(team.role === MembershipRole.OWNER ||
+ team.role === MembershipRole.ADMIN ||
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore this exists wtf?
+ (team.isOrgAdmin && team.isOrgAdmin)) && (
+ <>
+ {/* TODO */}
+ {/* */}
+
+ {/* Hide if there is a parent ID */}
+ {!team.parentId ? (
+ <>
+
+ >
+ ) : null}
+ >
+ )}
+
+
+ );
+ })}
+ >
+ );
+};
+
+const SettingsSidebarContainer = ({
+ className = "",
+ navigationIsOpenedOnMobile,
+ bannersHeight,
+}: SettingsSidebarContainerProps) => {
+ const searchParams = useCompatSearchParams();
+ const { t } = useLocale();
+ const tabsWithPermissions = useTabs();
+ const [otherTeamMenuState, setOtherTeamMenuState] = useState<
+ {
+ teamId: number | undefined;
+ teamMenuOpen: boolean;
+ }[]
+ >();
+ const session = useSession();
+ const { data: currentOrg } = trpc.viewer.organizations.listCurrent.useQuery(undefined, {
+ enabled: !!session.data?.user?.org,
+ });
+
+ const { data: otherTeams } = trpc.viewer.organizations.listOtherTeams.useQuery(undefined, {
+ enabled: !!session.data?.user?.org,
+ });
+
// Same as above but for otherTeams
useEffect(() => {
if (otherTeams) {
@@ -244,17 +423,6 @@ const SettingsSidebarContainer = ({
const isOrgAdminOrOwner =
currentOrg && currentOrg?.user?.role && ["OWNER", "ADMIN"].includes(currentOrg?.user?.role);
- if (isOrgAdminOrOwner) {
- const teamsIndex = tabsWithPermissions.findIndex((tab) => tab.name === "teams");
-
- tabsWithPermissions.splice(teamsIndex + 1, 0, {
- name: "other_teams",
- href: "/settings/organizations/teams/other",
- icon: "users",
- children: [],
- });
- }
-
return (