import { IdentityProvider } from "@prisma/client"; import { signOut, useSession } from "next-auth/react"; import { useForm } from "react-hook-form"; import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; import { identityProviderNameMap } from "@calcom/lib/auth"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { userMetadata } from "@calcom/prisma/zod-utils"; import { trpc } from "@calcom/trpc/react"; import { Alert, Button, Form, Meta, PasswordField, Select, SettingsToggle, showToast } from "@calcom/ui"; type ChangePasswordSessionFormValues = { oldPassword: string; newPassword: string; sessionTimeout?: number; apiError: string; }; const PasswordView = () => { const { data } = useSession(); const { t } = useLocale(); const utils = trpc.useContext(); const { data: user } = trpc.viewer.me.useQuery(); const metadata = userMetadata.parse(user?.metadata); const sessionMutation = trpc.viewer.updateProfile.useMutation({ onSuccess: () => { showToast(t("session_timeout_changed"), "success"); formMethods.reset(formMethods.getValues()); }, onSettled: () => { utils.viewer.me.invalidate(); }, onMutate: async ({ metadata }) => { await utils.viewer.me.cancel(); const previousValue = utils.viewer.me.getData(); const previousMetadata = userMetadata.parse(previousValue?.metadata); if (previousValue && metadata?.sessionTimeout) { utils.viewer.me.setData(undefined, { ...previousValue, metadata: { ...previousMetadata, sessionTimeout: metadata?.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 formMethods = useForm({ defaultValues: { oldPassword: "", newPassword: "", sessionTimeout: metadata?.sessionTimeout, }, }); const sessionTimeoutWatch = formMethods.watch("sessionTimeout"); const handleSubmit = (values: ChangePasswordSessionFormValues) => { const { oldPassword, newPassword, sessionTimeout } = values; if (oldPassword && newPassword) { passwordMutation.mutate({ oldPassword, newPassword }); } if (metadata?.sessionTimeout !== sessionTimeout) { sessionMutation.mutate({ metadata: { ...metadata, sessionTimeout } }); } }; 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 ? (

{t("account_managed_by_identity_provider", { provider: identityProviderNameMap[user.identityProvider], })}

{t("account_managed_by_identity_provider_description", { provider: identityProviderNameMap[user.identityProvider], })}

) : (
{formMethods.formState.errors.apiError && (
)}

{t("invalid_password_hint", { passwordLength: passwordMinLength })}

{ if (!e) { formMethods.setValue("sessionTimeout", undefined, { shouldDirty: true }); } else { formMethods.setValue("sessionTimeout", 10, { shouldDirty: true }); } }} /> {sessionTimeoutWatch && (

{t("session_timeout_after")}