diff --git a/client/src/components/Login/PasswordStrength.vue b/client/src/components/Login/PasswordStrength.vue index 28c6993a8ebc..f32cbd6993ef 100644 --- a/client/src/components/Login/PasswordStrength.vue +++ b/client/src/components/Login/PasswordStrength.vue @@ -12,7 +12,13 @@ const props = defineProps({ const passwordStrength = ref("empty"); const strengthScore = ref(0); -const showPasswordGuidelines = ref(false); + +const passwordRules = ref({ + minLength: false, + hasNumber: false, + hasUppercase: false, + hasSpecialChar: false, +}); // Evaluate password strength using zxcvbn function evaluatePasswordStrength(newPassword: string) { @@ -35,10 +41,19 @@ function evaluatePasswordStrength(newPassword: string) { } } +// Function to validate password rules +function validatePasswordRules(password: string) { + passwordRules.value.minLength = password.length >= 12; + passwordRules.value.hasNumber = /\d/.test(password); + passwordRules.value.hasUppercase = /[A-Z]/.test(password); + passwordRules.value.hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password); +} + // Watch for changes in the password prop watch(() => props.password, (newPassword) => { if (typeof newPassword === "string") { evaluatePasswordStrength(newPassword); + validatePasswordRules(newPassword || ""); } }); @@ -46,7 +61,7 @@ watch(() => props.password, (newPassword) => { @@ -88,7 +111,7 @@ watch(() => props.password, (newPassword) => { @import "theme/blue.scss"; .password-strength-bar-container { - background-color: #f0f0f0; + background-color: $gray-200; height: 8px; border-radius: 4px; overflow: hidden; @@ -98,29 +121,51 @@ watch(() => props.password, (newPassword) => { .password-strength-bar { height: 100%; transition: width 0.3s ease; -} -.password-strength-bar.weak { - background-color: $brand-danger; -} + &.weak { + background-color: $brand-danger; + } + + &.medium { + background-color: $brand-warning; + } -.password-strength-bar.medium { - background-color: $brand-warning; + &.strong { + background-color: $brand-success; + } } -.password-strength-bar.strong { - background-color: $brand-success; +.password-strength { + &.weak { + color: $brand-danger; + } + + &.medium { + color: $brand-warning; + } + + &.strong { + color: $brand-success; + } } -.password-strength.weak { - color: $brand-danger; +.password-help ul { + list-style: none; + padding: 0; } -.password-strength.medium { - color: $brand-warning; +.password-help li { + display: flex; + align-items: center; + margin: 0.2rem; } -.password-strength.strong { +.password-help li.rule-met { color: $brand-success; } + +.password-help li .fa { + margin-right: 0.5rem; +} + diff --git a/client/src/components/Login/RegisterForm.vue b/client/src/components/Login/RegisterForm.vue index d42b0552a3a3..0d951aab06b7 100644 --- a/client/src/components/Login/RegisterForm.vue +++ b/client/src/components/Login/RegisterForm.vue @@ -38,6 +38,7 @@ interface Props { } const props = defineProps(); +const showPassword = ref(false); const emit = defineEmits<{ (e: "toggle-login"): void; @@ -64,6 +65,10 @@ function toggleLogin() { emit("toggle-login"); } +function togglePasswordVisibility() { + showPassword.value = !showPassword.value; +} + async function submit() { disableCreate.value = true; @@ -145,10 +150,20 @@ async function submit() { id="register-form-password" v-model="password" name="password" - type="password" + :type="showPassword ? 'text' : 'password'" autocomplete="new-password" - required + required /> + + + @@ -245,6 +260,21 @@ async function submit() { border: 1px solid #ccc; padding: 2px 5px; border-radius: 4px; - } + } } + +.password-toggle-icon { + position: absolute; + right: 0.75rem; + background: none; + border: none; + cursor: pointer; + color: $gray-600; + font-size: 1rem; +} + +.password-toggle-icon:hover { + color: $gray-900; +} +