Skip to content

Commit

Permalink
fix: review changes
Browse files Browse the repository at this point in the history
  • Loading branch information
LuukBlankenstijn committed Dec 18, 2024
1 parent f957ecc commit 2255547
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 98 deletions.
110 changes: 110 additions & 0 deletions src/components/auth/PasswordStrength.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { Icon } from 'semantic-ui-react';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import validator from 'validator';

interface PasswordStrengthProps {
password: string;
setPasswordIsValid: (valid: boolean) => void;
}

function PasswordStrength(props: PasswordStrengthProps) {
const { password, setPasswordIsValid } = props;
const { t } = useTranslation();

const [eightCharacters, setEightCharacters] = useState(false);
const [lowerCase, setLowerCase] = useState(false);
const [upperCase, setUpperCase] = useState(false);
const [numbers, setNumbers] = useState(false);
const [symbols, setSymbols] = useState(false);

const hasEightCharacters = (p: string) => {
return p.length >= 8;
};
const hasLowerCase = (p: string) => {
return /[a-z]/.test(p);
};
const hasUpperCase = (p: string) => {
return /[A-Z]/.test(p);
};
const hasNumbers = (p: string) => {
return /[0-9]/.test(p);
};
const hasSymbols = (p: string) => {
return validator.isStrongPassword(p, {
minLength: 0,
minLowercase: 0,
minUppercase: 0,
minNumbers: 0,
minSymbols: 1,
});
};
const validatePassword = (p: string) => {
const passwordHasEightCharacters = hasEightCharacters(p);
const passwordHasLowerCase = hasLowerCase(p);
const passwordHasUpperCase = hasUpperCase(p);
const passwordHasNumbers = hasNumbers(p);
const passwordHasSymbols = hasSymbols(p);
setEightCharacters(passwordHasEightCharacters);
setLowerCase(passwordHasLowerCase);
setUpperCase(passwordHasUpperCase);
setNumbers(passwordHasNumbers);
setSymbols(passwordHasSymbols);
setPasswordIsValid(passwordHasEightCharacters && passwordHasLowerCase && passwordHasUpperCase && passwordHasNumbers && passwordHasSymbols);
};

useEffect(() => {
validatePassword(password);
}, [password]);

return (
<>
<h3>
{t('pages.resetPassword.requirements.header')}
</h3>
<table
style={{
textAlign: 'left',
marginLeft: 'auto',
marginRight: 'auto',
marginBottom: '1em',
}}
>
<tbody>
<tr>
<td>
{eightCharacters ? <Icon name="check" color="green"/> : <Icon name="close" color="red"/>}
</td>
<td>{t('pages.resetPassword.requirements.length')}</td>
</tr>
<tr>
<td>
{lowerCase ? <Icon name="check" color="green"/> : <Icon name="close" color="red"/>}
</td>
<td>{t('pages.resetPassword.requirements.lowerCase')}</td>
</tr>
<tr>
<td>
{upperCase ? <Icon name="check" color="green"/> : <Icon name="close" color="red"/>}
</td>
<td>{t('pages.resetPassword.requirements.upperCase')}</td>
</tr>
<tr>
<td>
{numbers ? <Icon name="check" color="green"/> : <Icon name="close" color="red"/>}
</td>
<td>{t('pages.resetPassword.requirements.number')}</td>
</tr>
<tr>
<td>
{symbols ? <Icon name="check" color="green"/> : <Icon name="close" color="red"/>}
</td>
<td>{t('pages.resetPassword.requirements.symbol')}</td>
</tr>
</tbody>
</table>
</>
);
}

export default PasswordStrength;
11 changes: 8 additions & 3 deletions src/components/auth/ResetPasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ import { useTranslation } from 'react-i18next';
import { authResetPassword } from '../../stores/auth/actionCreators';
import ResourceStatus from '../../stores/resourceStatus';
import { RootState } from '../../stores/store';
import PasswordStrength from './PasswordStrength';

interface Props {
token: string;
status: ResourceStatus;
resetPassword: (password: string, passwordRepeat: string, token: string) => void;
validatePassword: (password: string) => void;
}

