Improve login errors.
authorAlexandre Segura <mex.zktk@gmail.com>
Mon, 26 Jun 2017 16:43:22 +0200
changeset 90 990f2c928b15
parent 89 06f609adfbf8
child 91 143ff08ec2cc
Improve login errors.
client/src/components/Login.js
client/src/components/Register.js
client/src/constants/actionTypes.js
client/src/reducers/authReducer.js
client/src/sagas/authSaga.js
client/src/store/configureStore.js
--- a/client/src/components/Login.js	Mon Jun 26 15:45:50 2017 +0200
+++ b/client/src/components/Login.js	Mon Jun 26 16:43:22 2017 +0200
@@ -1,7 +1,7 @@
 import React, { Component } from 'react';
 import { connect } from 'react-redux';
 import { bindActionCreators } from 'redux';
-import { Grid, Row, Col, Panel, FormGroup, ControlLabel, FormControl, Button, Alert } from 'react-bootstrap';
+import { Grid, Row, Col, Panel, FormGroup, ControlLabel, FormControl, Button, Alert, HelpBlock } from 'react-bootstrap';
 import '../App.css';
 import Navbar from './Navbar';
 import * as authActions from '../actions/authActions';
@@ -20,10 +20,25 @@
     this.props.history.push('/register');
   }
 
