├── src ├── common │ ├── components │ │ ├── style │ │ │ ├── home.css │ │ │ └── message.css │ │ ├── Message.js │ │ ├── reset │ │ │ ├── reset.css │ │ │ ├── ResetPage.js │ │ │ └── Reset.js │ │ ├── 404.js │ │ ├── AddMessage.js │ │ ├── About.js │ │ ├── personal │ │ │ └── Dashboard.js │ │ ├── Home.js │ │ ├── assets │ │ │ └── TextInput.js │ │ ├── layout │ │ │ ├── Sidebar.js │ │ │ └── Header.js │ │ ├── Forget.js │ │ ├── Signup.js │ │ ├── MessagesList.js │ │ ├── Login.js │ │ └── WorkSpace.js │ ├── reducers │ │ ├── version.js │ │ ├── layout.js │ │ ├── index.js │ │ ├── reducer.js │ │ └── user.js │ ├── containers │ │ ├── ResetPage.js │ │ ├── SignupPage.js │ │ ├── ForgetPage.js │ │ ├── LoginPage.js │ │ ├── DevTools.js │ │ ├── ExternalLayout.js │ │ ├── InternalLayout.js │ │ └── App.js │ ├── actions │ │ ├── layout.js │ │ ├── message.js │ │ └── user.js │ ├── api │ │ ├── fetchComponentDataBeforeRender.js │ │ ├── promiseMiddleware.js │ │ └── user.js │ ├── themes │ │ └── personal.js │ ├── sagas │ │ └── sagas.js │ ├── routes.js │ └── store │ │ └── configureStore.js ├── server │ ├── index.js │ ├── webpack.js │ ├── devtools.js │ └── server.js └── client │ └── index.js ├── .jshintrc ├── .gitignore ├── .vscode └── settings.json ├── .travis.yml ├── styles ├── images │ ├── facebook.png │ ├── signin.png │ └── googleplus.png └── index.css ├── test ├── setup.js ├── state │ └── layout.spec.js ├── render │ └── Sidebar.spec.js └── behaviour │ └── Sidebar.spec.js ├── README.md ├── webpack.config.js └── package.json /src/common/components/style/home.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } -------------------------------------------------------------------------------- /src/common/components/style/message.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | lib 4 | .DS_Store 5 | dist -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | } -------------------------------------------------------------------------------- /src/common/reducers/version.js: -------------------------------------------------------------------------------- 1 | export default function version(state = 0, action) { 2 | return state; 3 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.0" 4 | - "0.12" 5 | - "0.11" 6 | - "0.10" 7 | - "iojs" -------------------------------------------------------------------------------- /styles/images/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Big-Silver/React-Redux-Material-Chatting/HEAD/styles/images/facebook.png -------------------------------------------------------------------------------- /styles/images/signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Big-Silver/React-Redux-Material-Chatting/HEAD/styles/images/signin.png -------------------------------------------------------------------------------- /styles/images/googleplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Big-Silver/React-Redux-Material-Chatting/HEAD/styles/images/googleplus.png -------------------------------------------------------------------------------- /src/server/index.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register')({ 2 | presets: ['es2015', 'stage-2', 'react'] 3 | }); 4 | require('./server'); 5 | -------------------------------------------------------------------------------- /test/setup.js: -------------------------------------------------------------------------------- 1 | var jsdom = require('jsdom-no-contextify'); 2 | 3 | global.document = jsdom.jsdom(''); 4 | global.window = document.parentWindow; -------------------------------------------------------------------------------- /src/common/reducers/layout.js: -------------------------------------------------------------------------------- 1 | import { TOGGLE_SIDEBAR } from '../actions/layout'; 2 | 3 | export default function layout(state = {sidebarOpen: false}, action) { 4 | switch (action.type) { 5 | case TOGGLE_SIDEBAR: 6 | return { 7 | sidebarOpen : action.value 8 | }; 9 | default: 10 | return state; 11 | } 12 | } -------------------------------------------------------------------------------- /src/common/components/Message.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import PropTypes from "prop-types" 3 | 4 | const Message = ({ message, author }) => ( 5 |

6 | {author}: {message} 7 |

8 | ) 9 | 10 | Message.propTypes = { 11 | message: PropTypes.string.isRequired, 12 | author: PropTypes.string.isRequired 13 | } 14 | 15 | export default Message -------------------------------------------------------------------------------- /src/common/components/reset/reset.css: -------------------------------------------------------------------------------- 1 | .forget { 2 | text-align: center; 3 | box-shadow: 0 1px 4px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.12)! important; 4 | width: 500px; 5 | height: 320px! important; 6 | margin: 150px auto; 7 | } 8 | 9 | .email-header { 10 | width: 332px; 11 | text-align: left! important; 12 | margin: 42px auto 0px auto; 13 | } 14 | 15 | .forget .signup_submit { 16 | margin-top: 30px; 17 | } -------------------------------------------------------------------------------- /src/common/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { routerStateReducer } from 'redux-router'; 3 | import undoable from 'redux-undo'; 4 | 5 | import user from './user'; 6 | import layout from './layout'; 7 | import version from './version'; 8 | 9 | const rootReducer = combineReducers({ 10 | user : user, 11 | version : version, 12 | layout : undoable(layout), 13 | router : routerStateReducer 14 | }); 15 | 16 | export default rootReducer; -------------------------------------------------------------------------------- /src/server/webpack.js: -------------------------------------------------------------------------------- 1 | // Webpack dev server 2 | // Ran in parallel with the Express server 3 | 4 | import WebpackDevServer from "webpack-dev-server"; 5 | import webpack from "webpack"; 6 | import config from "../../webpack.config.dev"; 7 | 8 | var server = new WebpackDevServer(webpack(config), { 9 | // webpack-dev-server options 10 | publicPath: config.output.publicPath, 11 | hot: true, 12 | stats: { colors: true }, 13 | }); 14 | server.listen(8080, "localhost", function() {}); 15 | -------------------------------------------------------------------------------- /src/common/containers/ResetPage.js: -------------------------------------------------------------------------------- 1 | import { bindActionCreators } from 'redux'; 2 | import { connect } from 'react-redux'; 3 | import Reset from '../components/reset/Reset'; 4 | import * as ResetAction from '../actions/user'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | requestEmail: state.user.requestEmail 9 | }; 10 | } 11 | 12 | function mapDispatchToProps(dispatch) { 13 | return bindActionCreators(ResetAction, dispatch); 14 | } 15 | 16 | export default connect(mapStateToProps, mapDispatchToProps)(Reset); -------------------------------------------------------------------------------- /src/common/actions/layout.js: -------------------------------------------------------------------------------- 1 | 2 | import { ActionCreators } from 'redux-undo'; 3 | 4 | 5 | 6 | export function undo() { 7 | return (dispatch, getState) => { 8 | dispatch(ActionCreators.undo()); 9 | }; 10 | } 11 | 12 | export function redo() { 13 | return (dispatch, getState) => { 14 | dispatch(ActionCreators.redo()); 15 | }; 16 | } 17 | 18 | /** 19 | * Bundle User into layout 20 | */ 21 | 22 | import { GET_USER, getUser} from './user'; 23 | export { getUser as getUser }; 24 | export { GET_USER as GET_USER }; 25 | 26 | -------------------------------------------------------------------------------- /src/common/containers/SignupPage.js: -------------------------------------------------------------------------------- 1 | import { bindActionCreators } from 'redux'; 2 | import { connect } from 'react-redux'; 3 | import Signup from '../components/Signup'; 4 | import * as SignupActions from '../actions/user'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | userEmail: state.user.userEmail, 9 | error: state.user.error, 10 | logged: state.user.logged 11 | }; 12 | } 13 | 14 | function mapDispatchToProps(dispatch) { 15 | return bindActionCreators(SignupActions, dispatch); 16 | } 17 | 18 | export default connect(mapStateToProps, mapDispatchToProps)(Signup); -------------------------------------------------------------------------------- /src/common/components/404.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router'; 3 | 4 | class Error404 extends Component { 5 | 6 | render() { 7 | return ( 8 |
9 |

404: Page not found

10 |

Sorry, we've misplaced that URL or it's pointing to something that does not exist.

11 |

> Head back home

12 |
13 | ); 14 | } 15 | } 16 | 17 | export default Error404; 18 | 19 | -------------------------------------------------------------------------------- /src/common/reducers/reducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { reducer as formReducer } from 'redux-form'; 3 | import { routerReducer } from 'react-router-redux'; 4 | import userReducer from './user' 5 | import indexReducer from './index' 6 | import layoutReducer from './layout' 7 | import versionReducer from './version' 8 | 9 | 10 | const reducer = combineReducers({ 11 | form: formReducer, 12 | routing: routerReducer, 13 | user: userReducer, 14 | index: indexReducer, 15 | layout: layoutReducer, 16 | version: versionReducer 17 | }); 18 | 19 | export default reducer; -------------------------------------------------------------------------------- /src/common/containers/ForgetPage.js: -------------------------------------------------------------------------------- 1 | import { bindActionCreators } from 'redux'; 2 | import { connect } from 'react-redux'; 3 | import Forget from '../components/Forget'; 4 | import * as ForgetActions from '../actions/user'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | userName: state.user.userName, 9 | userId: state.user.userId, 10 | error: state.user.error, 11 | logged: state.user.logged 12 | }; 13 | } 14 | 15 | function mapDispatchToProps(dispatch) { 16 | return bindActionCreators(ForgetActions, dispatch); 17 | } 18 | 19 | export default connect(mapStateToProps, mapDispatchToProps)(Forget); -------------------------------------------------------------------------------- /src/common/containers/LoginPage.js: -------------------------------------------------------------------------------- 1 | import { bindActionCreators } from 'redux'; 2 | import { connect } from 'react-redux'; 3 | import Login from '../components/Login'; 4 | import * as LoginActions from '../actions/user'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | userName: state.user.userName, 9 | userId: state.user.userId, 10 | error: state.user.error, 11 | logged: state.user.logged 12 | }; 13 | } 14 | 15 | function mapDispatchToProps(dispatch) { 16 | return bindActionCreators(LoginActions, dispatch); 17 | } 18 | 19 | export default connect(mapStateToProps, mapDispatchToProps)(Login); 20 | -------------------------------------------------------------------------------- /src/common/actions/message.js: -------------------------------------------------------------------------------- 1 | import request from 'axios'; 2 | import Querystring from 'querystring'; 3 | import config from '../../../package.json'; 4 | 5 | export const ADD_MESSAGE = 'ADD_MESSAGE'; 6 | export const MESSAGE_RECEIVED = 'MESSAGE_RECEIVED'; 7 | 8 | let nextMessageId = 0; 9 | const nextUserId = 0; 10 | 11 | export const addMessage = (message, author) => ({ 12 | type: ADD_MESSAGE, 13 | id: nextMessageId++, 14 | message, 15 | author 16 | }) 17 | 18 | export const messageReceived = (message, author) => ({ 19 | type: MESSAGE_RECEIVED, 20 | id: nextMessageId++, 21 | message, 22 | author 23 | }) 24 | -------------------------------------------------------------------------------- /src/server/devtools.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import DevTools from '../common/containers/DevTools'; 4 | 5 | export default function createDevToolsWindow(store) { 6 | const popup = window.open(null, 'Redux DevTools', 'menubar=no,location=no,resizable=yes,scrollbars=no,status=no'); 7 | // Reload in case it already exists 8 | popup.location.reload(); 9 | 10 | setTimeout(() => { 11 | popup.document.write('
'); 12 | render( 13 | , 14 | popup.document.getElementById('react-devtools-root') 15 | ); 16 | }, 10); 17 | } -------------------------------------------------------------------------------- /src/common/api/fetchComponentDataBeforeRender.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This looks at static needs parameter in components and waits for the promise to be fullfilled 3 | * It is used to make sure server side rendered pages wait for APIs to resolve before returning res.end() 4 | */ 5 | 6 | export function fetchComponentDataBeforeRender(dispatch, components, params) { 7 | const needs = components.reduce( (prev, current) => { 8 | return (current.need || []) 9 | .concat((current.WrappedComponent ? current.WrappedComponent.need : []) || []) 10 | .concat(prev); 11 | }, []); 12 | const promises = needs.map(need => dispatch(need())); 13 | return Promise.all(promises); 14 | } -------------------------------------------------------------------------------- /src/common/api/promiseMiddleware.js: -------------------------------------------------------------------------------- 1 | export default function promiseMiddleware() { 2 | return next => action => { 3 | const { promise, type, ...rest } = action; 4 | 5 | if (!promise) return next(action); 6 | 7 | const SUCCESS = type + '_SUCCESS'; 8 | const REQUEST = type + '_REQUEST'; 9 | const FAILURE = type + '_FAILURE'; 10 | next({ ...rest, type: REQUEST }); 11 | return promise 12 | .then(req => { 13 | next({ ...rest, req, type: SUCCESS }); 14 | return true; 15 | }) 16 | .catch(error => { 17 | next({ ...rest, error, type: FAILURE }); 18 | console.log(error); 19 | return false; 20 | }); 21 | }; 22 | } -------------------------------------------------------------------------------- /src/common/components/AddMessage.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { bindActionCreators } from 'redux'; 3 | import {connect} from 'react-redux'; 4 | 5 | const AddMessage = (props) => { 6 | let input 7 | 8 | return ( 9 |
10 | { 12 | if (e.key === 'Enter') { 13 | props.dispatch(input.value, 'Me') 14 | input.value = '' 15 | } 16 | }} 17 | type="text" 18 | ref={(node) => { 19 | input = node 20 | }} 21 | /> 22 |
23 | ) 24 | } 25 | 26 | AddMessage.propTypes = { 27 | dispatch: PropTypes.func.isRequired 28 | } 29 | 30 | export default AddMessage -------------------------------------------------------------------------------- /src/common/containers/DevTools.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | // Exported from redux-devtools 4 | import { createDevTools } from 'redux-devtools'; 5 | 6 | // Monitors are separate packages, and you can make a custom one 7 | import LogMonitor from 'redux-devtools-log-monitor'; 8 | import DockMonitor from 'redux-devtools-dock-monitor'; 9 | 10 | // createDevTools takes a monitor and produces a DevTools component 11 | const DevTools = createDevTools( 12 | // Monitors are individually adjustable with props. 13 | // Consult their repositories to learn about those props. 14 | // Here, we put LogMonitor inside a DockMonitor. 15 | 19 | 20 | 21 | ); 22 | 23 | 24 | export default DevTools; 25 | -------------------------------------------------------------------------------- /src/common/components/About.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Helmet from 'react-helmet'; 3 | 4 | 5 | class About extends Component { 6 | 7 | render() { 8 | return ( 9 |
10 | 11 |