function ResetPasswordForm(props: Props) {
const { t } = useTranslation();
const [password, changePassword] = useState('');
const [passwordIsValid, setPasswordIsValid] = useState(false);
const [passwordRepeat, changePasswordRepeat] = useState('');

return (
Expand All @@ -37,7 +38,6 @@ function ResetPasswordForm(props: Props) {
}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
changePassword(e.target.value);
props.validatePassword(e.target.value);
}}
/>
<Form.Field
Expand All @@ -48,16 +48,21 @@ function ResetPasswordForm(props: Props) {
icon="lock"
iconPosition="left"
error={
validator.isEmpty(passwordRepeat) || !validator.equals(passwordRepeat, password)
!passwordIsValid || !validator.equals(passwordRepeat, password)
}
label={t('pages.resetPassword.repeatPassword')}
onChange={(e: ChangeEvent<HTMLInputElement>) => changePasswordRepeat(e.target.value)}
/>
<PasswordStrength
password={password}
setPasswordIsValid={setPasswordIsValid}
/>
<Button
fluid
primary
size="large"
type="submit"
disabled={validator.isEmpty(passwordRepeat) || !validator.equals(passwordRepeat, password) || !validator.isStrongPassword(password)}
onClick={() => props.resetPassword(password, passwordRepeat, props.token)}
loading={props.status === ResourceStatus.FETCHING}
>
Expand Down
14 changes: 9 additions & 5 deletions src/components/auth/SetupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Dispatch } from 'redux';
import { connect } from 'react-redux';
import { authSetup } from '../../stores/auth/actionCreators';
import validator from 'validator';
import PasswordStrength from './PasswordStrength';

