-
-
{
- const showRemoveAvatarButton = !isFallbackImg || (value && userAvatar !== value);
- return (
- <>
-
-
-
{t("profile_picture")}
-
- {
- formMethods.setValue("avatar", newAvatar, { shouldDirty: true });
- }}
- imageSrc={value || undefined}
- triggerButtonColor={showRemoveAvatarButton ? "secondary" : "primary"}
- />
-
- {showRemoveAvatarButton && (
-
- )}
-
-
- >
- );
- }}
- />
-
- {extraField}
-
-
-
-
-
-
-
-
- md.render(formMethods.getValues("bio") || "")}
- setText={(value: string) => {
- formMethods.setValue("bio", turndown(value), { shouldDirty: true });
- }}
- excludedToolbarItems={["blockType"]}
- disableLists
- firstRender={firstRender}
- setFirstRender={setFirstRender}
- />
-
+
+
(
+ <>
+
+
+ {
+ formMethods.setValue("avatar", newAvatar, { shouldDirty: true });
+ }}
+ imageSrc={value || undefined}
+ />
+
+ >
+ )}
+ />
+
+ {extraField}
+
+
+
+
+
+
+
+
+ md.render(formMethods.getValues("bio") || "")}
+ setText={(value: string) => {
+ formMethods.setValue("bio", turndown(value), { shouldDirty: true });
+ }}
+ excludedToolbarItems={["blockType"]}
+ disableLists
+ firstRender={firstRender}
+ setFirstRender={setFirstRender}
+ />
-
-
-
+
);
};
diff --git a/apps/web/pages/settings/security/impersonation.tsx b/apps/web/pages/settings/security/impersonation.tsx
index d3afab267ee364..fbe31be02ba233 100644
--- a/apps/web/pages/settings/security/impersonation.tsx
+++ b/apps/web/pages/settings/security/impersonation.tsx
@@ -1,34 +1,23 @@
-import { useState } from "react";
+import type { GetServerSidePropsContext } from "next";
+import { useForm } from "react-hook-form";
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 { Button, Form, Label, Meta, showToast, Skeleton, Switch } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
- return (
-
-
-
-
-
-
- );
-};
+import { ssrInit } from "@server/lib/ssr";
-const ProfileImpersonationView = ({ user }: { user: RouterOutputs["viewer"]["me"] }) => {
+const ProfileImpersonationView = () => {
const { t } = useLocale();
const utils = trpc.useContext();
- const [disableImpersonation, setDisableImpersonation] = useState
(
- user?.disableImpersonation
- );
-
+ const { data: user } = trpc.viewer.me.useQuery();
const mutation = trpc.viewer.updateProfile.useMutation({
onSuccess: () => {
showToast(t("profile_updated_successfully"), "success");
+ reset(getValues());
},
onSettled: () => {
utils.viewer.me.invalidate();
@@ -37,54 +26,83 @@ const ProfileImpersonationView = ({ user }: { user: RouterOutputs["viewer"]["me"
await utils.viewer.me.cancel();
const previousValue = utils.viewer.me.getData();
- setDisableImpersonation(disableImpersonation);
-
+ if (previousValue && disableImpersonation) {
+ utils.viewer.me.setData(undefined, { ...previousValue, 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");
},
});
+ const formMethods = useForm<{ disableImpersonation: boolean }>({
+ defaultValues: {
+ disableImpersonation: user?.disableImpersonation,
+ },
+ });
+
+ const {
+ formState: { isSubmitting, isDirty },
+ setValue,
+ reset,
+ getValues,
+ watch,
+ } = formMethods;
+
+ const isDisabled = isSubmitting || !isDirty;
return (
<>
-
-
- {
- mutation.mutate({ disableImpersonation: !checked });
- }}
- disabled={mutation.isLoading}
- switchContainerClassName="py-6 px-4 sm:px-6 border-subtle rounded-b-xl border border-t-0"
- />
-
+
+
>
);
};
-const ProfileImpersonationViewWrapper = () => {
- const { data: user, isLoading } = trpc.viewer.me.useQuery();
- const { t } = useLocale();
+ProfileImpersonationView.getLayout = getLayout;
+ProfileImpersonationView.PageWrapper = PageWrapper;
- if (isLoading || !user)
- return ;
-
- return ;
+export const getServerSideProps = async (context: GetServerSidePropsContext) => {
+ const ssr = await ssrInit(context);
+ await ssr.viewer.me.prefetch();
+ return {
+ props: {
+ trpcState: ssr.dehydrate(),
+ },
+ };
};
-ProfileImpersonationViewWrapper.getLayout = getLayout;
-ProfileImpersonationViewWrapper.PageWrapper = PageWrapper;
-
-export default ProfileImpersonationViewWrapper;
+export default ProfileImpersonationView;
diff --git a/apps/web/pages/settings/security/password.tsx b/apps/web/pages/settings/security/password.tsx
index 71077c944725ac..6da897b4301cb4 100644
--- a/apps/web/pages/settings/security/password.tsx
+++ b/apps/web/pages/settings/security/password.tsx
@@ -1,29 +1,13 @@
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 { userMetadata } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
-import {
- Alert,
- Button,
- Form,
- Meta,
- PasswordField,
- Select,
- SettingsToggle,
- showToast,
- SkeletonButton,
- SkeletonContainer,
- SkeletonText,
-} from "@calcom/ui";
+import { Alert, Button, Form, Meta, PasswordField, Select, SettingsToggle, showToast } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
@@ -34,58 +18,34 @@ type ChangePasswordSessionFormValues = {
apiError: string;
};
-interface PasswordViewProps {
- user: RouterOutputs["viewer"]["me"];
-}
-
-const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-const PasswordView = ({ user }: PasswordViewProps) => {
+const PasswordView = () => {
const { data } = useSession();
const { t } = useLocale();
const utils = trpc.useContext();
- const metadata = userMetadataSchema.safeParse(user?.metadata);
- const initialSessionTimeout = metadata.success ? metadata.data?.sessionTimeout : undefined;
-
- const [sessionTimeout, setSessionTimeout] = useState(initialSessionTimeout);
+ const { data: user } = trpc.viewer.me.useQuery();
+ const metadata = userMetadata.safeParse(user?.metadata);
+ const sessionTimeout = metadata.success ? metadata.data?.sessionTimeout : undefined;
const sessionMutation = trpc.viewer.updateProfile.useMutation({
- onSuccess: (data) => {
+ onSuccess: () => {
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);
+ const previousValue = utils.viewer.me.getData();
+ const previousMetadata = userMetadata.parse(previousValue?.metadata);
- if (previousValue && sessionTimeout && previousMetadata.success) {
+ if (previousValue && sessionTimeout) {
utils.viewer.me.setData(undefined, {
...previousValue,
- metadata: { ...previousMetadata?.data, sessionTimeout: sessionTimeout },
+ metadata: { ...previousMetadata, sessionTimeout: sessionTimeout },
});
- return { previousValue };
}
+ return { previousValue };
},
onError: (error, _, context) => {
if (context?.previousValue) {
@@ -124,30 +84,20 @@ const PasswordView = ({ user }: PasswordViewProps) => {
defaultValues: {
oldPassword: "",
newPassword: "",
+ sessionTimeout,
},
});
- 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 }
- );
- }
+ const sessionTimeoutWatch = formMethods.watch("sessionTimeout");
+ const handleSubmit = (values: ChangePasswordSessionFormValues) => {
+ const { oldPassword, newPassword, sessionTimeout: newSessionTimeout } = values;
if (oldPassword && newPassword) {
passwordMutation.mutate({ oldPassword, newPassword });
}
+ if (sessionTimeout !== newSessionTimeout) {
+ sessionMutation.mutate({ metadata: { ...metadata, sessionTimeout: newSessionTimeout } });
+ }
};
const timeoutOptions = [5, 10, 15].map((mins) => ({
@@ -162,7 +112,7 @@ const PasswordView = ({ user }: PasswordViewProps) => {
return (
<>
-
+
{user && user.identityProvider !== IdentityProvider.CAL ? (
@@ -180,127 +130,87 @@ const PasswordView = ({ user }: PasswordViewProps) => {
) : (