Why I coded this

12 |

Redux and React are rapidly developing code bases. I was having difficultly finding a simple boiler plate example to base my Redux app on. I decided to create my own stripped down version.

13 | 14 |
15 |

My name is Callum Rimmer.

16 |

You can find me at my
17 | Github account (@caljrimmer)
18 | Twitter account(@caljrimmer)

19 |
20 |
21 | 22 | ); 23 | } 24 | } 25 | 26 | export default About; -------------------------------------------------------------------------------- /src/common/themes/personal.js: -------------------------------------------------------------------------------- 1 | import Colors from 'material-ui/lib/styles/colors'; 2 | import ColorManipulator from 'material-ui/lib/utils/color-manipulator'; 3 | import Spacing from 'material-ui/lib/styles/spacing'; 4 | import zIndex from 'material-ui/lib/styles/zIndex'; 5 | 6 | zIndex.appBar = 2000; 7 | 8 | export default { 9 | spacing: Spacing, 10 | zIndex: zIndex, 11 | fontFamily: 'Roboto, sans-serif', 12 | palette: { 13 | primary1Color: Colors.cyan500, 14 | primary2Color: Colors.cyan700, 15 | primary3Color: Colors.lightBlack, 16 | accent1Color: Colors.pinkA200, 17 | accent2Color: Colors.grey100, 18 | accent3Color: Colors.grey500, 19 | textColor: Colors.darkBlack, 20 | alternateTextColor: Colors.white, 21 | canvasColor: Colors.white, 22 | borderColor: Colors.grey300, 23 | disabledColor: ColorManipulator.fade(Colors.darkBlack, 0.3), 24 | pickerHeaderColor: Colors.cyan500, 25 | } 26 | }; -------------------------------------------------------------------------------- /src/common/sagas/sagas.js: -------------------------------------------------------------------------------- 1 | 2 | import { takeLatest, call, put } from "redux-saga/effects"; 3 | import axios from 'axios'; 4 | 5 | // watcher saga: watches for actions dispatched to the store, starts worker saga 6 | export function* watcherSaga() { 7 | yield takeLatest("API_CALL_REQUEST", workerSaga); 8 | } 9 | 10 | // function that makes the api request and returns a Promise for response 11 | function fetchDog() { 12 | return axios({ 13 | method: "get", 14 | url: "https://dog.ceo/api/breeds/image/random" 15 | }); 16 | } 17 | 18 | // worker saga: makes the api call when watcher saga sees the action 19 | function* workerSaga() { 20 | try { 21 | const response = yield call(fetchDog); 22 | const dog = response.data.message; 23 | 24 | // dispatch a success action to the store with the new dog 25 | yield put({ type: "API_CALL_SUCCESS", dog }); 26 | 27 | } catch (error) { 28 | // dispatch a failure action to the store with the error 29 | yield put({ type: "API_CALL_FAILURE", error }); 30 | } 31 | } -------------------------------------------------------------------------------- /src/common/components/personal/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import TextField from 'material-ui/lib/text-field'; 3 | import RaisedButton from 'material-ui/lib/raised-button'; 4 | import { bindActionCreators } from 'redux'; 5 | import {connect} from 'react-redux'; 6 | 7 | import {reduxForm} from 'redux-form'; 8 | import Helmet from 'react-helmet' 9 | import * as UserActions from '../../actions/user'; 10 | 11 | 12 | class Dashboard extends Component { 13 | 14 | constructor(props) { 15 | super(props); 16 | } 17 | 18 | render() { 19 | console.log(this.props.user); 20 | return ( 21 |
22 | 23 | TVOI DASHBOARD SHATAL 24 |
25 | ); 26 | } 27 | } 28 | 29 | Dashboard.propTypes = { 30 | 31 | }; 32 | 33 | function mapStateToProps(state) { 34 | return { 35 | user : state.user 36 | }; 37 | } 38 | 39 | function mapDispatchToProps(dispatch) { 40 | return bindActionCreators(UserActions,dispatch); 41 | } 42 | 43 | 44 | export default connect(mapStateToProps, mapDispatchToProps)(Dashboard); 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Redux shipping tool 2 | 3 | 4 | 5 | ## About 6 | This project is Redux Material UI using React.js, react-router, Redux, axios and Metronic Topic. 7 | Making the project with registering, sign in, reSetting password and Management users easily. This React example is written by [Big Silver]. 8 | 9 | ## Development Installation 10 | 11 | In the project's directory, run the following commands: 12 | 13 | ```bash 14 | # Clone our repo 15 | $ git clone https://github.com/Big-Silver/React-Redux-Material-UI.git React-Redux-Material-UI 16 | 17 | # Change directory to your app 18 | $ cd React-Redux-Material-UI 19 | 20 | # Install the project 21 | $ npm Install 22 | 23 | # Run the project 24 | $ npm start 25 | 26 | ``` 27 | Then Visit 28 | 29 | ```bash 30 | http://localhost:3002 31 | ``` 32 | 33 | ## Backend 34 | 35 | Please setup the following backend project on your end. 36 | ```bash 37 | https://github.com/Big-Silver/Node-Chat.git 38 | ``` 39 | 40 | ## Reference 41 | 42 | Before the project, you have to change the API address at `package.json`. 43 | 44 | ```bash 45 | "apiHost": "xxx.xxx.xxx.xxx" 46 | "apiPort": "xxxx" 47 | ``` 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/common/components/Home.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { bindActionCreators } from 'redux'; 3 | import {connect} from 'react-redux'; 4 | import {Field, reduxForm} from 'redux-form'; 5 | import Helmet from 'react-helmet' 6 | import { Grid, Row, Col } from 'react-bootstrap'; 7 | import classNames from 'classnames'; 8 | 9 | import * as UserActions from '../actions/user'; 10 | 11 | import MessagesList from "./MessagesList" 12 | import AddMessage from "./AddMessage" 13 | 14 | require('./style/home.css'); 15 | 16 | class Home extends Component { 17 | 18 | constructor(props) { 19 | super(props); 20 | } 21 | 22 | componentWillReceiveProps(nextProps) { 23 | if(!nextProps.user.logged){ 24 | this.props.history.pushState(null, "/"); 25 | } 26 | } 27 | 28 | render() { 29 | return ( 30 |
31 | 32 |
33 | 34 | {/* */} 35 |
36 |
37 | ); 38 | } 39 | } 40 | 41 | function mapStateToProps(state) { 42 | return { 43 | user : state.user 44 | }; 45 | } 46 | 47 | function mapDispatchToProps(dispatch) { 48 | return bindActionCreators(UserActions,dispatch); 49 | } 50 | 51 | export default connect(mapStateToProps, mapDispatchToProps)(Home); -------------------------------------------------------------------------------- /src/common/routes.js: -------------------------------------------------------------------------------- 1 | import { Route } from "react-router"; 2 | import React from "react"; 3 | import App from "./containers/App"; 4 | //Redux Smart 5 | import LoginPage from "./containers/LoginPage"; 6 | import SignupPage from "./containers/SignupPage"; 7 | 8 | import Dashboard from './components/personal/Dashboard'; 9 | //Redux Dumb 10 | import WorkSpace from "./components/WorkSpace"; 11 | import HomePage from "./components/Home"; 12 | import AboutPage from "./components/About"; 13 | import error404 from "./components/404"; 14 | import cookie from 'react-cookie'; 15 | 16 | function requireAuth() { 17 | // if(cookie.load('token')){ 18 | // return true; 19 | // }else{ 20 | // window.location = '/login' 21 | // } 22 | } 23 | 24 | function unRequireAuth() { 25 | // if(!cookie.load('token')){ 26 | // return true; 27 | // }else{ 28 | // window.location = '/home' 29 | // } 30 | } 31 | 32 | 33 | export default ( 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ); 44 | -------------------------------------------------------------------------------- /src/common/components/assets/TextInput.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | class TextInput extends Component { 5 | constructor(props, context) { 6 | super(props, context); 7 | this.state = { 8 | text: this.props.text || '' 9 | }; 10 | } 11 | 12 | handleSubmit(e) { 13 | const text = e.target.value.trim(); 14 | if (e.which === 13) { 15 | this.props.onSave(text); 16 | if (this.props.newTodo) { 17 | this.setState({ text: '' }); 18 | } 19 | } 20 | } 21 | 22 | handleChange(e) { 23 | this.setState({ text: e.target.value }); 24 | } 25 | 26 | handleBlur(e) { 27 | if (!this.props.newTodo) { 28 | this.props.onSave(e.target.value); 29 | } 30 | } 31 | 32 | render() { 33 | return ( 34 | 46 | ); 47 | } 48 | } 49 | 50 | TextInput.propTypes = { 51 | onSave: PropTypes.func.isRequired, 52 | text: PropTypes.string, 53 | placeholder: PropTypes.string, 54 | editing: PropTypes.bool, 55 | newTodo: PropTypes.bool 56 | }; 57 | 58 | export default TextInput; -------------------------------------------------------------------------------- /test/state/layout.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import addons from 'react/addons'; 3 | import expect from 'expect'; 4 | 5 | import * as LayoutActions from '../../src/common/actions/layout'; 6 | import configureStore from '../../src/common/store/configureStore'; 7 | 8 | describe('Layout State', function(){ 9 | 10 | before('render and locate element', function() { 11 | const store = configureStore({}); 12 | this.store = store; 13 | }); 14 | 15 | it('layout state should exist', function() { 16 | expect(this.store.getState().layout).toExist(); 17 | }); 18 | 19 | it('layout state to instansiate with...', function() { 20 | expect(this.store.getState().layout.present).toEqual({ 21 | sidebarOpen : false 22 | }); 23 | }); 24 | 25 | it('dispatch TOGGLE_SIDEBAR, state should change layout state', function() { 26 | expect(this.store.getState().layout.present).toEqual({ 27 | sidebarOpen : false 28 | }); 29 | this.store.dispatch(LayoutActions.toggleSidebar(true)); 30 | expect(this.store.getState().layout.present).toEqual({ 31 | sidebarOpen : true 32 | }); 33 | }); 34 | 35 | it('dispatch UNDO, state should change layout state', function() { 36 | this.store.dispatch(LayoutActions.undo()); 37 | expect(this.store.getState().layout.present).toEqual({ 38 | sidebarOpen : false 39 | }); 40 | }); 41 | 42 | it('dispatch REDO, state should change layout state', function() { 43 | this.store.dispatch(LayoutActions.redo()); 44 | expect(this.store.getState().layout.present).toEqual({ 45 | sidebarOpen : true 46 | }); 47 | }); 48 | 49 | }); -------------------------------------------------------------------------------- /src/common/api/user.js: -------------------------------------------------------------------------------- 1 | import request from 'axios'; 2 | import config from '../../../package.json'; 3 | 4 | 5 | export function getUser(token, callback) { 6 | if (!token) { 7 | return callback(false); 8 | } 9 | request 10 | .get(`http://${config.apiHost}:${config.apiPort}/api/users/check?access_token=${token}`) 11 | .then(function (response) { 12 | if (response.status === 200) { 13 | request 14 | .get(`http://${config.apiHost}:${config.apiPort}/api/users/${response.data.valid.userId}?access_token=${token}`) 15 | .then(function (response) { 16 | // console.log(response.data); 17 | callback(response.data); 18 | }) 19 | .catch(function (err) { 20 | // console.log(err); 21 | callback(false); 22 | }) 23 | } else { 24 | callback(false); 25 | } 26 | }) 27 | .catch(function (err) { 28 | // console.log(err); 29 | callback(false); 30 | }); 31 | } 32 | // return callback(false); 33 | //request() 34 | // Rather than immediately returning, we delay our code with a timeout to simulate asynchronous behavior 35 | // setTimeout(() => { 36 | // callback({ 37 | // name : 'John Smith', 38 | // dept : 'Web Team', 39 | // lastLogin : new Date(), 40 | // email : 'john@smith.com', 41 | // id : 'abcde1234' 42 | // }); 43 | // }, 500); 44 | 45 | // In the case of a real world API call, you'll normally run into a Promise like this: 46 | // API.getUser().then(user => callback(user)); -------------------------------------------------------------------------------- /test/render/Sidebar.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import addons from 'react/addons'; 3 | import expect from 'expect'; 4 | 5 | import Sidebar from '../../src/common/components/layout/Sidebar'; 6 | 7 | const TestUtils = React.addons.TestUtils; 8 | 9 | describe('Sidebar Rending', function(){ 10 | 11 | const user = { 12 | name : 'John Smith', 13 | dept : 'Web Team', 14 | lastLogin : new Date(), 15 | email : 'john@smith.com', 16 | id : 'abcde1234' 17 | }; 18 | const version = '0.0.1'; 19 | 20 | 21 | before('render and locate element', function() { 22 | const renderedComponent = TestUtils.renderIntoDocument( 23 | 24 | ); 25 | 26 | const username = TestUtils.findRenderedDOMComponentWithClass( 27 | renderedComponent, 28 | 'user-name' 29 | ); 30 | 31 | const versionNumber = TestUtils.findRenderedDOMComponentWithClass( 32 | renderedComponent, 33 | 'version' 34 | ); 35 | 36 | this.linkArray = TestUtils.scryRenderedDOMComponentsWithClass( 37 | renderedComponent, 38 | 'sidebar-nav-item' 39 | ); 40 | 41 | this.firstLink = this.linkArray[0].getDOMNode(); 42 | this.username = username.getDOMNode(); 43 | this.versionNumber = versionNumber.getDOMNode(); 44 | 45 | }); 46 | 47 | it('user name should be "' + user.name+ '"', function() { 48 | expect(this.username.textContent).toBe(user.name); 49 | }); 50 | 51 | it('version should not be ' + version , function() { 52 | expect(this.versionNumber.textContent).toBe('Currently version ' + version); 53 | }); 54 | 55 | it('There should be 5 Navigation Links', function() { 56 | expect(this.linkArray.length - 1).toBe(5); 57 | }); 58 | 59 | it('First link should be "Home [static]"', function() { 60 | expect(this.firstLink.textContent).toBe('Home [static]'); 61 | }); 62 | 63 | }); -------------------------------------------------------------------------------- /test/behaviour/Sidebar.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import addons from 'react/addons'; 3 | import expect from 'expect'; 4 | 5 | import App from '../../src/common/containers/App'; 6 | import { Provider } from 'react-redux'; 7 | import configureStore from '../../src/common/store/configureStore'; 8 | 9 | const TestUtils = React.addons.TestUtils; 10 | 11 | describe('Sidebar behaviour', function(){ 12 | 13 | before('render and locate element', function() { 14 | const store = configureStore({}); 15 | 16 | const renderedComponent = TestUtils.renderIntoDocument( 17 | 18 | {()=> 19 | 20 | } 21 | 22 | ); 23 | 24 | const wrapper = TestUtils.findRenderedDOMComponentWithClass( 25 | renderedComponent, 26 | 'wrapper' 27 | ); 28 | 29 | const sidebar = TestUtils.findRenderedDOMComponentWithClass( 30 | renderedComponent, 31 | 'sidebar' 32 | ); 33 | 34 | const sidebarToggle = TestUtils.findRenderedDOMComponentWithClass( 35 | renderedComponent, 36 | 'sidebar-toggle' 37 | ); 38 | 39 | this.wrapper = wrapper.getDOMNode(); 40 | this.sidebar = sidebar.getDOMNode(); 41 | this.sidebarToggle = sidebarToggle.getDOMNode(); 42 | 43 | }); 44 | 45 | it('sidebar should exist', function() { 46 | expect(this.sidebar).toExist(); 47 | }); 48 | 49 | it('sidebar should be closed', function() { 50 | expect(this.sidebar.getAttribute('class')).toBe('sidebar'); 51 | }); 52 | 53 | it('sidebar toggle should exist', function() { 54 | expect(this.sidebarToggle).toExist(); 55 | }); 56 | 57 | it('clicking sidebar toggle should open sidebar', function() { 58 | expect(this.wrapper.getAttribute('class')).toBe('wrapper'); 59 | TestUtils.Simulate.click(this.sidebarToggle); 60 | expect(this.wrapper.getAttribute('class')).toBe('wrapper open'); 61 | }); 62 | 63 | }); -------------------------------------------------------------------------------- /src/common/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import DevTools from '../containers/DevTools'; 3 | import { reduxReactRouter } from 'redux-router'; 4 | import thunk from 'redux-thunk'; 5 | import createHistory from 'history/lib/createBrowserHistory'; 6 | import createLogger from 'redux-logger'; 7 | import promiseMiddleware from '../api/promiseMiddleware'; 8 | import rootReducer from '../reducers'; 9 | 10 | const middlewareBuilder = () => { 11 | 12 | let middleware = {}; 13 | let universalMiddleware = [thunk,promiseMiddleware]; 14 | let allComposeElements = []; 15 | 16 | if(process.browser){ 17 | if(process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test'){ 18 | middleware = applyMiddleware(...universalMiddleware); 19 | allComposeElements = [ 20 | middleware, 21 | reduxReactRouter({ 22 | createHistory 23 | }) 24 | ] 25 | }else{ 26 | middleware = applyMiddleware(...universalMiddleware,createLogger()); 27 | allComposeElements = [ 28 | middleware, 29 | reduxReactRouter({ 30 | createHistory 31 | }), 32 | DevTools.instrument() 33 | ] 34 | } 35 | }else{ 36 | middleware = applyMiddleware(...universalMiddleware); 37 | allComposeElements = [ 38 | middleware 39 | ] 40 | } 41 | 42 | return allComposeElements; 43 | 44 | } 45 | 46 | const finalCreateStore = compose(...middlewareBuilder())(createStore); 47 | 48 | export default function configureStore(initialState) { 49 | const store = finalCreateStore(rootReducer, initialState); 50 | 51 | if (module.hot) { 52 | // Enable Webpack hot module replacement for reducers 53 | module.hot.accept('../reducers', () => { 54 | const nextRootReducer = require('../reducers'); 55 | store.replaceReducer(nextRootReducer); 56 | }); 57 | } 58 | 59 | return store; 60 | } -------------------------------------------------------------------------------- /src/common/components/layout/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router'; 3 | import classNames from 'classnames'; 4 | 5 | class Sidebar extends Component { 6 | 7 | constructor(props){ 8 | super(props); 9 | } 10 | 11 | render() { 12 | 13 | const {version,user} = this.props; 14 | 15 | return ( 16 | 17 |
18 | 19 |
20 |

