fix: functionality

This commit is contained in:
Piyush Gupta
2025-05-09 15:41:00 +05:30
parent bc489e050a
commit c02f070307
11 changed files with 64 additions and 31 deletions
@@ -1,6 +1,5 @@
"use client";
import { formbricksLogout } from "@/app/lib/formbricks";
import { appLanguages } from "@/lib/i18n/utils";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { forgotPasswordAction } from "@/modules/auth/forgot-password/actions";
@@ -35,7 +34,12 @@ import { updateUserAction } from "../actions";
const ZEditProfileNameFormSchema = ZUser.pick({ name: true, locale: true });
type TEditProfileNameForm = z.infer<typeof ZEditProfileNameFormSchema>;
export const EditProfileDetailsForm = ({ user }: { user: TUser }) => {
interface IEditProfileDetailsFormProps {
user: TUser;
isPasswordResetEnabled?: boolean;
}
export const EditProfileDetailsForm = ({ user, isPasswordResetEnabled }: IEditProfileDetailsFormProps) => {
const form = useForm<TEditProfileNameForm>({
defaultValues: { name: user.name, locale: user.locale || "en" },
mode: "onChange",
@@ -72,7 +76,6 @@ export const EditProfileDetailsForm = ({ user }: { user: TUser }) => {
} else {
toast.success(t("auth.forgot-password.email-sent.heading"));
await signOut({ callbackUrl: "/auth/login" });
await formbricksLogout();
}
setIsResettingPassword(false);
@@ -146,23 +149,25 @@ export const EditProfileDetailsForm = ({ user }: { user: TUser }) => {
)}
/>
<div className="mt-4 space-y-2">
<Label htmlFor="reset-password">{t("auth.forgot-password.reset_password")}</Label>
<p className="mt-1 text-sm text-slate-500">
{t("auth.forgot-password.reset_password_description")}
</p>
<div className="flex items-center justify-between gap-2">
<Input type="email" id="reset-password" defaultValue={user.email} disabled />
<Button
onClick={handleResetPassword}
loading={isResettingPassword}
disabled={isResettingPassword || !user.email}
size="default"
variant="secondary">
{t("auth.forgot-password.reset_password")}
</Button>
{isPasswordResetEnabled && (
<div className="mt-4 space-y-2">
<Label htmlFor="reset-password">{t("auth.forgot-password.reset_password")}</Label>
<p className="mt-1 text-sm text-slate-500">
{t("auth.forgot-password.reset_password_description")}
</p>
<div className="flex items-center justify-between gap-2">
<Input type="email" id="reset-password" defaultValue={user.email} disabled />
<Button
onClick={handleResetPassword}
loading={isResettingPassword}
disabled={isResettingPassword}
size="default"
variant="secondary">
{t("auth.forgot-password.reset_password")}
</Button>
</div>
</div>
</div>
)}
<Button
type="submit"
@@ -1,6 +1,6 @@
import { AccountSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(account)/components/AccountSettingsNavbar";
import { AccountSecurity } from "@/app/(app)/environments/[environmentId]/settings/(account)/profile/components/AccountSecurity";
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
import { IS_FORMBRICKS_CLOUD, PASSWORD_RESET_DISABLED } from "@/lib/constants";
import { getOrganizationsWhereUserIsSingleOwner } from "@/lib/organization/service";
import { getUser } from "@/lib/user/service";
import { getIsMultiOrgEnabled, getIsTwoFactorAuthEnabled } from "@/modules/ee/license-check/lib/utils";
@@ -32,6 +32,8 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
throw new Error(t("common.user_not_found"));
}
const isPasswordResetEnabled = !PASSWORD_RESET_DISABLED && user.identityProvider === "email";
return (
<PageContentWrapper>
<PageHeader pageTitle={t("common.account_settings")}>
@@ -42,7 +44,7 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
<SettingsCard
title={t("environments.settings.profile.personal_information")}
description={t("environments.settings.profile.update_personal_info")}>
<EditProfileDetailsForm user={user} />
<EditProfileDetailsForm user={user} isPasswordResetEnabled={isPasswordResetEnabled} />
</SettingsCard>
<SettingsCard
title={t("common.avatar")}
+2 -1
View File
@@ -23,7 +23,8 @@
"text": "Du kannst Dich jetzt mit deinem neuen Passwort einloggen"
}
},
"reset_password": "Passwort zurücksetzen"
"reset_password": "Passwort zurücksetzen",
"reset_password_description": "Du wirst abgemeldet, um dein Passwort zurückzusetzen."
},
"invite": {
"create_account": "Konto erstellen",
+2 -1
View File
@@ -23,7 +23,8 @@
"text": "You can now log in with your new password"
}
},
"reset_password": "Reset password"
"reset_password": "Reset password",
"reset_password_description": "You will be logged out to reset your password."
},
"invite": {
"create_account": "Create an account",
+2 -1
View File
@@ -23,7 +23,8 @@
"text": "Vous pouvez maintenant vous connecter avec votre nouveau mot de passe."
}
},
"reset_password": "Réinitialiser le mot de passe"
"reset_password": "Réinitialiser le mot de passe",
"reset_password_description": "Vous serez déconnecté pour réinitialiser votre mot de passe."
},
"invite": {
"create_account": "Créer un compte",
+2 -1
View File
@@ -23,7 +23,8 @@
"text": "Agora você pode fazer login com sua nova senha"
}
},
"reset_password": "Redefinir senha"
"reset_password": "Redefinir senha",
"reset_password_description": "Você será desconectado para redefinir sua senha."
},
"invite": {
"create_account": "Cria uma conta",
+2 -1
View File
@@ -23,7 +23,8 @@
"text": "Pode agora iniciar sessão com a sua nova palavra-passe"
}
},
"reset_password": "Redefinir palavra-passe"
"reset_password": "Redefinir palavra-passe",
"reset_password_description": "Será desconectado para redefinir a sua palavra-passe."
},
"invite": {
"create_account": "Criar uma conta",
+2 -1
View File
@@ -23,7 +23,8 @@
"text": "您現在可以使用新密碼登入"
}
},
"reset_password": "重設密碼"
"reset_password": "重設密碼",
"reset_password_description": "您將被登出以重設您的密碼。"
},
"invite": {
"create_account": "建立帳戶",
@@ -1,9 +1,11 @@
"use server";
import { PASSWORD_RESET_DISABLED } from "@/lib/constants";
import { actionClient } from "@/lib/utils/action-client";
import { getUserByEmail } from "@/modules/auth/lib/user";
import { sendForgotPasswordEmail } from "@/modules/email";
import { z } from "zod";
import { OperationNotAllowedError, ResourceNotFoundError } from "@formbricks/types/errors";
import { ZUserEmail } from "@formbricks/types/user";
const ZForgotPasswordAction = z.object({
@@ -13,9 +15,21 @@ const ZForgotPasswordAction = z.object({
export const forgotPasswordAction = actionClient
.schema(ZForgotPasswordAction)
.action(async ({ parsedInput }) => {
const user = await getUserByEmail(parsedInput.email);
if (user) {
await sendForgotPasswordEmail(user);
if (PASSWORD_RESET_DISABLED) {
throw new OperationNotAllowedError("Password reset is disabled");
}
const user = await getUserByEmail(parsedInput.email);
if (!user) {
throw new ResourceNotFoundError("user", parsedInput.email);
}
if (user.identityProvider !== "email") {
throw new OperationNotAllowedError("Password reset is not allowed for SSO users");
}
await sendForgotPasswordEmail(user);
return { success: true };
});
+1
View File
@@ -92,6 +92,7 @@ export const getUserByEmail = reactCache(async (email: string) =>
email: true,
emailVerified: true,
isActive: true,
identityProvider: true,
},
});
@@ -4,7 +4,7 @@ import { actionClient } from "@/lib/utils/action-client";
import { getUserByEmail } from "@/modules/auth/lib/user";
import { sendVerificationEmail } from "@/modules/email";
import { z } from "zod";
import { InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { InvalidInputError, OperationNotAllowedError, ResourceNotFoundError } from "@formbricks/types/errors";
import { ZUserEmail } from "@formbricks/types/user";
const ZResendVerificationEmailAction = z.object({
@@ -18,6 +18,11 @@ export const resendVerificationEmailAction = actionClient
if (!user) {
throw new ResourceNotFoundError("user", parsedInput.email);
}
if (user.identityProvider !== "email") {
throw new OperationNotAllowedError("Email verification is not allowed for SSO users");
}
if (user.emailVerified) {
throw new InvalidInputError("Email address has already been verified");
}