openreplay/frontend/app/components/ForgotPassword/ForgotPassword.js
2023-03-24 15:58:07 +01:00

253 lines
9.3 KiB
JavaScript

import React from 'react';
import { connect } from 'react-redux';
import ReCAPTCHA from 'react-google-recaptcha';
import withPageTitle from 'HOCs/withPageTitle';
import { Form, Input, Loader, Button, Link, Icon, Message } from 'UI';
import { requestResetPassword, resetPassword, resetErrors } from 'Duck/user';
import { login as loginRoute } from 'App/routes';
import { withRouter } from 'react-router-dom';
import { validateEmail } from 'App/validate';
import cn from 'classnames';
import stl from './forgotPassword.module.css';
const LOGIN = loginRoute();
const recaptchaRef = React.createRef();
const ERROR_DONT_MATCH = "Passwords don't match.";
const MIN_LENGTH = 8;
const PASSWORD_POLICY = `Password should contain at least ${MIN_LENGTH} symbols.`;
const checkDontMatch = (newPassword, newPasswordRepeat) =>
newPasswordRepeat.length > 0 && newPasswordRepeat !== newPassword;
@connect(
(state, props) => ({
errors: state.getIn(['user', 'requestResetPassowrd', 'errors']),
resetErrors: state.getIn(['user', 'resetPassword', 'errors']),
loading:
state.getIn(['user', 'requestResetPassowrd', 'loading']) ||
state.getIn(['user', 'resetPassword', 'loading']),
params: new URLSearchParams(props.location.search),
}),
{ requestResetPassword, resetPassword, resetErrors }
)
@withPageTitle('Password Reset - OpenReplay')
@withRouter
export default class ForgotPassword extends React.PureComponent {
state = {
email: '',
code: ' ',
password: '',
passwordRepeat: '',
requested: false,
updated: false,
CAPTCHA_ENABLED: window.env.CAPTCHA_ENABLED === 'true',
};
handleSubmit = (token) => {
const { email, password } = this.state;
const { params } = this.props;
const pass = params.get('pass');
const invitation = params.get('invitation');
const resetting = pass && invitation;
if (!resetting) {
this.props
.requestResetPassword({ email: email.trim(), 'g-recaptcha-response': token })
.then(() => {
const { errors } = this.props;
if (!errors) this.setState({ requested: true });
});
} else {
if (this.isSubmitDisabled()) return;
this.props.resetPassword({ email: email.trim(), invitation, pass, password }).then(() => {
const { resetErrors } = this.props;
if (!resetErrors) this.setState({ updated: true });
});
}
};
isSubmitDisabled() {
const { password, passwordRepeat } = this.state;
if (password !== passwordRepeat || password.length < MIN_LENGTH) return true;
return false;
}
write = ({ target: { value, name } }) => this.setState({ [name]: value });
shouldShouwPolicy() {
const { password } = this.state;
if (password.length > 7) return false;
if (password === '') return false;
return true;
}
onSubmit = (e) => {
e.preventDefault();
const { CAPTCHA_ENABLED } = this.state;
if (CAPTCHA_ENABLED && recaptchaRef.current) {
recaptchaRef.current.execute();
} else if (!CAPTCHA_ENABLED) {
this.handleSubmit();
}
};
componentWillUnmount() {
this.props.resetErrors();
}
render() {
const { CAPTCHA_ENABLED } = this.state;
const { errors, loading, params } = this.props;
const { requested, updated, password, passwordRepeat, email } = this.state;
const dontMatch = checkDontMatch(password, passwordRepeat);
const pass = params.get('pass');
const invitation = params.get('invitation');
const resetting = pass && invitation;
const validEmail = validateEmail(email);
return (
<div className="flex items-center justify-center h-screen">
<div className="flex flex-col items-center">
<div className="m-10 ">
<img src="/assets/logo.svg" width={200} />
</div>
<div className="border rounded bg-white" style={{ width: '350px' }}>
<h2 className="text-center text-2xl font-medium mb-6 border-b p-5 w-full">
{ resetting ? "Create Password" : "Reset Password" }
</h2>
<div className="px-8">
{resetting && <div className="my-6">Provide your email address, so we can send you a link to reset your password.</div> }
<Form
onSubmit={this.onSubmit}
style={{ minWidth: '50%' }}
className="flex flex-col items-center justify-center"
>
{/* <div className="mb-8">
<h2 className="text-center text-3xl mb-6">{`${
resetting ? 'Create' : 'Reset'
} Password`}</h2>
</div> */}
<Loader loading={loading}>
<div data-hidden={updated} className="w-full">
{CAPTCHA_ENABLED && (
<div className={stl.recaptcha}>
<ReCAPTCHA
ref={recaptchaRef}
size="invisible"
data-hidden={requested}
sitekey={window.env.CAPTCHA_SITE_KEY}
onChange={(token) => this.handleSubmit(token)}
/>
</div>
)}
{!resetting && !requested && (
<Form.Field>
<label>{'Email Address:'}</label>
<Input
autoFocus={true}
autocomplete="email"
type="text"
placeholder="Email"
name="email"
onChange={this.write}
className="w-full"
icon="envelope"
/>
</Form.Field>
)}
{requested && !errors && (
<div className="flex flex-col items-center justify-center">
<div className="w-16 h-16 rounded-full bg-tealx-light flex items-center justify-center mb-2">
<Icon name="envelope-check" size={30} color="tealx" />
</div>
<div>Alright! a reset link was emailed to {email}. Click on it to reset your account password.</div>
</div>
)}
{resetting && (
<React.Fragment>
<Form.Field>
<i className={stl.inputIconPassword} />
<Input
autocomplete="new-password"
type="password"
placeholder="Password"
name="password"
onChange={this.write}
className="w-full"
/>
</Form.Field>
<div className={stl.passwordPolicy} data-hidden={!this.shouldShouwPolicy()}>
{PASSWORD_POLICY}
</div>
<Form.Field>
<i className={stl.inputIconPassword} />
<Input
autocomplete="new-password"
type="password"
placeholder="Confirm Password"
name="passwordRepeat"
onChange={this.write}
className="w-full"
/>
</Form.Field>
</React.Fragment>
)}
<Message error hidden={!dontMatch}>
{ERROR_DONT_MATCH}
</Message>
</div>
</Loader>
<div className="mt-4">
{errors && (
<div className={stl.errors}>
{errors.map((error) => (
<span>
{error}
<br />
</span>
))}
</div>
)}
<div data-hidden={!updated} className={stl.success}>
<Icon name="check" size="30" color="green" />
{'Your password has been updated sucessfully.'}
</div>
</div>
{!(updated || requested) && (
<Button
type="submit"
variant="primary"
loading={loading}
className="w-full"
disabled={(resetting && this.isSubmitDisabled()) || (!resetting && !validEmail)}
>
{resetting ? 'Create' : 'Email password reset link'}
</Button>
)}
<div className="my-8">
<Link to={LOGIN}>
{updated && (
<Button variant="primary" type="submit" primary>
{'Login'}
</Button>
)}
<div data-hidden={updated} className="link">{'Back to Login'}</div>
</Link>
</div>
</Form>
</div>
</div>
</div>
</div>
);
}
}