This is an example of a isomorphic website built with Redux and React

21 |

Logged in as {user.name}

22 |
23 | 24 | 33 | 34 |
35 |

36 | Visit GitHub Repo
37 | Based on Lanyon Theme 38 |

39 |
40 | 41 |
42 | ); 43 | } 44 | } 45 | 46 | export default Sidebar; 47 | -------------------------------------------------------------------------------- /src/common/containers/ExternalLayout.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { bindActionCreators } from 'redux'; 3 | import request from 'axios'; 4 | import { connect } from 'react-redux'; 5 | import { Link } from 'react-router'; 6 | import classNames from 'classnames'; 7 | import * as LayoutActions from '../actions/layout'; 8 | import * as UserActions from '../actions/user'; 9 | import Helmet from 'react-helmet'; 10 | import Home from '../components/Home'; 11 | import LoginPage from "./LoginPage"; 12 | import SignupPage from "./SignupPage"; 13 | import Header from '../components/layout/Header'; 14 | import Paper from 'material-ui/lib/paper'; 15 | import cookie from 'react-cookie'; 16 | import { Grid, Row, Col } from 'react-bootstrap'; 17 | 18 | 19 | class ExternalLayout extends Component { 20 | 21 | constructor(props){ 22 | super(props); 23 | this.eventToggleSidebar = this.eventToggleSidebar.bind(this) 24 | this.eventUndo = this.eventUndo.bind(this) 25 | this.eventRedo = this.eventRedo.bind(this) 26 | } 27 | 28 | componentWillReceiveProps(nextState) { 29 | if(nextState.user.token && !cookie.load('token')) { 30 | console.log('Setting up token in cookie'); 31 | cookie.save('token', nextState.user.token); 32 | } 33 | // if(nextState.user.token && !nextState.user.info) { 34 | // this.props.getUserInfo(nextState.user); 35 | // } 36 | 37 | if(nextState.user.clearCookie && cookie.load('token')) { 38 | cookie.remove('token'); 39 | this.props.toogleClearCookie(); 40 | } 41 | } 42 | 43 | eventToggleSidebar(e) { 44 | e.preventDefault(); 45 | this.props.toggleSidebar(!this.props.layout.sidebarOpen); 46 | } 47 | 48 | eventUndo(e) { 49 | e.preventDefault(); 50 | this.props.undo(); 51 | } 52 | 53 | eventRedo(e) { 54 | e.preventDefault(); 55 | this.props.redo(); 56 | } 57 | 58 | render() { 59 | 60 | const { user, version } = this.props; 61 | 62 | return ( 63 |
64 |
65 | 66 | {this.props.children} 67 | 68 |
69 |
70 | ); 71 | } 72 | } 73 | 74 | function mapStateToProps(state) { 75 | return { 76 | user : state.user 77 | }; 78 | } 79 | 80 | function mapDispatchToProps(dispatch) { 81 | return bindActionCreators(Object.assign({}, LayoutActions, UserActions), dispatch); 82 | } 83 | 84 | export default connect(mapStateToProps, mapDispatchToProps)(ExternalLayout); 85 | -------------------------------------------------------------------------------- /src/common/containers/InternalLayout.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { bindActionCreators } from 'redux'; 3 | import request from 'axios'; 4 | import { connect } from 'react-redux'; 5 | import { Link } from 'react-router'; 6 | import classNames from 'classnames'; 7 | import * as LayoutActions from '../actions/layout'; 8 | import * as UserActions from '../actions/user'; 9 | import Helmet from 'react-helmet'; 10 | import Home from '../components/Home'; 11 | import LoginPage from "./LoginPage"; 12 | import SignupPage from "./SignupPage"; 13 | import Header from '../components/layout/Header'; 14 | import Paper from 'material-ui/lib/paper'; 15 | import cookie from 'react-cookie'; 16 | import { Grid, Row, Col } from 'react-bootstrap'; 17 | 18 | 19 | class InternalLayout extends Component { 20 | 21 | constructor(props){ 22 | super(props); 23 | this.eventToggleSidebar = this.eventToggleSidebar.bind(this) 24 | this.eventUndo = this.eventUndo.bind(this) 25 | this.eventRedo = this.eventRedo.bind(this) 26 | } 27 | 28 | componentWillReceiveProps(nextState) { 29 | if(nextState.user.token && !cookie.load('token')) { 30 | console.log('Setting up token in cookie'); 31 | cookie.save('token', nextState.user.token); 32 | } 33 | // if(nextState.user.token && !nextState.user.info) { 34 | // this.props.getUserInfo(nextState.user); 35 | // } 36 | 37 | if(nextState.user.clearCookie && cookie.load('token')) { 38 | cookie.remove('token'); 39 | this.props.toogleClearCookie(); 40 | } 41 | } 42 | 43 | eventToggleSidebar(e) { 44 | e.preventDefault(); 45 | this.props.toggleSidebar(!this.props.layout.sidebarOpen); 46 | } 47 | 48 | eventUndo(e) { 49 | e.preventDefault(); 50 | this.props.undo(); 51 | } 52 | 53 | eventRedo(e) { 54 | e.preventDefault(); 55 | this.props.redo(); 56 | } 57 | 58 | render() { 59 | 60 | const { user, version } = this.props; 61 | 62 | 63 | return ( 64 |
65 |
66 |
67 |
68 |
69 | 70 | {this.props.children} 71 | 72 |
73 |
74 | ); 75 | } 76 | } 77 | 78 | function mapStateToProps(state) { 79 | return { 80 | user : state.user 81 | }; 82 | } 83 | 84 | function mapDispatchToProps(dispatch) { 85 | return bindActionCreators(Object.assign({}, LayoutActions, UserActions), dispatch); 86 | } 87 | 88 | export default connect(mapStateToProps, mapDispatchToProps)(InternalLayout); 89 | -------------------------------------------------------------------------------- /styles/index.css: -------------------------------------------------------------------------------- 1 | html{ 2 | font-family: 'Roboto', sans-serif; 3 | -webkit-font-smoothing: antialiased; 4 | } 5 | body{ 6 | margin:0px; 7 | } 8 | 9 | a{ 10 | text-decoration: none; 11 | } 12 | /* ===== App ====== */ 13 | /*.paper_dialog { 14 | margin-top: 64px; 15 | }*/ 16 | .home-page { 17 | margin-left: 256px 18 | } 19 | 20 | .paper_dialog div { 21 | box-shadow: 0 0 0 rgba(0,0,0,0.12), 0 0 0 rgba(0,0,0,0.12)! important 22 | } 23 | 24 | .paper_dialog div form { 25 | text-align: center; 26 | box-shadow: 0 1px 4px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.12)! important; 27 | width: 500px; 28 | height: 520px; 29 | margin: 150px auto; 30 | } 31 | 32 | /* ===== Header ====== */ 33 | .layout_header_bar #appbar_menu { 34 | color: #fff; 35 | } 36 | 37 | /* ===== Sign In ===== */ 38 | .form-title { 39 | padding-top: 25px; 40 | color: #32c5d2; 41 | } 42 | 43 | .sign_nav_space1 { 44 | height: 25px; 45 | } 46 | 47 | .form-login button{ 48 | margin-left: -170px; 49 | } 50 | 51 | .forgot_password { 52 | color: #00bcd4! important; 53 | margin-right: -91px; 54 | cursor: pointer; 55 | } 56 | 57 | .sign_nav_space1_span { 58 | height: 25px; 59 | border-top: 1px outset; 60 | } 61 | 62 | .login_option { 63 | margin: 0px auto; 64 | width: 318px; 65 | } 66 | 67 | .login_option h4 { 68 | font-weight: 600; 69 | font-size: 15px; 70 | color: #7d91aa!important; 71 | } 72 | 73 | .login_social_option li { 74 | float: left; 75 | display: inline; 76 | list-style: none; 77 | margin-right: 5px; 78 | margin-bottom: 5px; 79 | text-indent: -9999px; 80 | } 81 | 82 | .login_social_option li .facebook { 83 | background: url(./images/facebook.png); 84 | } 85 | 86 | .login_social_option li .googleplus { 87 | background: url(./images/googleplus.png); 88 | } 89 | 90 | .login_social_option li>a { 91 | cursor: pointer; 92 | width: 28px; 93 | height: 28px; 94 | display: block; 95 | -webkit-border-radius: 15px!important; 96 | } 97 | 98 | .login_creat_account { 99 | margin: 0 -40px -30px; 100 | padding: 15px 0 17px; 101 | text-align: center; 102 | background-color: #6c7a8d; 103 | height: 100px; 104 | width: 500px; 105 | margin: 0; 106 | } 107 | 108 | .login_creat_account p { 109 | margin-top: 20px; 110 | } 111 | 112 | .login_creat_account p a { 113 | cursor: pointer; 114 | font-weight: 600; 115 | font-size: 14px; 116 | color: #c3cedd! important; 117 | } 118 | 119 | .back_login { 120 | background-color: transparent! important; 121 | border: 1px solid #00bcd4! important; 122 | } 123 | 124 | .back_login span { 125 | color: #00bcd4! important; 126 | } 127 | 128 | #main { 129 | grid-area: main; 130 | } 131 | #new-message { 132 | position: fixed; 133 | bottom: 0; 134 | width: 100%; 135 | padding: 5px; 136 | margin-left: 0px; 137 | border-top: 1px solid #3f3f3f; 138 | } 139 | #messages-list { 140 | padding: 5px 0 0 5px; 141 | } 142 | #sidebar { 143 | grid-area: sidebar; 144 | padding: 5px 0 0 5px; 145 | border-right: 1px solid #3f3f3f; 146 | height: 100%; 147 | } -------------------------------------------------------------------------------- /src/common/components/reset/ResetPage.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { browserHistory } from 'react-router'; 3 | import TextField from 'material-ui/lib/text-field'; 4 | import RaisedButton from 'material-ui/lib/raised-button'; 5 | import Paper from 'material-ui/lib/paper'; 6 | import { bindActionCreators } from 'redux'; 7 | import {connect} from 'react-redux'; 8 | import { Button } from 'react-bootstrap'; 9 | import { Grid, Row, Col } from 'react-bootstrap'; 10 | 11 | import Header from '../components/layout/Header'; 12 | 13 | import {reduxForm} from 'redux-form'; 14 | import Helmet from 'react-helmet' 15 | import * as UserActions from '../actions/user'; 16 | 17 | require('./reset/reset.css'); 18 | 19 | class Reset extends Component { 20 | 21 | constructor(props) { 22 | super(props); 23 | this.onSubmit = this.onSubmit.bind(this); 24 | this.onSetForget = this.onSetForget.bind(this); 25 | } 26 | 27 | componentWillReceiveProps(nextProps) { 28 | // if(nextProps.user.requestEmail){ 29 | // this.props.history.pushState(null, "/reset_password"); 30 | // } 31 | } 32 | 33 | onSubmit(event) { 34 | // const email = this.refs.email.getValue(); 35 | // this.props.requestEmail(email); 36 | } 37 | 38 | onSetForget(event) { 39 | // this.props.history.pushState(null, "/"); 40 | } 41 | 42 | render() { 43 | return ( 44 |
45 | 46 |

