├── 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 |
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 |
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 |
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 |
25 | Home [static]
26 | Reddit [api]
27 | Todo [stateful]
28 | Counter [stateful]
29 | About [static]
30 | Login [static]
31 | {`Currently version ${version}`}
32 |
33 |
34 |
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 |
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 |
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 |
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 |
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 | Send
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 |
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 |
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);
--------------------------------------------------------------------------------