cspace-ui
Version:
CollectionSpace user interface for browsers
330 lines (282 loc) • 8.82 kB
JSX
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { defineMessages, intlShape, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import get from 'lodash/get';
import { components as inputComponents } from 'cspace-input';
import Notification from '../notification/Notification';
import getErrorDescription from '../../helpers/getErrorDescription';
import { isValidPassword } from '../../helpers/validationHelpers';
import {
ERR_MISSING_PW,
ERR_MISSING_PW_CONFIRM,
ERR_INVALID_PW,
ERR_PW_NOT_CONFIRMED,
} from '../../constants/errorCodes';
const { Button, PasswordInput } = inputComponents;
const messages = defineMessages({
prompt: {
id: 'passwordResetForm.prompt',
description: 'The prompt displayed on the password reset form.',
defaultMessage: 'Enter the new password for this account.',
},
password: {
id: 'passwordResetForm.password',
description: 'Label for the password field on the password reset form.',
defaultMessage: 'Password',
},
confirmPassword: {
id: 'passwordResetForm.confirmPassword',
description: 'Label for the confirm password field on the password reset form.',
defaultMessage: 'Confirm password',
},
submit: {
id: 'passwordResetForm.submit',
description: 'Label for the submit button on the password reset form.',
defaultMessage: 'Submit',
},
success: {
id: 'passwordResetForm.success',
description: 'Message displayed when a password reset has been successfully completed.',
defaultMessage: 'Your password has been reset. {loginLink} to continue.',
},
loginLink: {
id: 'passwordResetForm.loginLink',
description: 'Text of the link to the login page displayed after a password has been reset.',
defaultMessage: 'Sign in',
},
newRequestLink: {
id: 'passwordResetForm.newRequestLink',
description: 'Text of the link to make a new password reset request, displayed in the error message when a token is invalid or has expired.',
defaultMessage: 'make a new request',
},
error: {
id: 'passwordResetForm.error',
description: 'Generic message to display when a password reset fails, and no more specific message is available.',
defaultMessage: 'An error occurred while attempting to reset the password: {detail}',
},
errorMissingPassword: {
id: 'passwordResetForm.errorMissingPassword',
description: 'Message to display when no password is entered on the password reset form.',
defaultMessage: 'Please enter a new password.',
},
errorMissingConfirmation: {
id: 'passwordResetForm.errorMissingConfirmation',
description: 'Message to display when no password confirmation is entered on the password reset form.',
defaultMessage: 'Please confirm the new password.',
},
errorNotConfirmed: {
id: 'passwordResetForm.errorNotConfirmed',
description: 'Message to display when the password confirmation does not match the password on the password reset form.',
defaultMessage: 'The password was not correctly confirmed.',
},
errorInvalidPassword: {
id: 'passwordResetForm.errorInvalidPassword',
description: 'Message to display when the password is invalid on the password reset form.',
defaultMessage: 'The password must be between 8 and 24 characters.',
},
errorTokenExpired: {
id: 'passwordResetForm.errorTokenExpired',
description: 'Message to display when the password reset token has expired on the password reset form.',
defaultMessage: 'The password reset request has expired. Please {newRequestLink} to reset your password.',
},
errorTokenInvalid: {
id: 'passwordResetForm.errorTokenInvalid',
description: 'Message to display when the password reset token is invalid on the password reset form.',
defaultMessage: 'The password reset request could not be validated. Please {newRequestLink} to reset your password.',
},
});
const propTypes = {
token: PropTypes.string.isRequired,
reset: PropTypes.func.isRequired,
};
const contextTypes = {
intl: intlShape,
};
export default class PasswordResetForm extends Component {
constructor() {
super();
this.handleConfirmPasswordCommit = this.handleConfirmPasswordCommit.bind(this);
this.handlePasswordCommit = this.handlePasswordCommit.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {};
}
handleConfirmPasswordCommit(path, value) {
this.setState({
confirmPassword: value,
});
}
handlePasswordCommit(path, value) {
this.setState({
password: value,
});
}
handleSubmit(event) {
event.preventDefault();
const {
password,
confirmPassword,
} = this.state;
if (!password) {
this.setState({
error: {
code: ERR_MISSING_PW,
},
});
return;
}
if (!confirmPassword) {
this.setState({
error: {
code: ERR_MISSING_PW_CONFIRM,
},
});
return;
}
if (password !== confirmPassword) {
this.setState({
error: {
code: ERR_PW_NOT_CONFIRMED,
},
});
return;
}
if (!isValidPassword(password)) {
this.setState({
error: {
code: ERR_INVALID_PW,
},
});
return;
}
const {
reset,
token,
} = this.props;
this.setState({
error: null,
isPending: true,
isSuccess: false,
});
reset(password, token)
.then(() => {
this.setState({
error: null,
isPending: false,
isSuccess: true,
});
})
.catch((error) => {
this.setState({
error,
isPending: false,
isSuccess: false,
});
});
}
renderError() {
const {
error,
} = this.state;
const {
intl,
} = this.context;
if (!error) {
return undefined;
}
let message;
const values = {};
const {
code,
} = error;
if (code === ERR_MISSING_PW) {
message = messages.errorMissingPassword;
} else if (code === ERR_MISSING_PW_CONFIRM) {
message = messages.errorMissingConfirmation;
} else if (code === ERR_PW_NOT_CONFIRMED) {
message = messages.errorNotConfirmed;
} else if (code === ERR_INVALID_PW) {
message = messages.errorInvalidPassword;
} else {
const statusCode = get(error, ['response', 'status']);
if (statusCode === 400 || statusCode === 500) {
const data = get(error, ['response', 'data']);
if (/token .* not valid/.test(data)) {
message = messages.errorTokenInvalid;
values.newRequestLink = (
<Link to="/resetpw">{intl.formatMessage(messages.newRequestLink)}</Link>
);
} else if (/token .* expired/.test(data)) {
message = messages.errorTokenExpired;
values.newRequestLink = (
<Link to="/resetpw">{intl.formatMessage(messages.newRequestLink)}</Link>
);
}
}
}
if (!message) {
message = messages.error;
values.detail = getErrorDescription(error);
}
return (
<Notification
id="passwordResetForm.error"
items={[{
message,
values,
}]}
showCloseButton={false}
status="error"
/>
);
}
render() {
const {
isPending,
isSuccess,
password,
confirmPassword,
} = this.state;
const {
intl,
} = this.context;
const errorMessage = this.renderError();
if (isSuccess) {
const loginLink = <Link to="/login">{intl.formatMessage(messages.loginLink)}</Link>;
return (
<FormattedMessage {...messages.success} values={{ loginLink }} />
);
}
return (
<form onSubmit={this.handleSubmit}>
<p>
<FormattedMessage {...messages.prompt} />
</p>
<div>
<PasswordInput
autoComplete="new-password"
name="password"
placeholder={intl.formatMessage(messages.password)}
value={password}
onCommit={this.handlePasswordCommit}
/>
</div>
<div>
<PasswordInput
autoComplete="new-password"
name="confirmPassword"
placeholder={intl.formatMessage(messages.confirmPassword)}
value={confirmPassword}
onCommit={this.handleConfirmPasswordCommit}
/>
</div>
<Button type="submit" disabled={isPending}>
<FormattedMessage {...messages.submit} />
</Button>
{errorMessage}
</form>
);
}
}
PasswordResetForm.propTypes = propTypes;
PasswordResetForm.contextTypes = contextTypes;