mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 12:43:11 +10:00
Add email not verified
Also add a generic error view.
This commit is contained in:
parent
968673c830
commit
ae44704a62
6 changed files with 109 additions and 48 deletions
|
|
@ -43,7 +43,7 @@ describe('Application', () => {
|
|||
'/u/1234': 'Profile page with display name: 1234',
|
||||
'/login': 'Login',
|
||||
'/signup': 'Sign up',
|
||||
'/recover-password': 'Request new password',
|
||||
'/recover-password': 'Request a new password',
|
||||
'/this-url-should-not-be-found': 'Page not found',
|
||||
'/reset-password?t=token&e=email': 'Reset password',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,7 +29,12 @@ export default function reducer(state = initialState, action = {}) {
|
|||
case RECOVERY_SUCCESS:
|
||||
return { ...state, submittedEmail: payload.email, recoveryInProgress: false };
|
||||
case RECOVERY_ERROR:
|
||||
return { ...state, recoveryInProgress: false, recoveryError: payload };
|
||||
return {
|
||||
...state,
|
||||
recoveryInProgress: false,
|
||||
recoveryError: payload.error,
|
||||
submittedEmail: payload.email,
|
||||
};
|
||||
case RETYPE_EMAIL:
|
||||
return { ...state, submittedEmail: null, initialEmail: state.submittedEmail };
|
||||
case CLEAR_RECOVERY_ERROR:
|
||||
|
|
@ -43,9 +48,9 @@ export default function reducer(state = initialState, action = {}) {
|
|||
|
||||
export const recoveryRequest = () => ({ type: RECOVERY_REQUEST });
|
||||
export const recoverySuccess = email => ({ type: RECOVERY_SUCCESS, payload: { email } });
|
||||
export const recoveryError = error => ({
|
||||
export const recoveryError = (error, email) => ({
|
||||
type: RECOVERY_ERROR,
|
||||
payload: error,
|
||||
payload: { error, email },
|
||||
error: true,
|
||||
});
|
||||
export const retypeEmail = () => ({ type: RETYPE_EMAIL });
|
||||
|
|
@ -60,5 +65,5 @@ export const recoverPassword = email =>
|
|||
return sdk.passwordReset
|
||||
.request({ email })
|
||||
.then(() => dispatch(recoverySuccess(email)))
|
||||
.catch(e => dispatch(recoveryError(e)));
|
||||
.catch(error => dispatch(recoveryError(error, email)));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { PropTypes } from 'react';
|
|||
import { compose } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { sendVerificationEmail } from '../../ducks/user.duck';
|
||||
import { logout, authenticationInProgress } from '../../ducks/Auth.duck';
|
||||
|
|
@ -13,15 +13,6 @@ import { PasswordRecoveryForm } from '../../containers';
|
|||
|
||||
import css from './PasswordRecoveryPage.css';
|
||||
|
||||
const recoveryMessage = submittedEmail => {
|
||||
if (submittedEmail) {
|
||||
const email = <span className={css.submittedEmail}>{submittedEmail}</span>;
|
||||
return <FormattedMessage id="PasswordRecoveryPage.emailSubmittedMessage" values={{ email }} />;
|
||||
} else {
|
||||
return <FormattedMessage id="PasswordRecoveryPage.forgotPasswordMessage" />;
|
||||
}
|
||||
};
|
||||
|
||||
export const PasswordRecoveryPageComponent = props => {
|
||||
const {
|
||||
authInfoError,
|
||||
|
|
@ -45,13 +36,12 @@ export const PasswordRecoveryPageComponent = props => {
|
|||
onChange,
|
||||
onSubmitEmail,
|
||||
onRetypeEmail,
|
||||
intl,
|
||||
} = props;
|
||||
|
||||
const title = submittedEmail
|
||||
? <FormattedMessage id="PasswordRecoveryPage.emailSubmittedTitle" />
|
||||
: <FormattedMessage id="PasswordRecoveryPage.forgotPasswordTitle" />;
|
||||
|
||||
const message = recoveryMessage(submittedEmail);
|
||||
const title = intl.formatMessage({
|
||||
id: 'PasswordRecoveryPage.title',
|
||||
});
|
||||
|
||||
const resendEmailLink = (
|
||||
<InlineTextButton className={css.bottomLink} onClick={() => onSubmitEmail(submittedEmail)}>
|
||||
|
|
@ -65,23 +55,88 @@ export const PasswordRecoveryPageComponent = props => {
|
|||
</InlineTextButton>
|
||||
);
|
||||
|
||||
const emailSubmittedLinks = (
|
||||
<div className={css.bottomWrapper}>
|
||||
<p className={css.bottomText}>
|
||||
<FormattedMessage id="PasswordRecoveryPage.resendEmailInfo" values={{ resendEmailLink }} />
|
||||
const submitEmailContent = (
|
||||
<div className={css.content}>
|
||||
<KeysIcon />
|
||||
<h1 className={css.title}>
|
||||
<FormattedMessage id="PasswordRecoveryPage.forgotPasswordTitle" />
|
||||
</h1>
|
||||
<p><FormattedMessage id="PasswordRecoveryPage.forgotPasswordMessage" /></p>
|
||||
<PasswordRecoveryForm
|
||||
onChange={onChange}
|
||||
onSubmit={values => onSubmitEmail(values.email)}
|
||||
initialValues={{ email: initialEmail }}
|
||||
recoveryError={recoveryError}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const email = <span className={css.submittedEmail}>{submittedEmail}</span>;
|
||||
const emailSubmittedContent = (
|
||||
<div className={css.content}>
|
||||
<KeysIcon />
|
||||
<h1 className={css.title}>
|
||||
<FormattedMessage id="PasswordRecoveryPage.emailSubmittedTitle" />
|
||||
</h1>
|
||||
<p>
|
||||
<FormattedMessage id="PasswordRecoveryPage.emailSubmittedMessage" values={{ email }} />
|
||||
</p>
|
||||
<p className={css.bottomText}>
|
||||
<FormattedMessage id="PasswordRecoveryPage.fixEmailInfo" values={{ fixEmailLink }} />
|
||||
<div className={css.bottomWrapper}>
|
||||
<p className={css.bottomText}>
|
||||
<FormattedMessage
|
||||
id="PasswordRecoveryPage.resendEmailInfo"
|
||||
values={{ resendEmailLink }}
|
||||
/>
|
||||
</p>
|
||||
<p className={css.bottomText}>
|
||||
<FormattedMessage id="PasswordRecoveryPage.fixEmailInfo" values={{ fixEmailLink }} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const emailNotVerifiedContent = (
|
||||
<div className={css.content}>
|
||||
<KeysIcon />
|
||||
<h1 className={css.title}>
|
||||
<FormattedMessage id="PasswordRecoveryPage.emailNotVerifiedTitle" />
|
||||
</h1>
|
||||
<p>
|
||||
<FormattedMessage id="PasswordRecoveryPage.emailNotVerifiedMessage" values={{ email }} />
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage id="PasswordRecoveryPage.emailNotVerifiedContactAdmin" />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const genericErrorContent = (
|
||||
<div className={css.content}>
|
||||
<KeysIcon />
|
||||
<h1 className={css.title}>
|
||||
<FormattedMessage id="PasswordRecoveryPage.actionFailedTitle" />
|
||||
</h1>
|
||||
<p>
|
||||
<FormattedMessage id="PasswordRecoveryPage.actionFailedMessage" />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
let content;
|
||||
if (recoveryError && recoveryError.status === 409) {
|
||||
content = emailNotVerifiedContent;
|
||||
} else if (recoveryError && recoveryError.status === 404) {
|
||||
content = submitEmailContent;
|
||||
} else if (recoveryError) {
|
||||
content = genericErrorContent;
|
||||
} else if (submittedEmail) {
|
||||
content = emailSubmittedContent;
|
||||
} else {
|
||||
content = submitEmailContent;
|
||||
}
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
authInfoError={authInfoError}
|
||||
logoutError={logoutError}
|
||||
title="Request new password"
|
||||
>
|
||||
<PageLayout authInfoError={authInfoError} logoutError={logoutError} title={title}>
|
||||
<Topbar
|
||||
authInProgress={authInProgress}
|
||||
currentUser={currentUser}
|
||||
|
|
@ -98,19 +153,7 @@ export const PasswordRecoveryPageComponent = props => {
|
|||
sendVerificationEmailError={sendVerificationEmailError}
|
||||
/>
|
||||
<div className={css.root}>
|
||||
<div className={css.content}>
|
||||
<KeysIcon />
|
||||
<h1 className={css.title}>{title}</h1>
|
||||
<p>{message}</p>
|
||||
{submittedEmail
|
||||
? emailSubmittedLinks
|
||||
: <PasswordRecoveryForm
|
||||
onChange={onChange}
|
||||
onSubmit={values => onSubmitEmail(values.email)}
|
||||
initialValues={{ email: initialEmail }}
|
||||
recoveryError={recoveryError}
|
||||
/>}
|
||||
</div>
|
||||
{content}
|
||||
</div>
|
||||
|
||||
</PageLayout>
|
||||
|
|
@ -152,6 +195,9 @@ PasswordRecoveryPageComponent.propTypes = {
|
|||
onSubmitEmail: func.isRequired,
|
||||
onRetypeEmail: func.isRequired,
|
||||
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
|
||||
// from withRouter
|
||||
history: shape({
|
||||
push: func.isRequired,
|
||||
|
|
@ -207,8 +253,10 @@ const mapDispatchToProps = dispatch => ({
|
|||
onRetypeEmail: () => dispatch(retypeEmail()),
|
||||
});
|
||||
|
||||
const PasswordRecoveryPage = compose(connect(mapStateToProps, mapDispatchToProps), withRouter)(
|
||||
PasswordRecoveryPageComponent
|
||||
);
|
||||
const PasswordRecoveryPage = compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
withRouter,
|
||||
injectIntl
|
||||
)(PasswordRecoveryPageComponent);
|
||||
|
||||
export default PasswordRecoveryPage;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { renderShallow } from '../../util/test-helpers';
|
||||
import { fakeIntl } from '../../util/test-data';
|
||||
import { PasswordRecoveryPageComponent } from './PasswordRecoveryPage';
|
||||
|
||||
const noop = () => null;
|
||||
|
|
@ -23,6 +24,7 @@ describe('ContactDetailsPage', () => {
|
|||
onChange={noop}
|
||||
onSubmitEmail={noop}
|
||||
onRetypeEmail={noop}
|
||||
intl={fakeIntl}
|
||||
/>
|
||||
);
|
||||
expect(tree).toMatchSnapshot();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ exports[`ContactDetailsPage matches snapshot 1`] = `
|
|||
<withRouter(PageLayout)
|
||||
authInfoError={null}
|
||||
logoutError={null}
|
||||
title="Request new password">
|
||||
title="PasswordRecoveryPage.title">
|
||||
<Topbar
|
||||
authInProgress={false}
|
||||
currentUser={null}
|
||||
|
|
|
|||
|
|
@ -232,6 +232,11 @@
|
|||
"PasswordRecoveryForm.loginLinkText": "Go log in.",
|
||||
"PasswordRecoveryForm.loginLinkInfo": "Suddenly remembered your password? {loginLink}",
|
||||
"PasswordRecoveryForm.sendInstructions": "Send instructions",
|
||||
"PasswordRecoveryPage.actionFailedTitle": "Whoops!",
|
||||
"PasswordRecoveryPage.actionFailedMessage": "Something went wrong. Please refresh the page and try again.",
|
||||
"PasswordRecoveryPage.emailNotVerifiedTitle": "We have bad news",
|
||||
"PasswordRecoveryPage.emailNotVerifiedMessage": "Unfortunately, we cannot recover your password, because your email {email} hasn't been verified.",
|
||||
"PasswordRecoveryPage.emailNotVerifiedContactAdmin": "To resolve the issue, please contact Saunatime administrators.",
|
||||
"PasswordRecoveryPage.emailSubmittedTitle": "Check your inbox",
|
||||
"PasswordRecoveryPage.emailSubmittedMessage": "We have sent the instructions for resetting your password to {email}.",
|
||||
"PasswordRecoveryPage.fixEmailLinkText": "Fix it.",
|
||||
|
|
@ -240,6 +245,7 @@
|
|||
"PasswordRecoveryPage.forgotPasswordMessage": "No worries! Please enter the email address you used when signing up and we'll send you instructions on how to create a new password.",
|
||||
"PasswordRecoveryPage.resendEmailLinkText": "Send another email.",
|
||||
"PasswordRecoveryPage.resendEmailInfo": "Didn't get the email? {resendEmailLink}",
|
||||
"PasswordRecoveryPage.title": "Request a new password",
|
||||
"PayoutDetailsForm.addressTitle": "Address",
|
||||
"PayoutDetailsForm.bankDetails": "Bank details",
|
||||
"PayoutDetailsForm.birthdayDatePlaceholder": "dd",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue