Skip to content

Commit

Permalink
feat: add checkmarks for password reqs (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
ni-jessica authored Nov 3, 2023
1 parent 3473629 commit 77c3288
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 78 deletions.
116 changes: 75 additions & 41 deletions frontend/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import Container from "@mui/material/Container";
import IconButton from "@mui/material/IconButton";
import InputAdornment from "@mui/material/InputAdornment";
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
import CheckIcon from "@mui/icons-material/Check";
import CloseIcon from "@mui/icons-material/Close";
import Visibility from "@mui/icons-material/Visibility";
import VisibilityOff from "@mui/icons-material/VisibilityOff";
import { createTheme, ThemeProvider } from "@mui/material/styles";
Expand All @@ -25,6 +27,7 @@ import MuiAlert, { AlertProps } from "@mui/material/Alert";
import CircularProgress from "@mui/material/CircularProgress";
import { useRouter } from "next/navigation";
import { checkSecurity } from "./handlers";
import PasswordChecklist from "react-password-checklist";

// can change colors if we want
const theme = createTheme({
Expand All @@ -34,20 +37,14 @@ const theme = createTheme({
},
});

/* check that string contains number, upper case, lower case, special character,
and at least 10 characters */
const strongPasswordPattern = new RegExp(
"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^da-zA-Z]).{10,}$"
);

interface SnackbarState extends SnackbarOrigin {
open: boolean;
}

export default function SignUp() {
const router = useRouter();
const [showPassword, setShowPassword] = useState<boolean>(false);
const [isStrongPassword, setIsStrongPassword] = useState<boolean>(false);
const [isValidPassword, setIsValidPassword] = useState<boolean>(false);
const [isValidForm, setIsValidForm] = useState<boolean>(false);
const [first, setFirst] = useState<string>("");
const [last, setLast] = useState<string>("");
Expand All @@ -67,7 +64,9 @@ export default function SignUp() {
setShowSnackbar({ ...showSnackbar, open: false });
};

const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
const handleSubmit = async (
event: FormEvent<HTMLFormElement>
) => {
event.preventDefault();
setIsLoading(true);
const response = await checkSecurity(); // makes an API call to the backend
Expand All @@ -76,10 +75,12 @@ export default function SignUp() {
if (response.status == "success") {
router.push(`/success`);
} else if (response.status == "fail") {
setErrorText("Password was found in a previously breached dataset! Please try a different password.")
setErrorText(
"Password was found in a previously breached dataset! Please try a different password."
);
setShowSnackbar({ ...showSnackbar, open: true });
} else {
setErrorText("Server error: please try again later")
setErrorText("Server error: please try again later");
setShowSnackbar({ ...showSnackbar, open: true });
}
};
Expand All @@ -94,23 +95,21 @@ export default function SignUp() {
};

useEffect(() => {
setIsStrongPassword(strongPasswordPattern.test(password));
setIsValidForm(
first.length > 0 &&
last.length > 0 &&
email.length > 0 &&
password === confirmPassword
);
}, [password, confirmPassword, isValidForm, isStrongPassword]);
setIsValidForm(first.length > 0 && last.length > 0 && email.length > 0);
}, [first, last, email]);

const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
props,
ref
) {
return (
<MuiAlert elevation={6} ref={ref} variant="filled" {...props} />
);
});
const Alert = forwardRef<HTMLDivElement, AlertProps>(
function Alert(props, ref) {
return (
<MuiAlert
elevation={6}
ref={ref}
variant="filled"
{...props}
/>
);
}
);

return (
<ThemeProvider theme={theme}>
Expand Down Expand Up @@ -159,7 +158,9 @@ export default function SignUp() {
id="firstName"
label="First Name"
aria-label="first name"
onChange={(event) => setFirst(event.target.value)}
onChange={(event) =>
setFirst(event.target.value)
}
autoFocus
/>
</Grid>
Expand All @@ -172,7 +173,9 @@ export default function SignUp() {
name="lastName"
aria-label="last name"
autoComplete="family-name"
onChange={(event) => setLast(event.target.value)}
onChange={(event) =>
setLast(event.target.value)
}
/>
</Grid>
<Grid item xs={12}>
Expand All @@ -184,7 +187,9 @@ export default function SignUp() {
name="email"
aria-label="email address"
autoComplete="email"
onChange={(event) => setEmail(event.target.value)}
onChange={(event) =>
setEmail(event.target.value)
}
/>
</Grid>
<Grid item xs={12}>
Expand All @@ -196,7 +201,11 @@ export default function SignUp() {
id="password"
autoComplete="new-password"
aria-label="password"
type={showPassword ? "text" : "password"}
type={
showPassword
? "text"
: "password"
}
onChange={(event) =>
setPassword(event.target.value)
}
Expand Down Expand Up @@ -227,7 +236,11 @@ export default function SignUp() {
label="Confirm your password"
id="confirm-password"
aria-label="confirm password"
type={showPassword ? "text" : "password"}
type={
showPassword
? "text"
: "password"
}
onChange={(event) =>
setConfirmPassword(event.target.value)
}
Expand All @@ -254,16 +267,35 @@ export default function SignUp() {
aria-label="password requirements"
component="div"
variant="inherit"
className="px-4"
className="p-4"
>
<p className="-mb-2">Password must contain:</p>
<ul>
<li>At least 10 characters</li>
<li>At least 1 lowercase character</li>
<li>At least 1 uppercase character</li>
<li>At least 1 number</li>
<li>At least 1 special character</li>
</ul>
<PasswordChecklist
rules={[
"minLength",
"lowercase",
"capital",
"number",
"specialChar",
"match",
]}
messages={{
specialChar: "Password has a special character.",
}}
minLength={10}
value={password}
valueAgain={confirmPassword}
onChange={(isValid) =>
setIsValidPassword(isValid)
}
iconComponents={{
ValidIcon: (
<CheckIcon className="text-green-500 pt-1" />
),
InvalidIcon: (
<CloseIcon className="opacity-70 pt-1" />
),
}}
/>
</Typography>
</Grid>
<Button
Expand All @@ -273,7 +305,9 @@ export default function SignUp() {
variant="contained"
sx={{ mt: 3, mb: 2 }}
disabled={
!isStrongPassword || !isValidForm || isLoading
!isValidPassword ||
!isValidForm ||
isLoading
}
>
{isLoading ? (
Expand Down
5 changes: 3 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"next": "^13.5.4",
"node-fetch": "^2.6.7",
"react": "^18",
"react-dom": "^18"
"react-dom": "^18",
"react-password-checklist": "^1.5.0"
},
"devDependencies": {
"@types/node": "^20",
Expand All @@ -30,4 +31,4 @@
"tailwindcss": "^3",
"typescript": "^5"
}
}
}
Loading

0 comments on commit 77c3288

Please sign in to comment.