-  renderError() {
-    return (
-      <Alert bsStyle="danger">Bad credentials</Alert>
-    )
+  renderErrorMessage(errorMessages, fieldname) {
+    if (errorMessages && errorMessages.has(fieldname)) {
+      return errorMessages.get(fieldname).map((message, key) =>
+        <HelpBlock key={ key }>{ message }</HelpBlock>
+      );
+    }
+  }
+
+  renderNonFieldErrors(errorMessages) {
+    if (errorMessages && errorMessages.has('non_field_errors')) {
+      const errors = errorMessages.get('non_field_errors');
+      return (
+        <Alert bsStyle="danger">
+        { errors.map((message, key) =>
+          <p key={ key }>{ message }</p>
+        ) }
+        </Alert>
+      )
+    }
   }
 
   render() {
@@ -32,6 +47,8 @@
       <h4 className="text-uppercase text-center">Login</h4>
     )
 
+    const errorMessages = this.props.login.get('errorMessages');
+
     return (
       <div>
         <Navbar history={this.props.history} />
@@ -40,15 +57,17 @@
             <Col md={6} mdOffset={3}>
               <Panel header={ panelHeader } className="panel-login">
                 <form>
-                  <FormGroup>
+                  <FormGroup validationState={ errorMessages && errorMessages.has('username') ? 'error' : null }>
                     <ControlLabel>Username</ControlLabel>
                     <FormControl componentClass="input" type="text" inputRef={ref => { this.username = ref; }} />
+                    { this.renderErrorMessage(errorMessages, 'username') }
                   </FormGroup>
-                  <FormGroup>
+                  <FormGroup validationState={ errorMessages && errorMessages.has('password') ? 'error' : null }>
                     <ControlLabel>Password</ControlLabel>
                     <FormControl componentClass="input" type="password" inputRef={ref => { this.password = ref; }} />
+                    { this.renderErrorMessage(errorMessages, 'password') }
                   </FormGroup>
-                  { this.props.login.error && this.renderError() }
+                  { this.renderNonFieldErrors(errorMessages) }
                   <Button block bsStyle="primary" onClick={this.login}>Login</Button>
                 </form>
               </Panel>
@@ -65,7 +84,6 @@
 
 function mapStateToProps(state, props) {
   return {
-    currentUser: state['currentUser'],
     login: state['login']
   };
 }
--- a/client/src/components/Register.js	Mon Jun 26 15:45:50 2017 +0200
+++ b/client/src/components/Register.js	Mon Jun 26 16:43:22 2017 +0200
@@ -1,7 +1,7 @@
 import React, { Component } from 'react';
 import { connect } from 'react-redux';
 import { bindActionCreators } from 'redux';
-import { Grid, Row, Col, Panel, FormGroup, ControlLabel, FormControl, Button, Alert, HelpBlock } from 'react-bootstrap';
+import { Grid, Row, Col, Panel, FormGroup, ControlLabel, FormControl, Button, HelpBlock } from 'react-bootstrap';
 import '../App.css';
 import Navbar from './Navbar';
 import * as authActions from '../actions/authActions';
@@ -22,12 +22,6 @@
     this.props.history.push('/login');
   }
 
-  renderError() {
-    return (
-      <Alert bsStyle="danger">Bad credentials</Alert>
-    )
-  }
-
   renderErrorMessage(errorMessages, fieldname) {
     if (errorMessages && errorMessages.has(fieldname)) {
       return errorMessages.get(fieldname).map((message, key) =>
--- a/client/src/constants/actionTypes.js	Mon Jun 26 15:45:50 2017 +0200
+++ b/client/src/constants/actionTypes.js	Mon Jun 26 16:43:22 2017 +0200
@@ -10,14 +10,15 @@
 
 export const AUTH_LOGIN_SUBMIT = 'AUTH_LOGIN_SUBMIT';
 export const AUTH_LOGIN_REQUEST = 'AUTH_LOGIN_REQUEST';
-export const AUTH_LOGIN_SUCCESS = 'AUTH_LOGIN_SUCCESS';
 export const AUTH_LOGIN_ERROR = 'AUTH_LOGIN_ERROR';
 
 export const AUTH_REGISTER_SUBMIT = 'AUTH_REGISTER_SUBMIT';
 export const AUTH_REGISTER_REQUEST = 'AUTH_REGISTER_REQUEST';
-export const AUTH_REGISTER_SUCCESS = 'AUTH_REGISTER_SUCCESS';
 export const AUTH_REGISTER_ERROR = 'AUTH_REGISTER_ERROR';
 
+// Used both by login & register
+export const AUTH_LOGIN_SUCCESS = 'AUTH_LOGIN_SUCCESS';
+
 export const AUTH_STORE_TOKEN_ASYNC = 'AUTH_STORE_TOKEN_ASYNC';
 export const AUTH_STORE_TOKEN = 'AUTH_STORE_TOKEN';
 export const AUTH_LOGOUT = 'AUTH_LOGOUT';
--- a/client/src/reducers/authReducer.js	Mon Jun 26 15:45:50 2017 +0200
+++ b/client/src/reducers/authReducer.js	Mon Jun 26 16:43:22 2017 +0200
@@ -8,7 +8,6 @@
     case types.AUTH_LOGOUT:
       return false;
     case types.AUTH_LOGIN_SUCCESS:
-    case types.AUTH_REGISTER_SUCCESS:
       return true;
     default:
       return state;
@@ -21,7 +20,6 @@
     case types.AUTH_LOGOUT:
       return null;
     case types.AUTH_LOGIN_SUCCESS:
-    case types.AUTH_REGISTER_SUCCESS:
       return new UserRecord(action.user);
     case types.USER_UPDATE_SETTINGS:
       return state.merge({
@@ -49,6 +47,7 @@
   loading: false,
   success: false,
   error: false,
+  errorMessages: Immutable.Map({})
 });
 
 export const login = (state = loginState, action) => {
@@ -65,6 +64,7 @@
         loading: false,
         success: action.type === types.AUTH_LOGIN_SUCCESS,
         error: action.type === types.AUTH_LOGIN_ERROR,
+        errorMessages: action.type === types.AUTH_LOGIN_ERROR ? Immutable.Map(action.error) : Immutable.Map({})
       })
     default:
       return state
@@ -86,11 +86,11 @@
         success: false,
         error: false,
       })
-    case types.AUTH_REGISTER_SUCCESS:
+    case types.AUTH_LOGIN_SUCCESS:
     case types.AUTH_REGISTER_ERROR:
       return Immutable.Map({
         loading: false,
-        success: action.type === types.AUTH_REGISTER_SUCCESS,
+        success: action.type === types.AUTH_LOGIN_SUCCESS,
         error: action.type === types.AUTH_REGISTER_ERROR,
         errorMessages: action.type === types.AUTH_REGISTER_ERROR ? Immutable.Map(action.error) : Immutable.Map({})
       })
--- a/client/src/sagas/authSaga.js	Mon Jun 26 15:45:50 2017 +0200
+++ b/client/src/sagas/authSaga.js	Mon Jun 26 16:43:22 2017 +0200
@@ -62,7 +62,7 @@
           type: types.AUTH_STORE_TOKEN_ASYNC,
           token: response.token,
         }, {
-          type: types.AUTH_REGISTER_SUCCESS,
+          type: types.AUTH_LOGIN_SUCCESS,
           user: response.user,
           token: response.token,
         }];
--- a/client/src/store/configureStore.js	Mon Jun 26 15:45:50 2017 +0200
+++ b/client/src/store/configureStore.js	Mon Jun 26 16:43:22 2017 +0200
@@ -35,6 +35,7 @@
     loading: false,
     success: false,
     error: false,
+    errorMessages: Immutable.Map({})
   }),
   register: Immutable.Map({
     loading: false,