Auth Refactor part 2

This commit is contained in:
Chi Vinh Le
2018-02-07 18:42:03 +01:00
parent f8e0556904
commit ee3d576a49
12 changed files with 381 additions and 123 deletions
@@ -0,0 +1,20 @@
.header, .cta, .success {
text-align: center;
font-size: 16px;
}
.success {
cursor: pointer;
padding: 8px 14px;
}
.signInLink {
color: blue;
font-weight: normal;
text-decoration: none;
}
.signInLink:hover {
text-decoration: underline;
}
@@ -0,0 +1,81 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './ForgotPassword.css';
import { Button, TextField, Alert, Success } from 'coral-ui';
class ForgotPassword extends React.Component {
constructor(props) {
super(props);
}
handleEmailChange = e => this.props.onEmailChange(e.target.value);
handleSubmit = e => {
e.preventDefault();
this.props.onSubmit();
};
handleSignInLink = e => {
e.preventDefault();
this.props.onSignInLink();
};
renderSuccess() {
return (
<div className={styles.success} onClick={this.handleSignInLink}>
{this.props.success}{' '}
<a
className={styles.signInLink}
href="#"
onClick={this.handleSignInLink}
>
Sign in
</a>
<Success />
</div>
);
}
renderForm() {
const { email, errorMessage } = this.props;
return (
<form onSubmit={this.handleSubmit}>
{errorMessage && <Alert>{errorMessage}</Alert>}
<TextField
label="Email Address"
value={email}
onChange={this.handleEmailChange}
/>
<Button type="submit" cStyle="black" full>
Reset Password
</Button>
<p className={styles.cta}>
Go back to{' '}
<a
href="#"
className={styles.signInLink}
onClick={this.handleSignInLink}
>
Sign In
</a>
.
</p>
</form>
);
}
render() {
return this.props.success ? this.renderSuccess() : this.renderForm();
}
}
ForgotPassword.propTypes = {
success: PropTypes.bool.isRequired,
email: PropTypes.string.isRequired,
onEmailChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
errorMessage: PropTypes.string.isRequired,
onSignInLink: PropTypes.func.isRequired,
};
export default ForgotPassword;
+2 -21
View File
@@ -1,37 +1,18 @@
.layout {
max-width: 800px;
margin: 0 auto;
}
.loginLayout {
max-width: 400px;
margin: 0 auto;
}
.loginHeader, .loginCTA, .forgotPasswordCTA, .passwordRequestSuccess {
.header, .cta {
text-align: center;
font-size: 16px;
}
.forgotPasswordLink, .signInLink {
color: blue;
font-weight: normal;
text-decoration: none;
}
.forgotPasswordLink:hover, .signInLink:hover {
text-decoration: underline;
}
.layout h1 {
font-size: 40px;
}
.loginHeader {
.header {
font-size: 30px;
}
.passwordRequestSuccess {
cursor: pointer;
padding: 8px 14px;
}
+20 -72
View File
@@ -1,89 +1,37 @@
import React from 'react';
import React, { Component } from 'react';
import SignIn from '../containers/SignIn';
import ForgotPassword from '../containers/ForgotPassword';
import PropTypes from 'prop-types';
import Layout from 'coral-admin/src/components/Layout';
import styles from './Login.css';
import { Button, TextField, Alert } from 'coral-ui';
import Layout from 'coral-admin/src/components/Layout';
import cn from 'classnames';
class AdminLogin extends React.Component {
constructor(props) {
super(props);
class LoginContainer extends Component {
renderForm() {
return this.props.forgotPassword ? (
<ForgotPassword onSignInLink={this.props.onSignInLink} />
) : (
<SignIn onForgotPasswordLink={this.props.onForgotPasswordLink} />
);
}
handleForgotPassword = e => {
e.preventDefault();
this.props.onForgotPassword();
};
handleEmailChange = e => this.props.onEmailChange(e.target.value);
handlePasswordChange = e => this.props.onPasswordChange(e.target.value);
handleSubmit = e => {
e.preventDefault();
this.props.onSubmit();
};
render() {
const { email, password, errorMessage } = this.props;
return (
<Layout fixedDrawer restricted={true}>
<div className={cn(styles.loginLayout, 'talk-admin-login')}>
<h1 className={styles.loginHeader}>Team sign in</h1>
<p className={styles.loginCTA}>
Sign in to interact with your community.
</p>
<form
className="talk-admin-login-sign-in"
onSubmit={this.handleSubmit}
>
{errorMessage && <Alert>{errorMessage}</Alert>}
<TextField
id="email"
label="Email Address"
value={email}
onChange={this.handleEmailChange}
/>
<TextField
id="password"
label="Password"
value={password}
onChange={this.handlePasswordChange}
type="password"
/>
<div style={{ height: 10 }} />
<Button
className="talk-admin-login-sign-in-button"
type="submit"
cStyle="black"
full
>
Sign In
</Button>
<p className={styles.forgotPasswordCTA}>
Forgot your password?{' '}
<a
href="#"
className={styles.forgotPasswordLink}
onClick={this.handleForgotPassword}
>
Request a new one.
</a>
</p>
</form>
<div className={cn(styles.layout, 'talk-admin-login')}>
<h1 className={styles.header}>Team sign in</h1>
<p className={styles.cta}>Sign in to interact with your community.</p>
{this.renderForm()}
</div>
</Layout>
);
}
}
AdminLogin.propTypes = {
email: PropTypes.string,
password: PropTypes.string,
onEmailChange: PropTypes.func,
onPasswordChange: PropTypes.func,
onForgotPassword: PropTypes.func,
onSubmit: PropTypes.func,
errorMessage: PropTypes.string,
requireRecaptcha: PropTypes.string,
LoginContainer.propTypes = {
forgotPassword: PropTypes.bool.isRequired,
onForgotPasswordLink: PropTypes.func.isRequired,
onSignInLink: PropTypes.func.isRequired,
};
export default AdminLogin;
export default LoginContainer;
@@ -0,0 +1,14 @@
.forgotPasswordCTA {
text-align: center;
font-size: 16px;
}
.forgotPasswordLink:hover {
text-decoration: underline;
}
.forgotPasswordLink {
color: blue;
font-weight: normal;
text-decoration: none;
}
@@ -0,0 +1,76 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './SignIn.css';
import { Button, TextField, Alert } from 'coral-ui';
class SignIn extends React.Component {
constructor(props) {
super(props);
}
handleForgotPasswordLink = e => {
e.preventDefault();
this.props.onForgotPasswordLink();
};
handleEmailChange = e => this.props.onEmailChange(e.target.value);
handlePasswordChange = e => this.props.onPasswordChange(e.target.value);
handleSubmit = e => {
e.preventDefault();
this.props.onSubmit();
};
render() {
const { email, password, errorMessage } = this.props;
return (
<form className="talk-admin-login-sign-in" onSubmit={this.handleSubmit}>
{errorMessage && <Alert>{errorMessage}</Alert>}
<TextField
id="email"
label="Email Address"
value={email}
onChange={this.handleEmailChange}
/>
<TextField
id="password"
label="Password"
value={password}
onChange={this.handlePasswordChange}
type="password"
/>
<div style={{ height: 10 }} />
<Button
className="talk-admin-login-sign-in-button"
type="submit"
cStyle="black"
full
>
Sign In
</Button>
<p className={styles.forgotPasswordCTA}>
Forgot your password?{' '}
<a
href="#"
className={styles.forgotPasswordLink}
onClick={this.handleForgotPasswordLink}
>
Request a new one.
</a>
</p>
</form>
);
}
}
SignIn.propTypes = {
email: PropTypes.string.isRequired,
password: PropTypes.string.isRequired,
onEmailChange: PropTypes.func.isRequired,
onPasswordChange: PropTypes.func.isRequired,
onForgotPasswordLink: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
errorMessage: PropTypes.string.isRequired,
requireRecaptcha: PropTypes.bool.isRequired,
};
export default SignIn;
@@ -0,0 +1,42 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withForgotPassword } from 'coral-framework/hocs';
import { compose } from 'recompose';
import ForgotPassword from '../components/ForgotPassword';
class ForgotPasswordContainer extends Component {
state = {
email: '',
password: '',
};
handleSubmit = () => {
this.props.forgotPassword(this.state.email);
};
handleEmailChange = email => {
this.setState({ email });
};
render() {
return (
<ForgotPassword
onSubmit={this.handleSubmit}
onEmailChange={this.handleEmailChange}
email={this.state.email}
errorMessage={this.props.errorMessage}
success={this.props.success}
onSignInLink={this.props.onSignInLink}
/>
);
}
}
ForgotPasswordContainer.propTypes = {
success: PropTypes.bool.isRequired,
forgotPassword: PropTypes.func.isRequired,
errorMessage: PropTypes.string.isRequired,
onSignInLink: PropTypes.func.isRequired,
};
export default compose(withForgotPassword)(ForgotPasswordContainer);
+10 -24
View File
@@ -1,44 +1,30 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withLogin } from 'coral-framework/hocs';
import { compose } from 'recompose';
import Login from '../components/Login';
class LoginContainer extends Component {
state = {
email: '',
password: '',
forgotPassword: false,
};
handleSubmit = () => {
this.props.login(this.state.email, this.state.password);
switchToForgotPassword = () => {
this.setState({ forgotPassword: true });
};
handleEmailChange = email => {
this.setState({ email });
};
handlePasswordChange = password => {
this.setState({ password });
switchToSignIn = () => {
this.setState({ forgotPassword: false });
};
render() {
return (
<Login
onSubmit={this.handleSubmit}
onEmailChange={this.handleEmailChange}
onPasswordChange={this.handlePasswordChange}
email={this.state.email}
password={this.state.password}
errorMessage={this.props.errorMessage}
forgotPassword={this.state.forgotPassword}
onForgotPasswordLink={this.switchToForgotPassword}
onSignInLink={this.switchToSignIn}
/>
);
}
}
LoginContainer.propTypes = {
login: PropTypes.func,
errorMessage: PropTypes.string,
};
LoginContainer.propTypes = {};
export default compose(withLogin)(LoginContainer);
export default LoginContainer;
@@ -0,0 +1,48 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withSignIn } from 'coral-framework/hocs';
import { compose } from 'recompose';
import SignIn from '../components/SignIn';
class SignInContainer extends Component {
state = {
email: '',
password: '',
};
handleSubmit = () => {
this.props.signIn(this.state.email, this.state.password);
};
handleEmailChange = email => {
this.setState({ email });
};
handlePasswordChange = password => {
this.setState({ password });
};
render() {
return (
<SignIn
onSubmit={this.handleSubmit}
onEmailChange={this.handleEmailChange}
onPasswordChange={this.handlePasswordChange}
email={this.state.email}
password={this.state.password}
errorMessage={this.props.errorMessage}
onForgotPasswordLink={this.props.onForgotPasswordLink}
requireRecaptcha={this.props.requireRecaptcha}
/>
);
}
}
SignInContainer.propTypes = {
signIn: PropTypes.func.isRequired,
errorMessage: PropTypes.string.isRequired,
onForgotPasswordLink: PropTypes.func.isRequired,
requireRecaptcha: PropTypes.bool.isRequired,
};
export default compose(withSignIn)(SignInContainer);
+2 -1
View File
@@ -6,4 +6,5 @@ export { default as withEmit } from './withEmit';
export { default as excludeIf } from './excludeIf';
export { default as connect } from './connect';
export { default as withMergedSettings } from './withMergedSettings';
export { default as withLogin } from './withLogin';
export { default as withSignIn } from './withSignIn';
export { default as withForgotPassword } from './withForgotPassword';
@@ -0,0 +1,61 @@
import React from 'react';
import hoistStatics from 'recompose/hoistStatics';
import PropTypes from 'prop-types';
import { translateError } from '../utils';
/**
* WithForgotPassword provides properties `forgotPasssword`, `loading`, `errorMessage`, `success`.
*/
export default hoistStatics(WrappedComponent => {
class WithForgotPassword extends React.Component {
static contextTypes = {
store: PropTypes.object,
rest: PropTypes.func,
};
state = {
error: null,
loading: false,
success: false,
};
forgotPassword = email => {
const { rest } = this.context;
const redirectUri = location.href;
this.setState({ loading: true, error: null, success: false });
rest('/account/password/reset', {
method: 'POST',
body: { email, loc: redirectUri },
})
.then(() => {
this.setState({ loading: false, error: null, success: true });
})
.catch(error => {
console.error(error);
this.setState({ loading: false, error });
});
};
getErrorMessage() {
if (!this.state.error) {
return '';
}
return translateError(this.state.error);
}
render() {
return (
<WrappedComponent
{...this.props}
forgotPassword={this.forgotPassword}
success={this.state.success}
loading={this.state.loading}
errorMessage={this.getErrorMessage()}
/>
);
}
}
return WithForgotPassword;
});
@@ -6,10 +6,10 @@ import { translateError } from '../utils';
import { t } from '../services/i18n';
/**
* WithLogin provides properties `login`, `loading` and `errorMessage`, `requireRecaptcha`.
* WithSignIn provides properties `signIn`, `loading` and `errorMessage`, `requireRecaptcha`.
*/
export default hoistStatics(WrappedComponent => {
class WithLogin extends React.Component {
class WithSignIn extends React.Component {
static contextTypes = {
store: PropTypes.object,
rest: PropTypes.func,
@@ -20,7 +20,7 @@ export default hoistStatics(WrappedComponent => {
loading: false,
};
login = (email, password, recaptchaResponse) => {
signIn = (email, password, recaptchaResponse) => {
const { store, rest } = this.context;
const params = {
method: 'POST',
@@ -62,7 +62,7 @@ export default hoistStatics(WrappedComponent => {
return (
<WrappedComponent
{...this.props}
login={this.login}
signIn={this.signIn}
loading={this.state.loading}
errorMessage={this.getErrorMessage()}
requireRecaptcha={false}
@@ -71,5 +71,5 @@ export default hoistStatics(WrappedComponent => {
}
}
return WithLogin;
return WithSignIn;
});