cspace-ui
Version:
CollectionSpace user interface for browsers
205 lines (173 loc) • 6.03 kB
JSX
/* global window, FormData */
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
import {
defineMessages,
FormattedMessage,
injectIntl,
intlShape,
} from 'react-intl';
import { isValidEmail } from '../../../helpers/validationHelpers';
const messages = defineMessages({
title: {
id: 'passwordResetRequestPage.title',
description: 'Title of the password reset request page.',
defaultMessage: 'Reset Password',
},
prompt: {
id: 'passwordResetRequestPage.prompt',
description: 'The prompt displayed on the password reset request page.',
defaultMessage: 'Please enter your email address to request a password reset.',
},
email: {
id: 'passwordResetRequestPage.email',
description: 'Label for the email field on the password reset request page.',
defaultMessage: 'Email',
},
submit: {
id: 'passwordResetRequestPage.submit',
description: 'Label for the submit button on the password reset request page.',
defaultMessage: 'Submit',
},
success: {
id: 'passwordResetRequestPage.success',
description: 'Message displayed when a password reset has been successfully requested.',
defaultMessage: 'An email has been sent to {email}. Follow the instructions in the email to finish resetting your password.',
},
error: {
id: 'passwordResetRequestPage.error',
description: 'Generic message to display when a password reset request fails, and no more specific message is available.',
defaultMessage: 'An error occurred while attempting to request the password reset: {detail}',
},
errorNotFound: {
id: 'passwordResetRequestPage.errorNotFound',
description: 'Message to display when the email is not found for a password reset request.',
defaultMessage: 'Could not find an account with the email {email}.',
},
errorMissingEmail: {
id: 'passwordResetRequestPage.errorMissingEmail',
description: 'Message to display when no email is entered on the password reset request page.',
defaultMessage: 'Please enter an email address.',
},
errorInvalidEmail: {
id: 'passwordResetRequestPage.errorInvalidEmail',
description: 'Message to display when the email entered on the password reset request page is not a valid email address.',
defaultMessage: '{email} is not a valid email address.',
},
errorSSORequired: {
id: 'passwordResetRequestPage.errorSSORequired',
description: 'Message to display on the password reset page when the account requires single sign-on.',
defaultMessage: '{email} is required to sign in using a single sign-on provider. The CollectionSpace account password cannot be reset.',
},
});
const propTypes = {
csrf: PropTypes.object,
tenantId: PropTypes.string,
intl: intlShape.isRequired,
};
const defaultProps = {
csrf: null,
tenantId: null,
};
function PasswordResetRequestPage(props) {
const {
csrf,
intl,
tenantId,
} = props;
const [error, setError] = useState();
const [isPending, setPending] = useState();
const [success, setSuccess] = useState();
if (success) {
return (
<p className="status success">{success}</p>
);
}
const handleSubmit = (event) => {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
const email = formData.get('email');
if (!email) {
setError(<FormattedMessage {...messages.errorMissingEmail} />);
return;
}
if (!isValidEmail(email)) {
setError(<FormattedMessage {...messages.errorInvalidEmail} values={{ email }} />);
return;
}
const url = new URL(form.action);
const params = url.searchParams;
if (tenantId) {
params.set('tid', tenantId);
}
params.set('email', email);
if (csrf) {
params.set(csrf.parameterName, csrf.token);
}
setError(null);
setPending(true);
window.fetch(url, {
method: 'POST',
})
.then((response) => Promise.all([response, response.text()]))
.then(([response, text]) => {
if (response.ok) {
setSuccess(<FormattedMessage {...messages.success} values={{ email: text }} />);
setError(null);
} else {
setSuccess(null);
if (response.status === 404) {
setError(<FormattedMessage {...messages.errorNotFound} values={{ email }} />);
} else if (/requires single sign-on/.test(text)) {
setError(<FormattedMessage {...messages.errorSSORequired} values={{ email }} />);
} else {
setError(<FormattedMessage {...messages.error} values={{ detail: text }} />);
}
}
})
.catch((err) => {
setError(<FormattedMessage {...messages.error} values={{ detail: err.message }} />);
})
.finally(() => {
setPending(false);
});
};
const errorMessage = error
? <p className="status error">{error}</p>
: undefined;
return (
<>
<Helmet>
<title>{intl.formatMessage(messages.title)}</title>
</Helmet>
{errorMessage}
<main>
<p><FormattedMessage {...messages.prompt} /></p>
<form method="POST" onSubmit={handleSubmit}>
<div>
{/* Ignore an eslint misfire. */}
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label>
<FormattedMessage {...messages.email} />
<input
autoComplete="email"
name="email"
type="text"
/>
</label>
</div>
<div>
<button className="send" disabled={isPending} type="submit">
<FormattedMessage {...messages.submit} />
</button>
</div>
</form>
</main>
</>
);
}
PasswordResetRequestPage.propTypes = propTypes;
PasswordResetRequestPage.defaultProps = defaultProps;
export default injectIntl(PasswordResetRequestPage);