diff --git a/src/components/PageLayout/PageLayout.js b/src/components/PageLayout/PageLayout.js index b7adc73d..feb13a11 100644 --- a/src/components/PageLayout/PageLayout.js +++ b/src/components/PageLayout/PageLayout.js @@ -2,6 +2,7 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import Helmet from 'react-helmet'; import { withRouter } from 'react-router-dom'; +import { FormattedMessage } from 'react-intl'; import { Topbar } from '../../containers'; const scrollToTop = () => { @@ -21,12 +22,32 @@ class PageLayout extends Component { } render() { - const { className, title, children, authError } = this.props; - // TODO: use FlashMessages for authError + const { className, title, children, authInfoError, logoutError } = this.props; + + // TODO: use FlashMessages for auth errors + + /* eslint-disable no-console */ + if (authInfoError && console && console.error) { + console.error(authInfoError); + } + if (logoutError && console && console.error) { + console.error(logoutError); + } + /* eslint-enable no-console */ + return (
- {authError ?

Error in auth: {authError.message}

: null} + {authInfoError + ?
+ +
+ : null} + {logoutError + ?
+ +
+ : null}

{title}

{children} @@ -37,17 +58,21 @@ class PageLayout extends Component { const { any, string, instanceOf, func } = PropTypes; -PageLayout.defaultProps = { className: '', children: null, authError: null }; +PageLayout.defaultProps = { className: '', children: null, authInfoError: null, logoutError: null }; PageLayout.propTypes = { className: string, title: string.isRequired, children: any, - authError: instanceOf(Error), + authInfoError: instanceOf(Error), + logoutError: instanceOf(Error), // history.listen function from withRouter listen: func.isRequired, }; -const mapStateToProps = state => ({ authError: state.Auth.error }); +const mapStateToProps = state => ({ + authInfoError: state.Auth.authInfoError, + logoutError: state.Auth.logoutError, +}); export default connect(mapStateToProps)(withRouter(PageLayout)); diff --git a/src/containers/AuthenticationPage/AuthenticationPage.js b/src/containers/AuthenticationPage/AuthenticationPage.js index 356d7f35..8c836f8f 100644 --- a/src/containers/AuthenticationPage/AuthenticationPage.js +++ b/src/containers/AuthenticationPage/AuthenticationPage.js @@ -1,23 +1,44 @@ import React, { PropTypes } from 'react'; import { connect } from 'react-redux'; import { Redirect } from 'react-router-dom'; +import { FormattedMessage } from 'react-intl'; import { PageLayout, NamedLink, NamedRedirect } from '../../components'; import { LoginForm, SignUpForm } from '../../containers'; import { login } from '../../ducks/Auth.duck'; export const AuthenticationPageComponent = props => { - const { location, tab, isAuthenticated, onLoginSubmit, onSignUpSubmit } = props; + const { + location, + tab, + isAuthenticated, + loginError, + onLoginSubmit, + onSignUpSubmit, + } = props; const isLogin = tab === 'login'; const from = location.state && location.state.from ? location.state.from : null; const authRedirect = from ? : ; + // TODO: use FlashMessages for auth errors + + /* eslint-disable no-console */ + if (loginError && console && console.error) { + console.error(loginError); + } + /* eslint-enable no-console */ + return ( {isAuthenticated ? authRedirect : null} + {loginError + ?
+ +
+ : null} {from ?

- You must log in to view the page at + {from}

: null} @@ -29,19 +50,28 @@ export const AuthenticationPageComponent = props => { ); }; -AuthenticationPageComponent.defaultProps = { tab: 'signup' }; +AuthenticationPageComponent.defaultProps = { + tab: 'signup', + authInfoError: null, + loginError: null, + logoutError: null, +}; -const { object, oneOf, shape, bool, func } = PropTypes; +const { object, oneOf, shape, bool, func, instanceOf } = PropTypes; AuthenticationPageComponent.propTypes = { location: shape({ state: object }).isRequired, tab: oneOf(['login', 'signup']), isAuthenticated: bool.isRequired, + loginError: instanceOf(Error), onLoginSubmit: func.isRequired, onSignUpSubmit: func.isRequired, }; -const mapStateToProps = state => ({ isAuthenticated: state.Auth.isAuthenticated }); +const mapStateToProps = state => ({ + isAuthenticated: state.Auth.isAuthenticated, + loginError: state.Auth.loginError, +}); const mapDispatchToProps = dispatch => ({ onLoginSubmit: ({ email, password }) => dispatch(login(email, password)), diff --git a/src/containers/AuthenticationPage/__snapshots__/AuthenticationPage.test.js.snap b/src/containers/AuthenticationPage/__snapshots__/AuthenticationPage.test.js.snap index 1b2ad3b3..4b349936 100644 --- a/src/containers/AuthenticationPage/__snapshots__/AuthenticationPage.test.js.snap +++ b/src/containers/AuthenticationPage/__snapshots__/AuthenticationPage.test.js.snap @@ -2,7 +2,9 @@ exports[`AuthenticationPageComponent matches snapshot 1`] = `

- You must log in to view the page at + /protected diff --git a/src/translations/en.json b/src/translations/en.json index 858a8517..c055e900 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1,8 +1,11 @@ { - "landingpage.examplelink": "Show nice studios! (from json)", + "AuthenticationPage.loginFailed": "The email and password you entered did not match our records. Please double-check and try again.", + "AuthenticationPage.loginRequiredFor": "You must log in to view the page at", "HeroSearchForm.placeholder": "Location search (soon)", "HeroSearchForm.search": "Search", - "HeroSection.title": "Book Studiotime anywhere", "HeroSection.subTitle": "The largest online community to rent music studios", - "ListingPage.loadingListingData": "Loading listing data" + "HeroSection.title": "Book Studiotime anywhere", + "ListingPage.loadingListingData": "Loading listing data", + "PageLayout.authInfoFailed": "Could not get authentication information.", + "PageLayout.logoutFailed": "Logout failed. Please try again." }