Skip to content

Commit

Permalink
Merge pull request #70 from chaitanyakole/reset_pass
Browse files Browse the repository at this point in the history
Bug #236457 [admin portal] Password validations are missing while reseting the password by tenant admin and cohort admin.
  • Loading branch information
gouravmore authored Mar 5, 2025
2 parents 373e3cc + 1cb4191 commit 5465ec8
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 19 deletions.
22 changes: 18 additions & 4 deletions public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,25 @@
"RESET_PASSWORD": "Reset Password",
"NEW_PASSWORD": "New Password",
"CONFIRM_PASSWORD": "Confirm Password",
"PASSWORD_RESET_SUCCESSFUL": "Password Reset Successful!",
"PASSWORD_RESET_FAILED": "Password Reset Failed. Please try again.",
"ENTER_VALID_PASSWORD": "Enter Valid Password",
"ADD_SINGLE_USER":"Add Single user",
"ADD_MULTIPLE_USERS":"Add Multiple users"
"ADD_SINGLE_USER": "Add Single user",
"ADD_MULTIPLE_USERS": "Add Multiple users",
"PASSWORD_RESET_SUCCESSFUL": "Password has been reset successfully.",
"PASSWORD_RESET_FAILED": "Failed to reset password. Please try again.",
"UNEXPECTED_ERROR": "An unexpected error occurred. Please try again later.",
"RESET_PASSWORD_FOR_USER": "Reset password for user:",
"PASSWORDS_DO_NOT_MATCH": "Passwords do not match.",
"PASSWORD_LENGTH_ERROR": "Password must be at least 8 characters long.",
"PASSWORD_UPPERCASE_ERROR": "Password must contain at least one uppercase letter.",
"PASSWORD_LOWERCASE_ERROR": "Password must contain at least one lowercase letter.",
"PASSWORD_NUMBER_ERROR": "Password must contain at least one number.",
"PASSWORD_SPECIAL_CHAR_ERROR": "Password must contain at least one special character.",
"PASSWORD_COMPLEXITY_REQUIREMENTS": "Password must: \n- Be at least 8 characters long\n- Contain uppercase and lowercase letters\n- Include a number\n- Include a special character"
},
"HINT": {
"PASSWORD_STRENGTH": {
"GENERAL": "Tip: Create a strong, unique password to protect your account."
}
},
"LOGIN_PAGE": {
"USERNAME": "Username",
Expand Down
117 changes: 102 additions & 15 deletions src/components/ResetPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@ import {
import { useTranslation } from "next-i18next";
import { resetPassword } from "@/services/LoginService";
import { showToastMessage } from "./Toastify";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";

interface ResetPasswordModalProps {
open: boolean;
onClose: () => void;
userData: any;
userData: {
tenantId?: string;
username?: string;
name?: string;
};
}

export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
Expand All @@ -42,15 +47,46 @@ export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
onClose();
};

const validatePasswords = () => {
if (newPassword.length < 8) {
setError("Password must be at least 8 characters long");
const validatePasswords = (): boolean => {
// Check if passwords match
if (newPassword !== confirmPassword) {
setError(t("COMMON.PASSWORDS_DO_NOT_MATCH"));
return false;
}
if (newPassword !== confirmPassword) {
setError("Passwords do not match");

// Validate password strength
const hasUpperCase = /[A-Z]/.test(newPassword);
const hasLowerCase = /[a-z]/.test(newPassword);
const hasNumber = /\d/.test(newPassword);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(newPassword);
const isValidLength = newPassword.length >= 8;

if (!isValidLength) {
setError(t("COMMON.PASSWORD_LENGTH_ERROR"));
return false;
}

if (!hasUpperCase) {
setError(t("COMMON.PASSWORD_UPPERCASE_ERROR"));
return false;
}

if (!hasLowerCase) {
setError(t("COMMON.PASSWORD_LOWERCASE_ERROR"));
return false;
}

if (!hasNumber) {
setError(t("COMMON.PASSWORD_NUMBER_ERROR"));
return false;
}

if (!hasSpecialChar) {
setError(t("COMMON.PASSWORD_SPECIAL_CHAR_ERROR"));
return false;
}

setError("");
return true;
};

Expand All @@ -59,34 +95,50 @@ export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({

setIsLoading(true);
setError("");
const tenantid = userData?.tenantId;

const userObj = {
userName: userData?.username,
newPassword: newPassword,
};

try {
const response = await resetPassword(userObj, tenantid);
const response = await resetPassword(userObj, userData?.tenantId);
if (response?.responseCode !== 200) {
throw new Error("Failed to reset password");
throw new Error(t("COMMON.PASSWORD_RESET_FAILED"));
}

showToastMessage(t("COMMON.PASSWORD_RESET_SUCCESSFUL"), "success");
handleClose();
} catch (err) {
setError(err instanceof Error ? err.message : "An error occurred");
showToastMessage(t("COMMON.PASSWORD_RESET_FAILED"), "error");
const errorMessage =
err instanceof Error ? err.message : t("COMMON.UNEXPECTED_ERROR");

setError(errorMessage);
showToastMessage(errorMessage, "error");
} finally {
setIsLoading(false);
}
};

return (
<Dialog open={open} onClose={handleClose} maxWidth="md" fullWidth>
<Dialog
open={open}
onClose={handleClose}
maxWidth="lg"
sx={{
"& .MuiDialog-paper": {
maxWidth: "400px",
width: "100%",
},
}}
fullWidth
>
<DialogTitle>{t("COMMON.RESET_PASSWORD")}</DialogTitle>
<DialogContent>
<Box sx={{ mt: 2 }}>
<Typography variant="body1" sx={{ mb: 2 }}>
Reset password for user: <strong>{userData?.name}</strong>
{t("COMMON.RESET_PASSWORD_FOR_USER")}{" "}
<strong>{userData?.name}</strong>
</Typography>

<TextField
Expand All @@ -97,6 +149,15 @@ export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
onChange={(e) => setNewPassword(e.target.value)}
margin="normal"
error={!!error}
helperText={error && newPassword ? error : ""}
aria-describedby="password-requirements"
InputProps={{
sx: {
"& input": {
WebkitTextSecurity: "disc",
},
},
}}
/>

<TextField
Expand All @@ -107,8 +168,34 @@ export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
onChange={(e) => setConfirmPassword(e.target.value)}
margin="normal"
error={!!error}
helperText={error}
helperText={error && confirmPassword ? error : ""}
aria-describedby="password-requirements"
InputProps={{
sx: {
"& input": {
WebkitTextSecurity: "disc",
},
},
}}
/>

<Box
id="password-requirements"
sx={{
backgroundColor: "rgba(0, 0, 0, 0.05)",
borderRadius: 2,
p: 1,
mt: 2,
display: "flex",
alignItems: "center",
gap: 1,
}}
>
<InfoOutlinedIcon color="primary" />
<Typography variant="body2" color="text.secondary">
{t("COMMON.PASSWORD_COMPLEXITY_REQUIREMENTS")}
</Typography>
</Box>
</Box>
</DialogContent>
<DialogActions sx={{ p: 2 }}>
Expand All @@ -118,7 +205,7 @@ export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
<Button
onClick={handleResetConfirm}
variant="contained"
sx={{ color: "white" }}
color="primary"
disabled={isLoading || !newPassword || !confirmPassword}
startIcon={isLoading ? <CircularProgress size={20} /> : null}
>
Expand Down

0 comments on commit 5465ec8

Please sign in to comment.