interface Props {
setup: (
Expand All @@ -24,23 +25,24 @@ function SetupForm(props: Props) {
const [firstName, changeFirstName] = useState('');
const [preposition, changePreposition] = useState('');
const [lastName, changeLastName] = useState('');
const [gender, changeGender] = useState(Gender.UNKNOWN);
const [gender, changeGender] = useState(Gender.MALE);
const [password, changePassword] = useState('');
const [rememberMe, changeRememberMe] = useState(false);
const [passwordIsValid, setPasswordIsValid] = useState(false);

const inputRef = useRef<Input>(null);
useEffect(() => {
inputRef.current!.focus();
}, []);

const formHasErrors = () =>{
return !validator.isEmail(email) || !validator.isStrongPassword(password);
return !validator.isEmail(email) || validator.isEmpty(firstName) || validator.isEmpty(lastName) || !passwordIsValid || gender == Gender.UNKNOWN;
};

return (
<Form
error={
!validator.isStrongPassword(password) || !validator.isEmpty(email)
formHasErrors()
}
onSubmit={() => {
if (!formHasErrors()) {
Expand Down Expand Up @@ -103,7 +105,7 @@ function SetupForm(props: Props) {
]}
onChange={(_e, data) => changeGender(data.value as Gender)}
required={true}
color='purple'
value={gender}
/>
</Form.Field>
<Form.Field
Expand All @@ -116,9 +118,10 @@ function SetupForm(props: Props) {
onChange={(e: ChangeEvent<HTMLInputElement>) => changePassword(e.target.value)}
required={true}
error={
!validator.isStrongPassword(password)
!passwordIsValid
}
/>
<PasswordStrength password={password} setPasswordIsValid={setPasswordIsValid} />
<Form.Field>
<Checkbox
toggle
Expand All @@ -133,6 +136,7 @@ function SetupForm(props: Props) {
primary
size="large"
type="submit"
disabled={formHasErrors()}
>
{t('pages.setup')}
</Button>
Expand Down
105 changes: 15 additions & 90 deletions src/pages/ResetPasswordPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import ResourceStatus from '../stores/resourceStatus';
import CenterInPage from '../components/CenterInPage';
import { useTitle } from '../components/TitleContext';
import { withRouter } from '../WithRouter';
import PasswordStrength from '../components/auth/PasswordStrength';

Check warning on line 23 in src/pages/ResetPasswordPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint

'PasswordStrength' is defined but never used

interface Props {
status: ResourceStatus;
Expand All @@ -29,61 +30,29 @@ interface Props {

function ResetPasswordPage(props: Props) {
const location = useLocation();

Check warning on line 32 in src/pages/ResetPasswordPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint

'location' is assigned a value but never used
const { token } = queryString.parse(location.search);
// const { token } = queryString.parse(location.search);
const { t } = useTranslation();
const { setTitle } = useTitle();

const [eightCharacters, changeEightCharacters] = useState(false);
const [lowerCase, changeLowerCase] = useState(false);
const [upperCase, changeUpperCase] = useState(false);
const [numbers, changeNumbers] = useState(false);
const [symbols, changeSymbols] = useState(false);
const [passwordIsValid, setPasswordIsValid] = useState(false);

Check warning on line 37 in src/pages/ResetPasswordPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint

'passwordIsValid' is assigned a value but never used

Check warning on line 37 in src/pages/ResetPasswordPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint

'setPasswordIsValid' is assigned a value but never used

useEffect(() => {
props.clearStatus();
setTitle(t('pages.resetPassword.title'));
}, []);

const token = 'test';
if (typeof token !== 'string') {
return <Redirect to="/login" />;
// return <Redirect to="/login" />;
}
//
// const payload = jose.decodeJwt(token);
// if (!(payload)) {
// return <Redirect to="/login" />;
// }

const payload = jose.decodeJwt(token);
if (!(payload)) {
return <Redirect to="/login" />;
}

const hasEightCharacters = (password: string) => {
changeEightCharacters(password.length >= 8);
};

const hasLowerCase = (password: string) => {
changeLowerCase(/[a-z]/.test(password));
};
const hasUpperCase = (password: string) => {
changeUpperCase(/[A-Z]/.test(password));
};
const hasNumbers = (password: string) => {
changeNumbers(/[0-9]/.test(password));
};
const hasSymbols = (password: string) => {
changeSymbols(validator.isStrongPassword(password, {
minLength: 0,
minLowercase: 0,
minUppercase: 0,
minNumbers: 0,
minSymbols: 1,
}));
};
const validatePassword = (password: string) => {
hasEightCharacters(password);
hasLowerCase(password);
hasUpperCase(password);
hasNumbers(password);
hasSymbols(password);
};

const newUser = payload.type === 'PASSWORD_SET';
// const newUser = payload.type === 'PASSWORD_SET';
const newUser = true;

if (props.status === ResourceStatus.FETCHED) {
return (
Expand Down Expand Up @@ -118,56 +87,12 @@ function ResetPasswordPage(props: Props) {
<AlertContainer internal />
<Container>
<CenterInPage>
<Header as="h1">
{newUser ? t('pages.resetPassword.setPassword') : t('pages.resetPassword.resetPassword')}
</Header>
<Segment>
<h3>
{t('pages.resetPassword.requirements.header')}
</h3>
<p>
<table
style={{
textAlign: 'left',
marginLeft: 'auto',
marginRight: 'auto',
}}
>
<tr>
<td>
{eightCharacters ? <Icon name="check" color="green" /> : <Icon name="close" color="red" />}
</td>
<td>{t('pages.resetPassword.requirements.length')}</td>
</tr>
<tr>
<td>
{lowerCase ? <Icon name="check" color="green" /> : <Icon name="close" color="red" />}
</td>
<td>{t('pages.resetPassword.requirements.lowerCase')}</td>
</tr>
<tr>
<td>
{upperCase ? <Icon name="check" color="green" /> : <Icon name="close" color="red" />}
</td>
<td>{t('pages.resetPassword.requirements.upperCase')}</td>
</tr>
<tr>
<td>
{numbers ? <Icon name="check" color="green" /> : <Icon name="close" color="red" />}
</td>
<td>{t('pages.resetPassword.requirements.number')}</td>
</tr>
<tr>
<td>
{symbols ? <Icon name="check" color="green" /> : <Icon name="close" color="red" />}
</td>
<td>{t('pages.resetPassword.requirements.symbol')}</td>
</tr>
</table>
</p>
<Header as="h1">
{newUser ? t('pages.resetPassword.setPassword') : t('pages.resetPassword.resetPassword')}
</Header>
<ResetPasswordForm
token={token}
validatePassword={validatePassword}
/>
<Button as={NavLink} to="/login" style={{ marginTop: '1em' }} basic>
<Icon name="arrow left" basic />
Expand Down

0 comments on commit 2255547

Please sign in to comment.