Forget Password ?

47 |
48 |

Enter your e-mail address below to reset your password.

49 |
50 | 56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | ); 70 | } 71 | } 72 | 73 | function mapStateToProps(state) { 74 | return { 75 | 76 | }; 77 | } 78 | 79 | function mapDispatchToProps(dispatch) { 80 | return bindActionCreators(UserActions,dispatch); 81 | } 82 | 83 | 84 | export default connect(mapStateToProps, mapDispatchToProps)(Reset); 85 | -------------------------------------------------------------------------------- /src/common/components/Forget.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { browserHistory } from 'react-router'; 3 | import TextField from 'material-ui/lib/text-field'; 4 | import RaisedButton from 'material-ui/lib/raised-button'; 5 | import Paper from 'material-ui/lib/paper'; 6 | import { bindActionCreators } from 'redux'; 7 | import {connect} from 'react-redux'; 8 | import { Button } from 'react-bootstrap'; 9 | import { Grid, Row, Col } from 'react-bootstrap'; 10 | 11 | import Header from '../components/layout/Header'; 12 | 13 | import {reduxForm} from 'redux-form'; 14 | import Helmet from 'react-helmet' 15 | import * as UserActions from '../actions/user'; 16 | 17 | require('./reset/reset.css'); 18 | 19 | class Forget extends Component { 20 | 21 | constructor(props) { 22 | super(props); 23 | this.onSubmit = this.onSubmit.bind(this); 24 | this.onSetLogin = this.onSetLogin.bind(this); 25 | } 26 | 27 | componentWillReceiveProps(nextProps) { 28 | if(nextProps.user.requestEmail){ 29 | this.props.history.pushState(null, "/reset_password"); 30 | } 31 | } 32 | 33 | onSubmit(event) { 34 | const email = this.refs.email.getValue(); 35 | this.props.requestEmail(email); 36 | } 37 | 38 | onSetLogin(event) { 39 | this.props.history.pushState(null, "/"); 40 | } 41 | 42 | render() { 43 | return ( 44 |
45 | 46 |

Forget Password ?

47 |
48 |

Enter your e-mail address below to reset your password.

49 |
50 | 56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | ); 70 | } 71 | } 72 | 73 | function mapStateToProps(state) { 74 | const request = state.user.requestEmail; 75 | return { 76 | user : state.user, 77 | requestEmail: request 78 | }; 79 | } 80 | 81 | function mapDispatchToProps(dispatch) { 82 | return bindActionCreators(UserActions,dispatch); 83 | } 84 | 85 | 86 | export default connect(mapStateToProps, mapDispatchToProps)(Forget); 87 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | var merge = require('merge'); 4 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 5 | 6 | var webpackConfig = { 7 | output: { 8 | path: path.join(__dirname, 'dist'), 9 | filename: 'bundle.js', 10 | publicPath: '/static/' 11 | }, 12 | plugins: [ 13 | new webpack.optimize.OccurenceOrderPlugin(), 14 | new webpack.NoErrorsPlugin() 15 | ] 16 | }; 17 | 18 | if (process.env.NODE_ENV === 'production') { 19 | 20 | webpackConfig = merge(webpackConfig,{ 21 | devtool: "source-map", 22 | entry : [ 23 | './src/client/index.js' 24 | ], 25 | module: { 26 | loaders: [{ 27 | test: /\.js$/, 28 | loader: 'babel', 29 | exclude: /node_modules/, 30 | include: __dirname 31 | }, 32 | { test: /\.(png|jpg|gif|jpeg)$/, loader: 'url-loader?limit=8192'}, 33 | { test: /\.json$/, loader: "json"}, 34 | { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap') } 35 | ]}, 36 | plugins : [ 37 | new webpack.DefinePlugin({ 38 | 'process.env': { 39 | NODE_ENV: JSON.stringify('production') 40 | } 41 | }), 42 | new ExtractTextPlugin("app.css"), 43 | new webpack.optimize.UglifyJsPlugin({minimize: true}) 44 | ] 45 | }); 46 | 47 | }else{ 48 | 49 | webpackConfig = merge(webpackConfig,{ 50 | devtool: 'inline-source-map', 51 | module: { 52 | loaders: [{ 53 | test: /\.js$/, 54 | loader: 'babel', 55 | exclude: /node_modules/, 56 | include: __dirname, 57 | env: { 58 | development: { 59 | plugins: [ 60 | 'react-transform' 61 | ], 62 | extra: { 63 | 'react-transform': { 64 | transforms: [{ 65 | transform: 'react-transform-hmr', 66 | imports: ['react'], 67 | locals: ['module'] 68 | }, 69 | { 70 | transform: 'react-transform-catch-errors', 71 | imports: ['react','redbox-react' ] 72 | } 73 | ]} 74 | } 75 | } 76 | },// 77 | query: { 78 | // optional: ['runtime'], 79 | presets: ['es2015', 'stage-2', 'react'], 80 | 81 | } 82 | }, 83 | { test: /\.(png|jpg|gif|jpeg)$/, loader: 'url-loader?limit=8192'}, 84 | { test: /\.json$/, loader: "json"}, 85 | // { test: /\.css$/, loader: 'style-loader!css-loader'} 86 | { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap') } 87 | 88 | ]}, 89 | entry : [ 90 | 'webpack-hot-middleware/client', 91 | './src/client/index.js' 92 | ], 93 | plugins : [ 94 | new webpack.HotModuleReplacementPlugin(), 95 | new ExtractTextPlugin("app.css") 96 | ] 97 | }); 98 | 99 | } 100 | 101 | module.exports = webpackConfig; -------------------------------------------------------------------------------- /src/common/components/layout/Header.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react'; 2 | import { browserHistory } from 'react-router'; 3 | import LeftNav from 'material-ui/lib/left-nav'; 4 | import {connect} from 'react-redux'; 5 | import {Link} from 'react-router'; 6 | import AppBar from 'material-ui/lib/app-bar'; 7 | import RaisedButton from 'material-ui/lib/raised-button'; 8 | import MenuItem from 'material-ui/lib/menus/menu-item'; 9 | import IconButton from 'material-ui/lib/icon-button'; 10 | import IconMenu from 'material-ui/lib/menus/icon-menu'; 11 | import NavigationClose from 'material-ui/lib/svg-icons/navigation/close'; 12 | import NavigationMoreVert from 'material-ui/lib/svg-icons/navigation/more-vert'; 13 | import FlatButton from 'material-ui/lib/flat-button'; 14 | import Helmet from 'react-helmet'; 15 | import ThemeManager from 'material-ui/lib/styles/theme-manager'; 16 | import PersonalTheme from '../../themes/personal'; 17 | import { bindActionCreators } from 'redux'; 18 | import * as UserActions from '../../actions/user'; 19 | 20 | class Header extends Component { 21 | 22 | constructor(props) { 23 | super(props); 24 | this.state = { 25 | open: true, 26 | users: [] 27 | }; 28 | this.handleToggle = this.handleToggle.bind(this); 29 | this.handleLogout = this.handleLogout.bind(this); 30 | this.handleWorkspace = this.handleWorkspace.bind(this); 31 | } 32 | 33 | componentWillMount() { 34 | // this.props.getUserInfo(); 35 | }; 36 | 37 | componentWillReceiveProps(nextProps) { 38 | this.setState({ 39 | users: this.props.user.users 40 | }) 41 | console.log('users: ', this.state.users); 42 | } 43 | 44 | handleLogout() { 45 | this.props.logout(this.props.user); 46 | } 47 | 48 | handleToggle() { 49 | this.setState({open: !this.state.open}); 50 | } 51 | 52 | handleWorkspace() { 53 | browserHistory.push('/'); 54 | } 55 | 56 | render() { 57 | const {user} = this.props; 58 | return ( 59 |
60 | { Menu} onTitleClick={this.handleToggle} 61 | zDepth={0} iconElementRight={ 62 | 65 | } 66 | targetOrigin={{horizontal: 'right', vertical: 'top'}} 67 | anchorOrigin={{horizontal: 'right', vertical: 'top'}} 68 | > 69 | 70 | 71 | 72 | }> 73 | } 74 | { 75 | {/*{!user.info && Login} 76 | {!user.info && Register}*/} 77 | } 78 |
79 | ); 80 | } 81 | } 82 | 83 | Header.getChildContext = { 84 | muiTheme: ThemeManager.getMuiTheme(PersonalTheme) 85 | }; 86 | 87 | function mapStateToProps(state) { 88 | return { 89 | user : state.user 90 | }; 91 | } 92 | 93 | function mapDispatchToProps(dispatch) { 94 | return bindActionCreators(UserActions,dispatch); 95 | } 96 | 97 | 98 | export default connect(mapStateToProps, mapDispatchToProps)(Header); -------------------------------------------------------------------------------- /src/common/containers/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { bindActionCreators } from 'redux'; 3 | import request from 'axios'; 4 | import { connect } from 'react-redux'; 5 | import { Link } from 'react-router'; 6 | import classNames from 'classnames'; 7 | import * as LayoutActions from '../actions/layout'; 8 | import * as UserActions from '../actions/user'; 9 | import Helmet from 'react-helmet'; 10 | import Home from '../components/Home'; 11 | import LoginPage from "./LoginPage"; 12 | import SignupPage from "./SignupPage"; 13 | import Header from '../components/layout/Header'; 14 | import Paper from 'material-ui/lib/paper'; 15 | import cookie from 'react-cookie'; 16 | import { Grid, Row, Col } from 'react-bootstrap'; 17 | 18 | 19 | class App extends Component { 20 | 21 | constructor(props){ 22 | super(props); 23 | this.eventToggleSidebar = this.eventToggleSidebar.bind(this) 24 | this.eventUndo = this.eventUndo.bind(this) 25 | this.eventRedo = this.eventRedo.bind(this) 26 | } 27 | 28 | componentWillReceiveProps(nextState) { 29 | if(nextState.user.token && !cookie.load('token')) { 30 | console.log('Setting up token in cookie'); 31 | cookie.save('token', nextState.user.token); 32 | } 33 | // if(nextState.user.token && !nextState.user.info) { 34 | // this.props.getUserInfo(nextState.user); 35 | // } 36 | 37 | if(nextState.user.clearCookie && cookie.load('token')) { 38 | cookie.remove('token'); 39 | this.props.toogleClearCookie(); 40 | } 41 | } 42 | 43 | eventToggleSidebar(e) { 44 | e.preventDefault(); 45 | this.props.toggleSidebar(!this.props.layout.sidebarOpen); 46 | } 47 | 48 | eventUndo(e) { 49 | e.preventDefault(); 50 | this.props.undo(); 51 | } 52 | 53 | eventRedo(e) { 54 | e.preventDefault(); 55 | this.props.redo(); 56 | } 57 | 58 | render() { 59 | 60 | const { user, version } = this.props; 61 | 62 | 63 | return ( 64 |
65 |
66 |
67 |
68 |
69 | 70 | 71 | 72 | 73 | {!user.sign && !user.logged && } 74 | 75 | 76 | 77 | 78 | {user.sign && !user.logged && } 79 | 80 | 81 | 82 | {/*
83 | {user.logged && } 84 |
*/} 85 | {/*{this.props.children}*/} 86 |
87 |
88 |
89 | ); 90 | } 91 | } 92 | 93 | function mapStateToProps(state) { 94 | return { 95 | version : state.version, 96 | user : state.user, 97 | layout : state.layout.present 98 | }; 99 | } 100 | 101 | function mapDispatchToProps(dispatch) { 102 | return bindActionCreators(Object.assign({}, LayoutActions, UserActions), dispatch); 103 | } 104 | 105 | export default connect(mapStateToProps, mapDispatchToProps)(App); 106 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "isomorphic-redux-app", 3 | "version": "0.1.1", 4 | "apiHost": "localhost", 5 | "apiPort": "3000", 6 | "description": "An example of a isomorphic Redux application", 7 | "scripts": { 8 | "slate": "rm -rf node_modules && npm install", 9 | "clean": "rm -rf dist", 10 | "start": "set NODE_ENV=development && node src/server/index.js --colors --profile", 11 | "start-prod": "set NODE_ENV=production && node src/server/index.js --progress --colors --profile", 12 | "build": "npm run clean && set NODE_ENV=production && webpack -p --progress --colors --profile", 13 | "test": "set NODE_ENV=test && mocha --compilers js:babel-core/register --recursive" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/Big-Silver/React-Redux-Material-UI.git" 18 | }, 19 | "license": "MIT", 20 | "dependencies": { 21 | "axios": "0.7.0", 22 | "bootstrap": "^4.0.0-alpha.6", 23 | "classnames": "^2.2.0", 24 | "cookie-parser": "^1.4.1", 25 | "express": "4.13.3", 26 | "history": "1.13.0", 27 | "inline-style-prefixer": "^0.6.3", 28 | "json-loader": "^0.5.4", 29 | "jsx-loader": "^0.13.2", 30 | "lodash.merge": "^4.0.1", 31 | "lodash.throttle": "^4.0.0", 32 | "material-ui": "^0.14.2", 33 | "mongodb": "^3.0.4", 34 | "querystring": "^0.2.0", 35 | "react": "^0.14.9", 36 | "react-addons-create-fragment": "^0.14.6", 37 | "react-addons-css-transition-group": "^15.6.0", 38 | "react-addons-pure-render-mixin": "^0.14.6", 39 | "react-addons-transition-group": "^0.14.8", 40 | "react-addons-update": "^0.14.6", 41 | "react-bootstrap": "^0.31.0", 42 | "react-cookie": "^0.4.3", 43 | "react-dom": "^0.14.9", 44 | "react-helmet": "^2.3.1", 45 | "react-material-ui-form-validator": "^2.0.0-beta.4", 46 | "react-redux": "^4.4.9", 47 | "react-router": "^2.7.0", 48 | "react-router-active-component": "^4.0.0-rc.0", 49 | "react-router-redux": "^4.0.5", 50 | "react-tap-event-plugin": "^0.2.2", 51 | "react-validation": "^3.0.7", 52 | "reactstrap": "^4.8.0", 53 | "redux": "^3.0.5", 54 | "redux-form": "^4.1.5", 55 | "redux-form-material-ui": "^5.0.0-beta.2", 56 | "redux-router": "^1.0.0-beta7", 57 | "redux-saga": "^0.16.0", 58 | "redux-thunk": "0.1.0", 59 | "redux-undo": "0.5.0", 60 | "socket.io-client": "^2.0.4", 61 | "warning": "^2.1.0" 62 | }, 63 | "devDependencies": { 64 | "babel-core": "^6.4.0", 65 | "babel-loader": "^6.2.1", 66 | "babel-plugin-react-transform": "^2.0.0", 67 | "babel-preset-es2015": "^6.3.13", 68 | "babel-preset-react": "^6.3.13", 69 | "babel-preset-stage-2": "^6.3.13", 70 | "babel-runtime": "^6.3.19", 71 | "css-loader": "0.9.0", 72 | "expect": "1.12.1", 73 | "extract-text-webpack-plugin": "0.8.2", 74 | "file-loader": "0.8.5", 75 | "jsdom-no-contextify": "3.1.0", 76 | "merge": "1.2.0", 77 | "mocha": "2.1.0", 78 | "react-transform-catch-errors": "^1.0.1", 79 | "react-transform-hmr": "^1.0.1", 80 | "redbox-react": "^1.2.0", 81 | "redux-devtools": "^3.0.1", 82 | "redux-devtools-dock-monitor": "^1.0.1", 83 | "redux-devtools-log-monitor": "^1.0.2", 84 | "redux-logger": "2.0.2", 85 | "style-loader": "0.8.0", 86 | "url-loader": "0.5.6", 87 | "webpack": "1.11.0", 88 | "webpack-dev-middleware": "1.2.0", 89 | "webpack-hot-middleware": "2.2.0" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/common/components/reset/Reset.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { browserHistory } from 'react-router'; 3 | import TextField from 'material-ui/lib/text-field'; 4 | import RaisedButton from 'material-ui/lib/raised-button'; 5 | import Paper from 'material-ui/lib/paper'; 6 | import { bindActionCreators } from 'redux'; 7 | import {connect} from 'react-redux'; 8 | import { Button } from 'react-bootstrap'; 9 | import { Grid, Row, Col } from 'react-bootstrap'; 10 | 11 | import Header from '../../components/layout/Header'; 12 | 13 | import {reduxForm} from 'redux-form'; 14 | import Helmet from 'react-helmet' 15 | import * as UserActions from '../../actions/user'; 16 | 17 | require('./reset.css'); 18 | 19 | class Reset extends Component { 20 | 21 | constructor(props) { 22 | super(props); 23 | this.onSubmit = this.onSubmit.bind(this); 24 | this.onSetForget = this.onSetForget.bind(this); 25 | } 26 | 27 | componentWillReceiveProps(nextProps) { 28 | if(nextProps.user.reSetPassowrd){ 29 | this.props.history.pushState(null, "/"); 30 | } 31 | } 32 | 33 | onSubmit(event) { 34 | const email = this.refs.email.getValue(); 35 | const code = this.refs.code.getValue(); 36 | const password = this.refs.password.getValue(); 37 | this.props.reSetPassword(email, code, password); 38 | } 39 | 40 | onSetForget(event) { 41 | this.props.history.pushState(null, "/forget_password"); 42 | } 43 | 44 | render() { 45 | return ( 46 |
47 | 48 |

Reset Password

49 |
50 |

Enter your e-mail address, reset code, reset password below to reset your password.

51 |
52 |
58 |
64 |
70 |
71 |
72 |
73 |
74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |
86 | 87 | ); 88 | } 89 | } 90 | 91 | function mapStateToProps(state) { 92 | return { 93 | 94 | }; 95 | } 96 | 97 | function mapDispatchToProps(dispatch) { 98 | return bindActionCreators(UserActions,dispatch); 99 | } 100 | export default connect(mapStateToProps, mapDispatchToProps)(Reset); 101 | -------------------------------------------------------------------------------- /src/client/index.js: -------------------------------------------------------------------------------- 1 | import 'babel-core/register'; 2 | import ReactDOM from 'react-dom'; 3 | import React from 'react'; 4 | import { ReduxRouter, Router, IndexRoute, Route, RouterContext, browserHistory } from 'react-router'; 5 | import { compose, createStore, applyMiddleware } from 'redux'; 6 | import createSagaMiddleware from 'redux-saga'; 7 | import { Provider } from 'react-redux'; 8 | import cookie from 'react-cookie'; 9 | import { syncHistoryWithStore, routerMiddleware } from 'react-router-redux'; 10 | import thunkMiddleware from 'redux-thunk'; 11 | import createLogger from 'redux-logger'; 12 | import createBrowserHistory from 'history/lib/createBrowserHistory' 13 | 14 | import configureStore from '../common/store/configureStore'; 15 | import routes from '../common/routes'; 16 | 17 | import App from "../common/containers/App"; 18 | import ExternalLayout from "../common/containers/ExternalLayout" 19 | import InternalLayout from "../common/containers/InternalLayout" 20 | 21 | import LoginPage from "../common/containers/LoginPage"; 22 | import SignupPage from "../common/containers/SignupPage"; 23 | import ForgetPage from "../common/containers/ForgetPage"; 24 | import ResetPage from "../common/containers/ResetPage"; 25 | import HomePage from "../common/components/Home"; 26 | import WorkSpace from "../common/components/WorkSpace"; 27 | 28 | import promiseMiddleware from '../common/api/promiseMiddleware'; 29 | 30 | import reducer from '../common/reducers/reducer'; 31 | // import { watcherSaga } from "../common/sagas/sagas"; 32 | 33 | import "../../styles/index.css"; 34 | import '../../node_modules/bootstrap/dist/css/bootstrap.css'; 35 | // const history = createBrowserHistory(); 36 | const initialState = window.__INITIAL_STATE__; 37 | // const store = configureStore(initialState); 38 | const rootElement = document.getElementById('root'); 39 | const sagaMiddleware = createSagaMiddleware(); 40 | const loggerMiddleware = createLogger(); 41 | const routeMiddleware = routerMiddleware(browserHistory); 42 | const store = createStore(reducer, {}, compose(applyMiddleware( 43 | promiseMiddleware, 44 | routeMiddleware, 45 | loggerMiddleware, 46 | thunkMiddleware, 47 | sagaMiddleware 48 | ), window.devToolsExtension ? window.devToolsExtension() : f => f)); 49 | 50 | // sagaMiddleware.run(watcherSaga); 51 | 52 | const history = syncHistoryWithStore(browserHistory, store); 53 | 54 | function requireAuth() { 55 | // if(cookie.load('token')){ 56 | // return true; 57 | // }else{ 58 | // window.location = '/login' 59 | // } 60 | } 61 | 62 | function unRequireAuth() { 63 | // if(!cookie.load('token')){ 64 | // return true; 65 | // }else{ 66 | // window.location = '/home' 67 | // } 68 | } 69 | 70 | function onRequire() { 71 | console.log('onRequire: ', store.getState()); 72 | } 73 | 74 | ReactDOM.render( 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | {/* 88 | 89 | 90 | 91 | */} 92 | 93 | , 94 | document.getElementById('root') 95 | ); 96 | // if (process.env.NODE_ENV !== 'production') { 97 | // var devtools = require('../server/devtools'); 98 | // devtools.default(store); 99 | // } -------------------------------------------------------------------------------- /src/common/components/Signup.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { bindActionCreators } from 'redux'; 3 | import {connect} from 'react-redux'; 4 | import {Field, reduxForm} from 'redux-form'; 5 | import Helmet from 'react-helmet' 6 | import { Grid, Row, Col } from 'react-bootstrap'; 7 | 8 | import TextField from 'material-ui/lib/text-field'; 9 | import RaisedButton from 'material-ui/lib/raised-button'; 10 | import Paper from 'material-ui/lib/paper'; 11 | 12 | import Header from '../components/layout/Header'; 13 | import * as UserActions from '../actions/user'; 14 | import { createUser, auth } from '../actions/user'; 15 | 16 | 17 | 18 | class Signup extends Component { 19 | 20 | constructor(props) { 21 | super(props); 22 | this.onSubmit = this.onSubmit.bind(this); 23 | this.onSetLogin = this.onSetLogin.bind(this); 24 | this.state={ 25 | values: { 26 | useremail: '', 27 | password: '' 28 | } 29 | } 30 | } 31 | 32 | componentWillReceiveProps(nextProps) { 33 | if(nextProps.user.signed){ 34 | this.props.history.pushState(null, "/home"); 35 | } 36 | } 37 | 38 | onSubmit(event) { 39 | event.preventDefault(); 40 | const search = this.props.location.search; 41 | const params = new URLSearchParams(search); 42 | const workspaceId = params.get('workspaceId'); 43 | const username = this.refs.username.getValue(); 44 | const email = this.refs.email.getValue(); 45 | const password = this.refs.password.getValue(); 46 | const confirm_password = this.refs.confirm_password.getValue(); 47 | if (password == confirm_password) { 48 | this.setState({ 49 | email: email, 50 | password: password 51 | }) 52 | this.props.createUser(username, email, password, workspaceId); 53 | } 54 | } 55 | 56 | gotoLogin() { 57 | this.props.history.pushState(null, "/home"); 58 | } 59 | 60 | onSetLogin() { 61 | this.props.history.pushState(null, "/"); 62 | } 63 | 64 | render() { 65 | console.log(this.props.isRegistered) 66 | let {isRegistered} = this.props 67 | let isRegisteredValue = isRegistered === undefined ? false : isRegistered 68 | return ( 69 |
70 | 71 |

