diff --git a/src/containers/LoginPage/LoginPage.jsx b/src/containers/LoginPage/LoginPage.jsx
new file mode 100644
index 0000000..76b37d3
--- /dev/null
+++ b/src/containers/LoginPage/LoginPage.jsx
@@ -0,0 +1,90 @@
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+import { pushState } from 'redux-router';
+import { login } from '../../redux/modules/auth';
+
+import './LoginPage.scss';
+
+class Login extends Component {
+
+ componentWillReceiveProps(nextProps) {
+ console.log('Login: componentWillReceiveProps: '+nextProps);
+ console.log('user: '+this.props.user+'\tnextUser='+nextProps.user);
+
+ if (nextProps.user) {
+ // logged in, let's show home
+ this.dispatch(pushState(null, '/counter'));
+ }
+ }
+
+ handleLogin(event) {
+ event.preventDefault();
+ const username = this.refs.username; // need for getDOMNode() call going away in React 0.14
+ const password = this.refs.password;
+ this.props.dispatch(login(username.value, password.value));
+ // username.value = '';
+ // password.value = '';
+ }
+
+ render(){
+ const {user, loginError} = this.props;
+ return(
+
+ );
+ }
+}
+
+Login.propTypes = {
+ user: PropTypes.string,
+ loginError: PropTypes.object,
+ dispatch: PropTypes.func.isRequired,
+ routerState: PropTypes.object.isRequired
+};
+
+function mapStateToProps(state){
+ const { auth, router } = state;
+ if(auth){
+ return {user: auth.user, loginError: auth.loginError, routerState: router};
+ }else{
+ return {user: null, routerState: router};
+ }
+}
+
+export default connect(mapStateToProps)(Login);
diff --git a/src/containers/LoginPage/LoginPage.scss b/src/containers/LoginPage/LoginPage.scss
new file mode 100644
index 0000000..f4aefa6
--- /dev/null
+++ b/src/containers/LoginPage/LoginPage.scss
@@ -0,0 +1,29 @@
+:global{
+ .panel-signin {
+ margin-top: 25%;
+ }
+
+ .form-signin {
+ max-width: auto;
+ padding: 15px;
+ margin: 0 auto;
+
+ .checkbox {
+ margin-bottom: 10px;
+ }
+
+ .form-control:focus {
+ z-index: 2;
+ }
+ input[type="text"] {
+ margin-bottom: 0px;
+ }
+ input[type="password"] {
+ margin-bottom: 0px;
+ }
+ }
+
+ .input-group {
+ margin-bottom: 10px;
+ }
+}
\ No newline at end of file
diff --git a/src/containers/index.js b/src/containers/index.js
index 717d106..6871317 100644
--- a/src/containers/index.js
+++ b/src/containers/index.js
@@ -1,2 +1,3 @@
export CoreLayout from './CoreLayout'
-export MoviesPage from './MoviesPage/MoviesPage'
\ No newline at end of file
+export MoviesPage from './MoviesPage/MoviesPage'
+export LoginPage from './LoginPage/LoginPage'
\ No newline at end of file
diff --git a/src/index.html b/src/index.html
index 4666891..8f69c16 100644
--- a/src/index.html
+++ b/src/index.html
@@ -2,9 +2,20 @@
React Transform Boilerplate
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/redux/middleware/promiseMiddleware.js b/src/redux/middleware/promiseMiddleware.js
new file mode 100644
index 0000000..935a8d9
--- /dev/null
+++ b/src/redux/middleware/promiseMiddleware.js
@@ -0,0 +1,27 @@
+export default function promiseMiddleware() {
+ return next => action => {
+ const { promise, type, ...rest } = action;
+
+ if (!promise) {
+ return next(action);
+ }
+
+ const [REQUEST, SUCCESS, FAILURE] = types;
+
+ next({ ...rest, type: REQUEST });
+
+ return promise
+ .then(res => {
+ next({ ...rest, res, type: SUCCESS });
+
+ return true;
+ })
+ .catch(error => {
+ next({ ...rest, error, type: FAILURE });
+
+ // Another benefit is being able to log all failures here
+ console.log(error);
+ return false;
+ });
+ };
+}
\ No newline at end of file
diff --git a/src/redux/modules/auth.js b/src/redux/modules/auth.js
new file mode 100644
index 0000000..3e41518
--- /dev/null
+++ b/src/redux/modules/auth.js
@@ -0,0 +1,154 @@
+import axios from 'axios';
+
+//--------------------------- Action constants --------------------------
+
+// names for actions can be more specific
+const LOGIN_REQUEST = 'LOGIN_REQUEST';
+const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
+const LOGIN_FAILURE = 'LOGIN_FAILURE';
+
+const LOGOUT_REQUEST = 'LOGOUT_REQUEST';
+const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';
+const LOGOUT_FAILURE = 'LOGOUT_FAILURE';
+
+//--------------------------- Reducer function --------------------------
+
+const initialState = {
+ // user: null,
+ // password: null,
+ // userRole: null,
+ // loggingIn: false,
+ // loggingOut: false,
+ // loginError: null,
+};
+
+export default function auth(state = initialState, action = {}) {
+ switch (action.type) {
+ case LOGIN_REQUEST:
+ return Object.assign({}, state, {loggingIn: true});
+
+ case LOGIN_SUCCESS:
+ return Object.assign({}, state, {
+ loggingIn: false, user: action.user, role: action.role});
+
+ case LOGIN_FAILURE:
+ return {
+ ...state,
+ loggingIn: false,
+ user: null,
+ role: null,
+ loginError: action.error
+ };
+ case LOGOUT_REQUEST:
+ return {
+ ...state,
+ loggingOut: true
+ };
+ case LOGOUT_SUCCESS:
+ return {
+ ...state,
+ loggingOut: false,
+ user: null,
+ userRole: null,
+ loginError: null
+ };
+ case LOGOUT_FAILURE:
+ return {
+ ...state,
+ loggingOut: false,
+ logoutError: action.error
+ };
+ default:
+ return state;
+ }
+}
+
+//--------------------------- Action functions --------------------------
+
+function loginRequest(user) {
+ return {
+ type: LOGIN_REQUEST,
+ user: user
+ };
+}
+
+function loginSuccess(user, data) {
+ return {
+ type: LOGIN_SUCCESS,
+ user: data.user,
+ role: data.role
+ };
+}
+
+function loginFailure(user, error) {
+ return {
+ type: LOGIN_FAILURE,
+ user: user,
+ error: error
+ };
+}
+
+export function login(user, password) {
+ return (dispatch) => {
+
+ dispatch(loginRequest(user));
+
+ return axios
+ .post('http://localhost:3001/api/login', { user: user, password: password })
+ .then(response => dispatch(loginSuccess(user, response.data)))
+ .catch(function (error){
+ const response=error.response;
+ if(response===undefined){
+ dispatch(loginFailure(user, error));
+ }else{
+ error.status = response.status;
+ error.statusText = response.statusText;
+ error.message = response.message;
+ dispatch(loginFailure(user, error));
+ }
+ });
+ };
+}
+
+function logoutRequest(user) {
+ return {
+ type: LOGOUT_REQUEST,
+ user
+ };
+}
+
+function logoutSuccess(user) {
+ return {
+ type: LOGOUT_SUCCESS,
+ user
+ };
+}
+
+function logoutFailure(user, error) {
+ return {
+ type: LOGOUT_FAILURE,
+ user,
+ error
+ };
+}
+
+export function logout(user) {
+ return dispatch => {
+
+ dispatch(logoutRequest(user));
+
+ return axios.post('http://localhost:3001/api/logout', { user: user })
+ .then(response => dispatch(logoutSuccess(response.data)))
+ .catch(function (error){
+ const response=error.response;
+ if(response===undefined){
+ dispatch(logoutFailure(user, error));
+ }else{
+ error.status = response.status;
+ error.statusText = response.statusText;
+ error.message = response.message;
+ dispatch(logout(user, error));
+ }
+ });
+ };
+}
\ No newline at end of file
diff --git a/src/redux/modules/counter.js b/src/redux/modules/counter.js
index 0a66f58..e87eb5f 100644
--- a/src/redux/modules/counter.js
+++ b/src/redux/modules/counter.js
@@ -1,8 +1,8 @@
-// actions constants
+//--------------------------- Action constants --------------------------
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
-// reducer
+//--------------------------- Reducer function --------------------------
export default function counter(state = 0, action) {
switch (action.type) {
case INCREMENT_COUNTER:
@@ -14,7 +14,7 @@ export default function counter(state = 0, action) {
}
}
-// actions functions
+//--------------------------- Action functions --------------------------
export function increment() {
return {
type: INCREMENT_COUNTER
diff --git a/src/redux/modules/reducer.js b/src/redux/modules/reducer.js
index d9e1feb..f951449 100644
--- a/src/redux/modules/reducer.js
+++ b/src/redux/modules/reducer.js
@@ -1,9 +1,11 @@
import { combineReducers } from 'redux';
import counter from './counter';
+import auth from './auth';
import { routerStateReducer as router } from 'redux-router';
export default combineReducers({
+ auth,
counter,
router
});
diff --git a/src/routes.js b/src/routes.js
index 420d04e..a57f143 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -1,13 +1,14 @@
import React from 'react';
import { Route, Redirect } from 'react-router';
-import { CoreLayout, MoviesPage } from './containers';
+import { CoreLayout, MoviesPage, LoginPage } from './containers';
import { Counter, AutoCounter } from './components';
export default (
-
-
-
-
-
+
+
+
+
+
+
);
\ No newline at end of file
diff --git a/webpack.config.dev.js b/webpack.config.dev.js
index 2ee8d4e..abdb523 100644
--- a/webpack.config.dev.js
+++ b/webpack.config.dev.js
@@ -12,6 +12,9 @@ module.exports = {
'./src/index'
]
},
+ resolve : {
+ extensions : ['', '.js', '.jsx']
+ },
output: {
path: path.join(__dirname, 'dist'),
//"entry" keys will be a bundle names
diff --git a/webpack.config.prod.js b/webpack.config.prod.js
index 0055225..58bf596 100644
--- a/webpack.config.prod.js
+++ b/webpack.config.prod.js
@@ -10,6 +10,9 @@ module.exports = {
'./src/index.js',
'./src/index.html'
],
+ resolve : {
+ extensions : ['', '.js', '.jsx']
+ },
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',