Skip to content

Commit

Permalink
Add Password Strength and Toogle Password
Browse files Browse the repository at this point in the history
  • Loading branch information
kushalkumar1362 committed May 30, 2024
1 parent 8e22dbf commit 76a4766
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 14 deletions.
88 changes: 86 additions & 2 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@
"preview": "vite preview"
},
"dependencies": {
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"axios": "^1.7.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^5.2.1",
"react-router-dom": "^6.23.1",
"recoil": "^0.7.7"
"recoil": "^0.7.7",
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@types/zxcvbn": "^4.4.4",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
Expand Down
25 changes: 20 additions & 5 deletions frontend/src/pages/Signin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { useSetRecoilState } from "recoil";
import { tokenState } from "../store/atoms/auth";
import { AiOutlineEyeInvisible, AiOutlineEye } from "react-icons/ai";

const Signin = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [showPassword, setShowPassword] = useState(false);

const [error, setError] = useState({
email: "",
password: "",
Expand Down Expand Up @@ -75,18 +78,30 @@ const Signin = () => {
</p>
</div>
<div className="mb-4">
<label htmlFor="password" className="block text-white">
<label htmlFor="password" className="block text-white relative">
Password
</label>
<input
type="password"
type={showPassword ? "text" : "password"}
id="password"
className="form-input mt-1 p-2 block w-full text-white rounded-lg bg-gray-700"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
required/>
<span
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-[38px] cursor-pointer ">
{showPassword ? (
<AiOutlineEyeInvisible
fontSize={24}
fill="#AFB2BF"
/>
) : (
<AiOutlineEye fontSize={24} fill="#AFB2BF" />
)}
</span>
</label>

<p className="text-sm font-semibold mb-2 text-red-600">
{error.password}
</p>
Expand Down
117 changes: 111 additions & 6 deletions frontend/src/pages/Signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ import { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { useSetRecoilState } from "recoil";
import { tokenState } from "../store/atoms/auth";
import { AiOutlineEyeInvisible, AiOutlineEye } from "react-icons/ai";
import zxcvbn, { ZXCVBNResult } from 'zxcvbn';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCheck, faCircle } from '@fortawesome/free-solid-svg-icons';

const Signup = () => {
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [passwordStrength, setPasswordStrength] = useState<ZXCVBNResult | null>(null);
const [error, setError] = useState({
username: "",
email: "",
Expand Down Expand Up @@ -48,6 +54,28 @@ const Signup = () => {
}
};

const handlePasswordChange = (e:React.ChangeEvent<HTMLInputElement>) => {
const newPassword = e.target.value;
setPassword(newPassword);
setPasswordStrength(zxcvbn(newPassword));
};

const strengthMeterColor = (score:number) => {
switch (score) {
case 0:
return 'bg-red-500';
case 1:
return 'bg-yellow-500';
case 2:
return 'bg-yellow-300';
case 3:
return 'bg-green-300';
case 4:
return 'bg-green-500';
default:
return 'bg-gray-500';
}
};
return (
<section className=" flex justify-center p-10 md:bg-grey-500">
<div className="w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
Expand Down Expand Up @@ -96,19 +124,96 @@ const Signup = () => {
</p>
</div>
<div className="mb-4">
<label htmlFor="password" className="block text-gray-200">
<label htmlFor="password" className="block text-gray-200 relative">
Password
</label>
<input
type="password"
type={showPassword ? "text" : "password"}
id="password"
className="form-input mt-1 p-2 block w-full rounded-lg text-white bg-gray-700"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
onChange={handlePasswordChange}
required/>
<span
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-[38px] cursor-pointer ">
{showPassword ? (
<AiOutlineEyeInvisible
fontSize={24}
fill="#AFB2BF"
/>
) : (
<AiOutlineEye fontSize={24} fill="#AFB2BF" />
)}
</span>
</label>
{passwordStrength && (
<div className="mt-2">
<div className="w-full bg-gray-200 rounded-full h-2.5 dark:bg-gray-700">
<div
className={`h-2.5 rounded-full ${strengthMeterColor(passwordStrength.score)}`}
style={{ width: `${(passwordStrength.score + 1) * 20}%` }}
></div>
</div>
<p className="text-gray-400 text-sm">
{['Weak', 'Fair', 'Good', 'Strong', 'Very Strong'][passwordStrength.score]}
</p>
</div>
)}

<ul className="list-none text-gray-400 text-sm mt-2">
<li className="text-sm">
<span className="low-upper-case">
<FontAwesomeIcon
icon={
/([a-z].*[A-Z])|([A-Z].*[a-z])/.test(password)
? faCheck
: faCircle
}
className={
/([a-z].*[A-Z])|([A-Z].*[a-z])/.test(password)
? 'text-green-600'
: 'text-gray-400'
}
/>
&nbsp;Lowercase &amp; Uppercase
</span>
</li>
<li className="text-sm">
<span className="one-number">
<FontAwesomeIcon
icon={/([0-9])/.test(password) ? faCheck : faCircle}
className={
/([0-9])/.test(password) ? 'text-green-600' : 'text-gray-400'
}
/>
&nbsp;Number (0-9)
</span>
</li>
<li className="text-sm">
<span className="one-special-char">
<FontAwesomeIcon
icon={/([!,%,&,@,#,$,^,*,?,_,~])/.test(password) ? faCheck : faCircle}
className={
/([!,%,&,@,#,$,^,*,?,_,~])/.test(password) ? 'text-green-600' : 'text-gray-400'
}
/>
&nbsp;Special Character (!@#$%^&*)
</span>
</li>
<li className="text-sm">
<span className="eight-character">
<FontAwesomeIcon
icon={password.length > 7 ? faCheck : faCircle}
className={
password.length > 7 ? 'text-green-600' : 'text-gray-400'
}
/>
&nbsp;At least 8 Characters
</span>
</li>
</ul>
</div>
<p className="text-sm font-semibold mb-2 text-red-600">
{error.password}
</p>
Expand Down

0 comments on commit 76a4766

Please sign in to comment.