Sign Up

72 |
79 |
86 |
94 |
102 | {isRegisteredValue && ( 103 | this.gotoLogin() 104 | )} 105 |
106 |
107 |
108 |
109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
120 | 121 | ); 122 | } 123 | } 124 | 125 | function mapStateToProps(state) { 126 | // console.log(state); 127 | const signed = state.user.signed; 128 | return { 129 | user : state.user, 130 | isRegistered: signed 131 | }; 132 | } 133 | 134 | function mapDispatchToProps(dispatch) { 135 | return bindActionCreators(UserActions,dispatch); 136 | } 137 | 138 | 139 | export default connect(mapStateToProps, mapDispatchToProps)(Signup); 140 | -------------------------------------------------------------------------------- /src/server/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import cookieParser from 'cookie-parser'; 3 | 4 | import webpack from 'webpack'; 5 | import webpackConfig from '../../webpack.config'; 6 | import webpackDevMiddleware from 'webpack-dev-middleware'; 7 | import webpackHotMiddleware from 'webpack-hot-middleware'; 8 | 9 | import React from 'react'; 10 | import ReactDOMServer from 'react-dom/server'; 11 | import { RouterContext, match } from 'react-router'; 12 | import { Provider } from 'react-redux'; 13 | import createLocation from 'history/lib/createLocation'; 14 | import { fetchComponentDataBeforeRender } from '../common/api/fetchComponentDataBeforeRender'; 15 | 16 | import configureStore from '../common/store/configureStore'; 17 | import { getUser } from '../common/api/user'; 18 | import routes from '../common/routes'; 19 | import packagejson from '../../package.json'; 20 | import Helmet from 'react-helmet'; 21 | 22 | import {connect} from 'react-redux'; 23 | 24 | 25 | const app = express(); 26 | // const MongoClient = require('mongodb').MongoClient; 27 | // const assert = require('assert'); 28 | 29 | // const url = 'mongodb://users:user1992@ds157723.mlab.com:57723/userauth'; 30 | // const dbName = 'userauth'; 31 | 32 | // MongoClient.connect(url, function(err, client) { 33 | // assert.equal(null, err); 34 | // console.log("Connected successfully to server"); 35 | 36 | // const db = client.db(dbName); 37 | 38 | // client.close(); 39 | // }); 40 | 41 | const renderFullPage = (html, initialState, head) => { 42 | return ` 43 | 44 | 45 | 46 | 47 | ${head.title} 48 | 49 | 50 | 51 | 52 |
${html}
53 | 56 | 57 | 58 | 59 | `; 60 | }; 61 | 62 | if(process.env.NODE_ENV !== 'production'){ 63 | const compiler = webpack(webpackConfig); 64 | app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: webpackConfig.output.publicPath })); 65 | app.use(webpackHotMiddleware(compiler)); 66 | }else{ 67 | app.use('/static', express.static(__dirname + '/../../dist')); 68 | } 69 | 70 | app.use(cookieParser()); 71 | 72 | app.use(function(req, res, next) { 73 | GLOBAL.navigator = { 74 | userAgent: req.headers['user-agent'] 75 | } 76 | next(); 77 | }); 78 | 79 | app.get('/*', function (req, res) { 80 | const location = createLocation(req.url); 81 | 82 | getUser(req.cookies.token || false, user => { 83 | console.log(user); 84 | match({ routes, location }, (err, redirectLocation, renderProps) => { 85 | 86 | if(err) { 87 | console.error(err); 88 | return res.status(500).end('Internal server error'); 89 | } 90 | if(!renderProps) { 91 | return res.status(404).end('Not found'); 92 | } 93 | 94 | var store = null; 95 | if(user) { 96 | console.log('Insert with user information') 97 | store = configureStore({ 98 | version: packagejson.version, 99 | user: { 100 | userId: user.id, 101 | info: user, 102 | token: req.cookies.token 103 | } 104 | }); 105 | }else{ 106 | console.log('Inser info without user') 107 | store = configureStore({version: packagejson.version}); 108 | } 109 | const InitialView = ( 110 | 111 | 112 | 113 | ); 114 | 115 | //This method waits for all render component promises to resolve before returning to browser 116 | fetchComponentDataBeforeRender(store.dispatch, renderProps.components, renderProps.params) 117 | .then(html => { 118 | const componentHTML = ReactDOMServer.renderToString(InitialView); 119 | const initialState = store.getState(); 120 | let head = Helmet.rewind(); 121 | res.status(200).end(renderFullPage(componentHTML,initialState, head)); 122 | }) 123 | .catch(err => { 124 | console.log(err); 125 | res.end(renderFullPage("",{})); 126 | }); 127 | }); 128 | 129 | } 130 | ); 131 | 132 | }); 133 | 134 | 135 | const server = app.listen(3002, function () { 136 | const port = server.address().port; 137 | console.log('Example app listening at http://localhost:%s', port); 138 | }); 139 | -------------------------------------------------------------------------------- /src/common/components/MessagesList.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { bindActionCreators } from 'redux'; 3 | import {connect} from 'react-redux'; 4 | import Message from "./Message" 5 | import classNames from 'classnames'; 6 | // import * as MessageActions from '../actions/message'; 7 | import * as UserActions from '../actions/user'; 8 | import io from "socket.io-client"; 9 | 10 | class MessagesList extends Component { 11 | 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | message: '', 16 | messages: [] 17 | }; 18 | this.socket = io('http://localhost:3000'); 19 | 20 | this.socket.on('RECEIVE_MESSAGE', function(data){ 21 | addMessage(data); 22 | }); 23 | 24 | const addMessage = data => { 25 | this.setState({messages: [...this.state.messages, data]}); 26 | }; 27 | 28 | this.sendMessage = ev => { 29 | var user_id = sessionStorage.getItem('rChat_user') 30 | ev.preventDefault(); 31 | this.socket.emit('SEND_MESSAGE', { 32 | user: user_id, 33 | message: this.state.message 34 | }) 35 | this.setState({message: ''}); 36 | } 37 | } 38 | 39 | componentWillMount() { 40 | this.props.init_message(); 41 | }; 42 | 43 | componentWillReceiveProps(nextProps) { 44 | this.setState({ 45 | messages: this.props.user.messages 46 | }); 47 | var msgs = this.props.user.messages; 48 | var total_group = []; 49 | var msg_group = []; 50 | var group_date = 0; 51 | setTimeout(() => { 52 | if (typeof msgs != 'undefined') { 53 | for(var i = 0; i < msgs.length; i++) { 54 | var d = new Date(msgs[i].date); 55 | if (msg_group.length == 0) { 56 | msg_group.push(msgs[i]); 57 | group_date = (d.getTime() / (1000 * 24 * 3600)).toFixed(0); 58 | } else { 59 | if (group_date == (d.getTime() / (1000 * 24 * 3600)).toFixed(0)) { 60 | msg_group.push(msgs[i]) 61 | } else { 62 | total_group.push( 63 | { 64 | 'date' : (d.getTime() / (1000 * 24 * 3600)).toFixed(0), 65 | 'value' : msg_group 66 | } 67 | ); 68 | msg_group = []; 69 | msg_group.push(msgs[i]); 70 | group_date = (d.getTime() / (1000 * 24 * 3600)).toFixed(0); 71 | } 72 | } 73 | if (i == msgs.length - 1) { 74 | total_group.push( 75 | { 76 | 'date' : group_date, 77 | 'value' : msg_group 78 | } 79 | ); 80 | } 81 | } 82 | } 83 | }, 100); 84 | }; 85 | 86 | render() { 87 | return ( 88 |
89 |
90 |
91 | {this.state.messages != [] && this.state.messages.map(function(message, i) { 92 | return ( 93 |
{message.user}: {message.message} : {message.date}
94 | ) 95 | })} 96 |
97 |
98 |
99 | this.setState({message: ev.target.value})}/> 100 |
101 | 102 |
103 |
104 | ); 105 | } 106 | } 107 | 108 | function mapStateToProps(state) { 109 | return { 110 | user: state.user 111 | }; 112 | } 113 | 114 | function mapDispatchToProps(dispatch) { 115 | return bindActionCreators(UserActions,dispatch); 116 | } 117 | 118 | 119 | export default connect(mapStateToProps, mapDispatchToProps)(MessagesList); 120 | 121 | // MessagesList.propTypes = { 122 | // messages: PropTypes.arrayOf( 123 | // PropTypes.shape({ 124 | // id: PropTypes.number.isRequired, 125 | // message: PropTypes.string.isRequired, 126 | // author: PropTypes.string.isRequired 127 | // }).isRequired 128 | // ).isRequired 129 | // } -------------------------------------------------------------------------------- /src/common/components/Login.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { browserHistory } from 'react-router'; 3 | import TextField from 'material-ui/lib/text-field'; 4 | import RaisedButton from 'material-ui/lib/raised-button'; 5 | import Paper from 'material-ui/lib/paper'; 6 | import { bindActionCreators } from 'redux'; 7 | import {connect} from 'react-redux'; 8 | import { Button } from 'react-bootstrap'; 9 | import { Grid, Row, Col } from 'react-bootstrap'; 10 | 11 | import Header from '../components/layout/Header'; 12 | 13 | import {reduxForm} from 'redux-form'; 14 | import Helmet from 'react-helmet' 15 | import * as UserActions from '../actions/user'; 16 | 17 | 18 | class Login extends Component { 19 | 20 | constructor(props) { 21 | super(props); 22 | this.onSubmit = this.onSubmit.bind(this); 23 | this.onSetSign = this.onSetSign.bind(this); 24 | this.onLoginFB = this.onLoginFB.bind(this); 25 | this.onLoginGG = this.onLoginGG.bind(this); 26 | this.onForgetPassword = this.onForgetPassword.bind(this); 27 | } 28 | 29 | componentWillReceiveProps(nextProps) { 30 | if(nextProps.user.logged){ 31 | this.props.history.pushState(null, "/home"); 32 | } 33 | } 34 | 35 | onSubmit(event) { 36 | event.preventDefault(); 37 | const search = this.props.location.search; 38 | const params = new URLSearchParams(search); 39 | const workspaceId = params.get('workspaceId'); 40 | const username = this.refs.email.getValue(); 41 | const password = this.refs.password.getValue(); 42 | this.props.auth(username, password, workspaceId); 43 | } 44 | 45 | onSetSign(event) { 46 | const search = this.props.location.search; 47 | const params = new URLSearchParams(search); 48 | const workspaceId = params.get('workspaceId'); 49 | this.props.history.push({ 50 | pathname: '/register', 51 | search: '?workspaceId=' + workspaceId, 52 | }) 53 | } 54 | 55 | onLoginFB(event) { 56 | event.preventDefault(); 57 | this.props.authFB(event); 58 | } 59 | 60 | onLoginGG(event) { 61 | console.log(this.props) 62 | event.preventDefault(); 63 | this.props.authGG(event); 64 | } 65 | 66 | onStateSubmit() { 67 | const username = this.refs.email.getValue(); 68 | const password = this.refs.password.getValue(); 69 | if(username =="" && password == "") 70 | return false; 71 | else 72 | return true; 73 | } 74 | 75 | onForgetPassword(event) { 76 | this.props.history.pushState(null, "/forget_password"); 77 | } 78 | 79 | gotoHome() { 80 | console.log("gothome") 81 | // this.props.history.pushState(null, "/home"); 82 | } 83 | 84 | render() { 85 | let {isLogged} = this.props 86 | let isLoggedValue = isLogged === undefined ? false : isLogged 87 | return ( 88 |
89 | 90 |

Sign In

91 |
92 |
98 |
105 | {isLoggedValue && ( 106 | this.gotoHome() 107 | )} 108 |
109 |
110 |
111 | 112 | Forgot Password? 113 |
114 |
115 |
116 |
117 | {/*
118 | 119 | 120 | 121 |

Or login with

122 | 123 | 124 |
    125 |
  • 126 |
  • 127 |
128 | 129 |
130 |
131 |
*/} 132 |
133 |
134 |

SIGN UP FOR ACCOUNT

135 |
136 | 137 | ); 138 | } 139 | } 140 | 141 | function mapStateToProps(state) { 142 | const logged = state.user.logged; 143 | return { 144 | user : state.user, 145 | isLogged: logged 146 | }; 147 | } 148 | 149 | function mapDispatchToProps(dispatch) { 150 | return bindActionCreators(UserActions,dispatch); 151 | } 152 | 153 | 154 | export default connect(mapStateToProps, mapDispatchToProps)(Login); 155 | -------------------------------------------------------------------------------- /src/common/actions/user.js: -------------------------------------------------------------------------------- 1 | import request from 'axios'; 2 | import Querystring from 'querystring'; 3 | import config from '../../../package.json'; 4 | 5 | export const REGISTER_USER = 'REGISTER_USER'; 6 | export const REGISTER_USER_SUCCESS = 'REGISTER_USER_SUCCESS'; 7 | export const REGISTER_USER_FAILURE = 'REGISTER_USER_FAILURE'; 8 | 9 | export const LOGIN = 'LOGIN'; 10 | export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; 11 | export const LOGIN_FAILURE = 'LOGIN_FAILURE'; 12 | 13 | export const LOGIN_FB = 'LOGIN_FB'; 14 | export const LOGIN_FB_SUCCESS = 'LOGIN_FB_SUCCESS'; 15 | export const LOGIN_FB_FAILURE = 'LOGIN_FB_FAILURE'; 16 | 17 | export const LOGIN_GG = 'LOGIN_GG'; 18 | export const LOGIN_GGSUCCESS = 'LOGIN_GG_SUCCESS'; 19 | export const LOGIN_GG_FAILURE = 'LOGIN_GG_FAILURE'; 20 | 21 | export const GET_USER_INFO = 'GET_USER_INFO'; 22 | export const GET_USER_INFO_SUCCESS = 'GET_USER_INFO_SUCCESS'; 23 | export const GET_USER_INFO_FAILURE = 'GET_USER_INFO_FAILURE'; 24 | 25 | export const REQUEST_EMAIL = 'REQUEST_EMAIL'; 26 | export const REQUEST_EMAIL_SUCCESS = 'REQUEST_EMAIL_SUCCESS'; 27 | export const REQUEST_EMAIL_FAILURE = 'REQUEST_EMAIL_FAILURE'; 28 | 29 | export const RESET_PASSWORD = 'RESET_PASSWORD'; 30 | export const RESET_PASSWORD_SUCCESS = 'RESET_PASSWORD_SUCCESS'; 31 | export const RESET_PASSWORD_FAILURE = 'RESET_PASSWORD_FAILURE'; 32 | 33 | export const LOGOUT = 'LOGOUT'; 34 | export const LOGOUT_REQUEST = 'LOGOUT_REQUEST'; 35 | export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; 36 | export const LOGOUT_FAILURE = 'LOGOUT_FAILURE'; 37 | 38 | export const CLEAR_COOKIE = 'CLEAR_COOKIE'; 39 | 40 | export const INIT_MESSAGE = 'INIT_MESSAGE'; 41 | export const INIT_MESSAGE_SUCCESS = 'INIT_MESSAGE_SUCCESS'; 42 | export const INIT_MESSAGE_FAILURE = 'INIT_MESSAGE_FAILURE'; 43 | 44 | export const INIT_WORKSPACE = 'INIT_WORKSPACE'; 45 | export const INIT_WORKSPACE_SUCCESS = 'INIT_WORKSPACE_SUCCESS'; 46 | export const INIT_WORKSPACE_FAILURE = 'INIT_WORKSPACE_FAILURE'; 47 | 48 | export const CREATE_WORKSPACE = 'CREATE_WORKSPACE'; 49 | export const CREATE_WORKSPACE_SUCCESS = 'CREATE_WORKSPACE_SUCCESS'; 50 | export const CREATE_WORKSPACE_FAILURE = 'CREATE_WORKSPACE_FAILURE'; 51 | 52 | export const FIND_WORKSPACE = 'FIND_WORKSPACE'; 53 | export const FIND_WORKSPACE_SUCCESS = 'FIND_WORKSPACE_SUCCESS'; 54 | export const FIND_WORKSPACE_FAILURE = 'FIND_WORKSPACE_FAILURE'; 55 | 56 | var http_config = { 57 | headers: { 58 | 'Accept': 'application/json', 59 | 'Content-Type': 'application/x-www-form-urlencoded', 60 | } 61 | }; 62 | 63 | export function getUserInfo() { 64 | return { 65 | type: GET_USER_INFO, 66 | promise: request.get(`http://${config.apiHost}:${config.apiPort}/users`, http_config) 67 | }; 68 | } 69 | 70 | export function createUser(username, email, password, ws_Id) { 71 | var data = Querystring.stringify({ 72 | "name": username, 73 | "email": email, 74 | "password": password, 75 | "workspace": ws_Id 76 | }); 77 | return { 78 | type: REGISTER_USER, 79 | promise: request.post(`http://${config.apiHost}:${config.apiPort}/register`, data, http_config) 80 | }; 81 | } 82 | 83 | export function auth(email, password, wsId) { 84 | var data = Querystring.stringify({ 85 | "workspaceId": wsId, 86 | "email": email, 87 | "password": password 88 | }); 89 | return { 90 | type: LOGIN, 91 | promise: request.post(`http://${config.apiHost}:${config.apiPort}/login`, data, http_config) 92 | }; 93 | } 94 | 95 | export function authFB() { 96 | return { 97 | type: LOGIN_FB, 98 | promise: request.post(`http://${config.apiHost}/auth/facebook`) 99 | }; 100 | } 101 | 102 | export function authGG() { 103 | return { 104 | type: LOGIN_GG, 105 | promise: request.post(`http://${config.apiHost}/auth/google`) 106 | }; 107 | } 108 | 109 | export function requestEmail(email) { 110 | var requestUrl = `http://${config.apiHost}:${config.apiPort}/reset?email=` + encodeURIComponent(email); 111 | return { 112 | type: REQUEST_EMAIL, 113 | promise: request.get(`${requestUrl}`) 114 | }; 115 | } 116 | 117 | export function reSetPassword(email, code, password) { 118 | /* Server sent the code */ 119 | 120 | // return { 121 | // type: RESET_PASSWORD, 122 | // promise: request.push(`http://${config.apiHost}/user/reset`, {UserEmail: email, ResetCode: code, Password: password}) 123 | // }; 124 | 125 | var data = Querystring.stringify({ 126 | "email": email, 127 | "password": password 128 | }); 129 | return { 130 | type: RESET_PASSWORD, 131 | promise: request.push(`http://${config.apiHost}:${config.apiPort}/reset`, data, http_config) 132 | }; 133 | } 134 | 135 | export function logout(user) { 136 | return { 137 | type: LOGOUT, 138 | payload: user 139 | } 140 | } 141 | 142 | export function toogleClearCookie() { 143 | return { 144 | type: CLEAR_COOKIE 145 | }; 146 | } 147 | 148 | export function init_message() { 149 | return { 150 | type: INIT_MESSAGE, 151 | promise: request.get(`http://${config.apiHost}:${config.apiPort}/message`, http_config) 152 | }; 153 | } 154 | 155 | export function create_workspace(infos) { 156 | var data = Querystring.stringify({ 157 | "name": infos.displayName, 158 | "fullName": infos.fullName, 159 | "admin": infos.email, 160 | "password" :infos.password 161 | }); 162 | return { 163 | type: CREATE_WORKSPACE, 164 | promise: request.post(`http://${config.apiHost}:${config.apiPort}/create_workspace`, data, http_config) 165 | } 166 | } 167 | 168 | export function init_workspaces() { 169 | return { 170 | type: INIT_WORKSPACE, 171 | promise: request.get(`http://${config.apiHost}:${config.apiPort}/init_workspaces`, http_config) 172 | } 173 | } 174 | 175 | export function find_workspace(email) { 176 | var data = Querystring.stringify({ 177 | "f_email": email 178 | }); 179 | return { 180 | type: FIND_WORKSPACE, 181 | promise: request.post(`http://${config.apiHost}:${config.apiPort}/find_workspace`, data, http_config) 182 | } 183 | } -------------------------------------------------------------------------------- /src/common/reducers/user.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { bindActionCreators } from 'redux'; 3 | import * as UserActions from '../actions/user'; 4 | import { auth } from '../actions/user'; 5 | import { 6 | LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE, 7 | LOGIN_FB_SUCCESS, LOGIN_FB_FAILURE, 8 | LOGIN_GG_SUCCESS, LOGIN_GG_FAILURE, 9 | GET_USER_INFO_FAILURE, GET_USER_INFO_SUCCESS, 10 | REQUEST_EMAIL_SUCCESS, REQUEST_EMAIL_FAILURE, 11 | RESET_PASSWORD_SUCCESS, RESET_PASSWORD_FAILURE, 12 | LOGOUT, LOGOUT_SUCCESS, 13 | CLEAR_COOKIE, 14 | REGISTER_USER_SUCCESS, REGISTER_USER_FAILURE, 15 | SET_LOGIN, SET_SIGN, 16 | INIT_MESSAGE, INIT_MESSAGE_SUCCESS, INIT_MESSAGE_FAILURE, 17 | CREATE_WORKSPACE, CREATE_WORKSPACE_SUCCESS, CREATE_WORKSPACE_FAILURE, 18 | INIT_WORKSPACE, INIT_WORKSPACE_SUCCESS, INIT_WORKSPACE_FAILURE, 19 | FIND_WORKSPACE, FIND_WORKSPACE_SUCCESS, FIND_WORKSPACE_FAILURE 20 | } from '../actions/user'; 21 | 22 | const initialState = { signed: false, logged: false, login: false, sign: false }; 23 | 24 | export default function user(state = initialState, action) { 25 | switch (action.type) { 26 | case LOGIN_REQUEST: 27 | return true; 28 | break; 29 | case LOGIN_SUCCESS: 30 | var user_session = sessionStorage.getItem('rChat_user'); 31 | sessionStorage.setItem('rChat_user', action.req.data[0]._id); 32 | sessionStorage.setItem('rUser_name', action.req.data[0].name); 33 | return Object.assign({}, state, { 34 | logged: true 35 | }); 36 | break; 37 | case LOGIN_FAILURE: 38 | return Object.assign({}, state, { 39 | error: 'Login is Failed.' 40 | }); 41 | break; 42 | case LOGIN_FB_SUCCESS: 43 | console.log("facebook login") 44 | break; 45 | case LOGIN_FB_FAILURE: 46 | return Object.assign({}, state, { 47 | error: "You are not logged in. Please log in and try again." 48 | }); 49 | break; 50 | case LOGIN_GG_SUCCESS: 51 | console.log("google login") 52 | break; 53 | case LOGIN_GG_FAILURE: 54 | console.log("google fail") 55 | break; 56 | case REGISTER_USER_SUCCESS: 57 | var user_session = sessionStorage.getItem('rChat_user'); 58 | sessionStorage.setItem('rChat_user', action.req.data._id); 59 | sessionStorage.setItem('rUser_name', action.req.data.name); 60 | return Object.assign({}, state, { 61 | signed: true 62 | }); 63 | break; 64 | case REGISTER_USER_FAILURE: 65 | return Object.assign({}, state, { 66 | error: 'Register is Failed.' 67 | }); 68 | break; 69 | case REQUEST_EMAIL_SUCCESS: 70 | return Object.assign({}, state, { 71 | requestEmail: true 72 | }); 73 | break; 74 | case REQUEST_EMAIL_FAILURE: 75 | return Object.assign({}, state, { 76 | error: action.error.data.ResponseStatus.Message 77 | }); 78 | break; 79 | case RESET_PASSWORD_SUCCESS: 80 | return Object.assign({}, state, { 81 | reSetPassowrd: true 82 | }); 83 | break; 84 | case RESET_PASSWORD_FAILURE: 85 | return Object.assign({}, state, { 86 | error: action.error.data.ResponseStatus.Message 87 | }); 88 | break; 89 | case GET_USER_INFO_SUCCESS: 90 | console.log('GET_USER_INFO_SUCCESS: ', action) 91 | return Object.assign({}, state, { 92 | users: action.req.data[0] 93 | }); 94 | break; 95 | case GET_USER_INFO_FAILURE: 96 | return Object.assign({}, state, { 97 | error: 'Getting user_info is Failed.' 98 | }); 99 | break; 100 | case CLEAR_COOKIE: 101 | return Object.assign({}, state, { 102 | clearCookie: false 103 | }); 104 | break; 105 | case SET_LOGIN: 106 | return Object.assign({}, state, { 107 | sign: false 108 | }); 109 | break; 110 | case SET_SIGN: 111 | return Object.assign({}, state, { 112 | sign: true 113 | }); 114 | break; 115 | case INIT_MESSAGE: 116 | console.log("INIT_MESSAGE"); 117 | return true; 118 | break; 119 | case INIT_MESSAGE_SUCCESS: 120 | return Object.assign({}, state, { 121 | messages: action.req.data 122 | }); 123 | break; 124 | case INIT_MESSAGE_FAILURE: 125 | return Object.assign({}, state, { 126 | error: 'Message is Failed.' 127 | }); 128 | break; 129 | case CREATE_WORKSPACE_SUCCESS: 130 | return Object.assign({}, state, { 131 | created_ws: action.req.data 132 | }); 133 | break; 134 | case CREATE_WORKSPACE_FAILURE: 135 | return Object.assign({}, state, { 136 | error: 'Creating the workspace is Failed.' 137 | }); 138 | break; 139 | case INIT_WORKSPACE_SUCCESS: 140 | return Object.assign({}, state, { 141 | workspaces: action.req.data 142 | }); 143 | break; 144 | case INIT_WORKSPACE_FAILURE: 145 | return Object.assign({}, state, { 146 | error: 'Getting Workspace is Failed.' 147 | }) 148 | break; 149 | case FIND_WORKSPACE_SUCCESS: 150 | // return Object.assign({}, state, { 151 | 152 | // }) 153 | console.log('find_workspace is success.'); 154 | break; 155 | case FIND_WORKSPACE_FAILURE: 156 | return Object.assign({}, state, { 157 | error: "Finding the Workspace is Failed." 158 | }) 159 | break; 160 | case LOGOUT: 161 | sessionStorage.clear(); 162 | return Object.assign({}, state, { 163 | info: null, 164 | token: null, 165 | userId: null, 166 | clearCookie: true, 167 | sign: false, 168 | logged: false 169 | }); 170 | break; 171 | default: 172 | return state; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/common/components/WorkSpace.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { bindActionCreators } from 'redux'; 3 | import {connect} from 'react-redux'; 4 | import injectTapEventPlugin from "react-tap-event-plugin"; 5 | import Helmet from 'react-helmet' 6 | import { Grid, Row, Col } from 'react-bootstrap'; 7 | import classNames from 'classnames'; 8 | import {Field, reduxForm} from 'redux-form'; 9 | import {Tabs, Tab} from 'material-ui/lib/tabs'; 10 | import { 11 | Table, 12 | TableBody, 13 | TableHeader, 14 | TableHeaderColumn, 15 | TableRow, 16 | TableRowColumn, 17 | } from 'material-ui/lib/Table'; 18 | import TextField from 'material-ui/lib/text-field'; 19 | import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator/lib'; 20 | import RaisedButton from 'material-ui/lib/raised-button'; 21 | 22 | import * as UserActions from '../actions/user'; 23 | injectTapEventPlugin(); 24 | 25 | require('./style/home.css'); 26 | 27 | const styles = { 28 | headline: { 29 | fontSize: 24, 30 | paddingTop: 16, 31 | marginBottom: 12, 32 | fontWeight: 400, 33 | }, 34 | }; 35 | 36 | class WorkSpace extends Component { 37 | 38 | constructor(props) { 39 | super(props); 40 | this.onSubmit = this.onSubmit.bind(this); 41 | this.onFind_Workspace = this.onFind_Workspace.bind(this); 42 | this.handleSubmit = this.handleSubmit.bind(this); 43 | this.validationhandleChange = this.validationhandleChange.bind(this); 44 | this.state = { 45 | ws_list: [], 46 | value: 'list', 47 | selected: [1], 48 | }; 49 | } 50 | 51 | componentWillMount() { 52 | this.props.init_workspaces(); 53 | }; 54 | 55 | componentWillReceiveProps(nextProps) { 56 | this.setState({ 57 | ws_list: this.props.user.workspaces 58 | }) 59 | } 60 | 61 | onSubmit(event) { 62 | event.preventDefault(); 63 | const password = this.refs.password.getValue(); 64 | const confirm_pass = this.refs.confirmPass.getValue(); 65 | var workspace = { 66 | fullName : this.refs.fullName.getValue(), 67 | displayName: this.refs.displayName.getValue(), 68 | email: this.refs.email.getValue(), 69 | password: this.refs.password.getValue(), 70 | } 71 | if (password == confirm_pass) { 72 | this.props.create_workspace(workspace); 73 | } 74 | } 75 | 76 | handleChange = (value) => { 77 | if (value == 'list' || value == 'create') { 78 | this.setState({ 79 | value: value, 80 | }); 81 | } 82 | }; 83 | 84 | onFind_Workspace(event) { 85 | event.preventDefault(); 86 | const f_email = this.refs.find_email.getValue(); 87 | this.props.find_workspace(f_email); 88 | } 89 | 90 | handleSubmit(event) { 91 | console.log('sfdsdfsdf') 92 | event.preventDefault(); 93 | } 94 | 95 | validationhandleChange(event) { 96 | const email = event.target.value; 97 | this.setState({ email }); 98 | } 99 | 100 | isSelected = (index) => { 101 | return this.state.selected.indexOf(index) !== -1; 102 | }; 103 | 104 | handleRowSelection = (selectedRows) => { 105 | this.setState({ 106 | selected: selectedRows, 107 | }); 108 | var params; 109 | for (var i = 0; i < this.state.ws_list.length; i++) { 110 | if (i == selectedRows[0]) { 111 | params = '?workspaceId=' + this.state.ws_list[i].name; 112 | } 113 | } 114 | this.props.history.push({ 115 | pathname: '/login', 116 | search: params, 117 | }) 118 | }; 119 | 120 | render() { 121 | const { email } = this.state; 122 | return ( 123 | 127 | 128 |
129 |

Workspace List

130 | 131 | 132 | 133 | Name 134 | URL 135 | 136 | 137 | 138 | {this.state.ws_list.map(function(wk, i) { 139 | return ( 140 | 141 | {wk.fullName} 142 | localhost:3002/home 143 | 144 | ) 145 | })} 146 | 147 |
148 |
149 |
150 |

Find Workspace

151 | 156 | 157 |
158 | console.log(errors)} 162 | > 163 | 171 | 172 | 173 |
174 | 175 |
176 |

Create Workspace

177 |
178 | 179 |
185 |
191 |
196 |
203 |
210 |
211 | 212 |
213 | 214 |
215 |
216 |
217 | ); 218 | } 219 | } 220 | 221 | function mapStateToProps(state) { 222 | return { 223 | user : state.user 224 | }; 225 | } 226 | 227 | function mapDispatchToProps(dispatch) { 228 | return bindActionCreators(UserActions,dispatch); 229 | } 230 | 231 | export default connect(mapStateToProps, mapDispatchToProps)(WorkSpace); --------------------------------------------------------------------------------