From 8eca499eec57e2e207f2a7ebfb1cb3b7882ad571 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 9 Sep 2016 23:35:12 +0300 Subject: [PATCH 01/18] mobx todolist --- frontend/src/pages/Test.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/pages/Test.jsx b/frontend/src/pages/Test.jsx index 81b9edd..92c2ef3 100644 --- a/frontend/src/pages/Test.jsx +++ b/frontend/src/pages/Test.jsx @@ -126,7 +126,6 @@ export default class Test extends Component { /> todoList.removeTodo(item)} toogle={todoList.toogleTodo} /> From 294644eff98c53d9598424fd687935b69463bdf0 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 10 Sep 2016 01:45:54 +0300 Subject: [PATCH 02/18] clean up --- .../src/components/AddComentForm/index.jsx | 39 +++++ .../{Projects => AddProjectModal}/index.jsx | 54 +------ .../src/components/CommentsList/index.jsx | 29 ++++ .../src/components/DashboardMenu/index.jsx | 27 ++++ frontend/src/components/Layouts/App.jsx | 9 ++ .../{pages => components/Layouts}/Login.jsx | 7 +- frontend/src/components/Layouts/Main.jsx | 17 +++ frontend/src/components/Layouts/index.css | 0 .../components/{Login => LoginForm}/index.css | 0 .../components/{Login => LoginForm}/index.jsx | 31 +--- .../src/components/ProjectsList/index.jsx | 52 +++++++ .../src/components/RegisterForm/index.css | 8 ++ .../src/components/RegisterForm/index.jsx | 34 +++++ frontend/src/components/TaskInfo/index.jsx | 22 +++ frontend/src/components/Tasks/index.jsx | 128 ----------------- .../components/{Menu => TasksMenu}/index.jsx | 23 +-- frontend/src/components/Users/index.jsx | 39 ----- frontend/src/components/UsersTable/index.jsx | 42 ++++++ .../AuthenticatedComponent.jsx} | 10 +- .../containers/Application/DashboardMenu.jsx | 13 ++ .../containers/Application/TasksLayout.jsx | 15 ++ .../src/containers/Application/TasksMenu.jsx | 15 ++ frontend/src/containers/Login.jsx | 21 --- frontend/src/containers/Login/LoginForm.jsx | 13 ++ .../src/containers/Login/RegisterForm.jsx | 12 ++ frontend/src/containers/Login/index.jsx | 14 ++ frontend/src/containers/NoMatch/index.jsx | 15 ++ frontend/src/containers/ProjectsList.jsx | 38 ----- .../ProjectsList/AddProjectModal.jsx | 17 +++ .../containers/ProjectsList/ProjectsList.jsx | 16 +++ .../src/containers/ProjectsList/index.jsx | 21 +++ .../TaskAdd/index.jsx} | 0 .../containers/TaskDetails/AddComentForm.jsx | 14 ++ .../containers/TaskDetails/CommentsList.jsx | 15 ++ .../src/containers/TaskDetails/TaskInfo.jsx | 17 +++ .../TaskDetails/index.jsx} | 12 +- .../src/containers/TasksList/TaskTable.jsx | 12 ++ .../TasksList/index.jsx} | 5 +- .../{Users.jsx => UsersList/UsersTable.jsx} | 11 +- frontend/src/containers/UsersList/index.jsx | 15 ++ frontend/src/containers/tasks.jsx | 39 ----- frontend/src/pages/.DS_Store | Bin 6148 -> 0 bytes frontend/src/pages/NoMatch.jsx | 20 --- frontend/src/pages/ProjectsList.jsx | 25 ---- frontend/src/pages/TasksLayout/index.jsx | 36 ----- frontend/src/pages/Test.jsx | 136 ------------------ frontend/src/pages/UsersList.jsx | 24 ---- frontend/src/pages/index.css | 4 - frontend/src/reduxApp/modules/comments.jsx | 54 +++++++ frontend/src/reduxApp/modules/reducer.jsx | 2 + frontend/src/reduxApp/modules/tasks.jsx | 44 +----- frontend/src/routes.jsx | 62 ++++---- test/README.md | 10 -- 53 files changed, 613 insertions(+), 725 deletions(-) create mode 100644 frontend/src/components/AddComentForm/index.jsx rename frontend/src/components/{Projects => AddProjectModal}/index.jsx (52%) create mode 100644 frontend/src/components/CommentsList/index.jsx create mode 100644 frontend/src/components/DashboardMenu/index.jsx create mode 100644 frontend/src/components/Layouts/App.jsx rename frontend/src/{pages => components/Layouts}/Login.jsx (58%) create mode 100644 frontend/src/components/Layouts/Main.jsx create mode 100644 frontend/src/components/Layouts/index.css rename frontend/src/components/{Login => LoginForm}/index.css (100%) rename frontend/src/components/{Login => LoginForm}/index.jsx (50%) create mode 100644 frontend/src/components/ProjectsList/index.jsx create mode 100644 frontend/src/components/RegisterForm/index.css create mode 100644 frontend/src/components/RegisterForm/index.jsx create mode 100644 frontend/src/components/TaskInfo/index.jsx delete mode 100644 frontend/src/components/Tasks/index.jsx rename frontend/src/components/{Menu => TasksMenu}/index.jsx (60%) delete mode 100644 frontend/src/components/Users/index.jsx create mode 100644 frontend/src/components/UsersTable/index.jsx rename frontend/src/containers/{Auth.jsx => Application/AuthenticatedComponent.jsx} (80%) create mode 100644 frontend/src/containers/Application/DashboardMenu.jsx create mode 100644 frontend/src/containers/Application/TasksLayout.jsx create mode 100644 frontend/src/containers/Application/TasksMenu.jsx delete mode 100644 frontend/src/containers/Login.jsx create mode 100644 frontend/src/containers/Login/LoginForm.jsx create mode 100644 frontend/src/containers/Login/RegisterForm.jsx create mode 100644 frontend/src/containers/Login/index.jsx create mode 100644 frontend/src/containers/NoMatch/index.jsx delete mode 100644 frontend/src/containers/ProjectsList.jsx create mode 100644 frontend/src/containers/ProjectsList/AddProjectModal.jsx create mode 100644 frontend/src/containers/ProjectsList/ProjectsList.jsx create mode 100644 frontend/src/containers/ProjectsList/index.jsx rename frontend/src/{pages/TasksLayout/TaskAdd.jsx => containers/TaskAdd/index.jsx} (100%) create mode 100644 frontend/src/containers/TaskDetails/AddComentForm.jsx create mode 100644 frontend/src/containers/TaskDetails/CommentsList.jsx create mode 100644 frontend/src/containers/TaskDetails/TaskInfo.jsx rename frontend/src/{pages/TasksLayout/TaskDetails.jsx => containers/TaskDetails/index.jsx} (50%) create mode 100644 frontend/src/containers/TasksList/TaskTable.jsx rename frontend/src/{pages/TasksLayout/TaskList.jsx => containers/TasksList/index.jsx} (70%) rename frontend/src/containers/{Users.jsx => UsersList/UsersTable.jsx} (56%) create mode 100644 frontend/src/containers/UsersList/index.jsx delete mode 100644 frontend/src/containers/tasks.jsx delete mode 100644 frontend/src/pages/.DS_Store delete mode 100644 frontend/src/pages/NoMatch.jsx delete mode 100644 frontend/src/pages/ProjectsList.jsx delete mode 100644 frontend/src/pages/TasksLayout/index.jsx delete mode 100644 frontend/src/pages/Test.jsx delete mode 100644 frontend/src/pages/UsersList.jsx delete mode 100644 frontend/src/pages/index.css create mode 100644 frontend/src/reduxApp/modules/comments.jsx delete mode 100644 test/README.md diff --git a/frontend/src/components/AddComentForm/index.jsx b/frontend/src/components/AddComentForm/index.jsx new file mode 100644 index 0000000..6e2df98 --- /dev/null +++ b/frontend/src/components/AddComentForm/index.jsx @@ -0,0 +1,39 @@ +import React, { Component, PropTypes } from 'react'; +import { Button, Input } from 'react-bootstrap'; + +class AddComentForm extends Component { + + state = { value: '' } + + static propTypes = { + addComment: PropTypes.func, + } + + onChange = (e) => { + this.setState({ value: e.target.value }); + } + + addComment = () => { + this.props.addComment(this.state.value); + this.setState({ value: '' }); + } + + render() { + return ( +
+
+ + +
+
+ ); + } +} + + +export default AddComentForm; diff --git a/frontend/src/components/Projects/index.jsx b/frontend/src/components/AddProjectModal/index.jsx similarity index 52% rename from frontend/src/components/Projects/index.jsx rename to frontend/src/components/AddProjectModal/index.jsx index f22813d..98ad260 100644 --- a/frontend/src/components/Projects/index.jsx +++ b/frontend/src/components/AddProjectModal/index.jsx @@ -3,56 +3,11 @@ import { Button, Label, ListGroupItem, ListGroup, Modal, Input } from 'react-boo import { Link } from 'react-router'; import Select from 'react-select'; -const ProductItem = ({ product, removeProject }) => { - const users = (product.users || []) - .map(user =>  ); - return ( - -
-
- {product.title} -
-
- -
-
- {users}  -
-
-
- ); -}; - -const ProjectsList = ({ openPopup, projects, removeProject }) => ( - - -
-
-
My projects
-
-
- -
- - {projects.map(product => ( - ) - )} - -
-); import 'react-select/dist/react-select.css'; const LinkedStateMixin = require('react-addons-linked-state-mixin'); -const ProjectPopup = React.createClass({ +const AddProjectModal = React.createClass({ mixins: [LinkedStateMixin], getInitialState() { return { @@ -106,8 +61,5 @@ const ProjectPopup = React.createClass({ }, }); -export { - ProductItem, - ProjectsList, - ProjectPopup, -}; +export default AddProjectModal; + diff --git a/frontend/src/components/CommentsList/index.jsx b/frontend/src/components/CommentsList/index.jsx new file mode 100644 index 0000000..f0ab8b8 --- /dev/null +++ b/frontend/src/components/CommentsList/index.jsx @@ -0,0 +1,29 @@ +import React, { PropTypes } from 'react'; +import { Button } from 'react-bootstrap'; + +const CommentsList = ({ comments, removeComent }) => ( +
+ {comments.map(comment => ( +
+

+ {comment.userName} + +

+ +

{comment.text}

+
+
+ ))} +
+); + +CommentsList.propTypes = { + comments: PropTypes.array, + removeComent: PropTypes.func, +}; + +export default CommentsList; diff --git a/frontend/src/components/DashboardMenu/index.jsx b/frontend/src/components/DashboardMenu/index.jsx new file mode 100644 index 0000000..a996e14 --- /dev/null +++ b/frontend/src/components/DashboardMenu/index.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Link } from 'react-router'; +import { Navbar, NavBrand, Nav, NavItem } from 'react-bootstrap'; + +import { LinkContainer } from 'react-router-bootstrap'; + +const DashboardMenu = ({ + logout, + addProject, +}) => ( + + + Task-tracker + + + + +); + +export default DashboardMenu; diff --git a/frontend/src/components/Layouts/App.jsx b/frontend/src/components/Layouts/App.jsx new file mode 100644 index 0000000..a0936f5 --- /dev/null +++ b/frontend/src/components/Layouts/App.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +const App = ({ children }) => ( +
+ {children} +
+); + +export default App; diff --git a/frontend/src/pages/Login.jsx b/frontend/src/components/Layouts/Login.jsx similarity index 58% rename from frontend/src/pages/Login.jsx rename to frontend/src/components/Layouts/Login.jsx index ad13b81..624fc53 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/components/Layouts/Login.jsx @@ -1,15 +1,14 @@ import React from 'react'; import { Grid, Row, Col } from 'react-bootstrap'; -import { LoginFormContainer, RegisterFormContainer } from 'containers/Login'; -const Login = () => ( +const Login = ({ left, right }) => ( - + {left} - + {right} diff --git a/frontend/src/components/Layouts/Main.jsx b/frontend/src/components/Layouts/Main.jsx new file mode 100644 index 0000000..f3511eb --- /dev/null +++ b/frontend/src/components/Layouts/Main.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Grid, Col, Row } from 'react-bootstrap'; + +const Main = ({ header, children }) => ( +
+ {header} + + + + {children} + + + +
+); + +export default Main; diff --git a/frontend/src/components/Layouts/index.css b/frontend/src/components/Layouts/index.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/components/Login/index.css b/frontend/src/components/LoginForm/index.css similarity index 100% rename from frontend/src/components/Login/index.css rename to frontend/src/components/LoginForm/index.css diff --git a/frontend/src/components/Login/index.jsx b/frontend/src/components/LoginForm/index.jsx similarity index 50% rename from frontend/src/components/Login/index.jsx rename to frontend/src/components/LoginForm/index.jsx index dc416a5..a1f15ff 100644 --- a/frontend/src/components/Login/index.jsx +++ b/frontend/src/components/LoginForm/index.jsx @@ -1,4 +1,4 @@ -import React, { Component, PropTypes } from 'react'; +import React, { PropTypes } from 'react'; import { Button, Input } from 'react-bootstrap'; import './index.css'; @@ -36,33 +36,6 @@ LoginForm.propTypes = { login: PropTypes.func, }; -class RegisterForm extends Component { - static propTypes = { - registr: PropTypes.func, - } - - regist = () => { - this.props.registr({ - email: this.refs.email.getValue(), - password: this.refs.password.getValue(), - name: this.refs.name.getValue(), - }); - } - - render() { - return ( -
- - - - -
- ); - } -} - - export { - RegisterForm, - LoginForm, + LoginForm, }; diff --git a/frontend/src/components/ProjectsList/index.jsx b/frontend/src/components/ProjectsList/index.jsx new file mode 100644 index 0000000..372d240 --- /dev/null +++ b/frontend/src/components/ProjectsList/index.jsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { Button, Label, ListGroupItem, ListGroup } from 'react-bootstrap'; +import { Link } from 'react-router'; + +const ProductItem = ({ product, removeProject }) => { + const users = (product.users || []) + .map(user =>  ); + + return ( + +
+
+ {product.title} +
+
+ +
+
+ {users}  +
+
+
+ ); +}; + +const ProjectsList = ({ openPopup, projects, removeProject }) => ( + + +
+
+
My projects
+
+
+ +
+ + {projects.map(product => ( + ) + )} + +
+); + +export default ProjectsList; diff --git a/frontend/src/components/RegisterForm/index.css b/frontend/src/components/RegisterForm/index.css new file mode 100644 index 0000000..1d79e14 --- /dev/null +++ b/frontend/src/components/RegisterForm/index.css @@ -0,0 +1,8 @@ + +.register-form { + padding: 10px; + border: 1px solid #ddd; + border-top: none; + border-radius: 5px; + border-radius: 0 0 5px 5px; +} diff --git a/frontend/src/components/RegisterForm/index.jsx b/frontend/src/components/RegisterForm/index.jsx new file mode 100644 index 0000000..e449f7f --- /dev/null +++ b/frontend/src/components/RegisterForm/index.jsx @@ -0,0 +1,34 @@ +import React, { Component, PropTypes } from 'react'; + +import { Button, Input } from 'react-bootstrap'; +import './index.css'; + +class RegisterForm extends Component { + static propTypes = { + registr: PropTypes.func, + } + + regist = () => { + this.props.registr({ + email: this.refs.email.getValue(), + password: this.refs.password.getValue(), + name: this.refs.name.getValue(), + }); + } + + render() { + return ( +
+ + + + +
+ ); + } +} + + +export { + RegisterForm, +}; diff --git a/frontend/src/components/TaskInfo/index.jsx b/frontend/src/components/TaskInfo/index.jsx new file mode 100644 index 0000000..f613b49 --- /dev/null +++ b/frontend/src/components/TaskInfo/index.jsx @@ -0,0 +1,22 @@ +import React, { PropTypes } from 'react'; +import { Button } from 'react-bootstrap'; + +const TaskInfo = ({ task: { title, description }, removeTask }) => ( +
+
+

{title}

+

+ {description} +

+
+ +
+); + +TaskInfo.propTypes = { + task: PropTypes.object, + removeTask: PropTypes.func, +}; + + +export default TaskInfo; diff --git a/frontend/src/components/Tasks/index.jsx b/frontend/src/components/Tasks/index.jsx deleted file mode 100644 index 53c725f..0000000 --- a/frontend/src/components/Tasks/index.jsx +++ /dev/null @@ -1,128 +0,0 @@ -import React, { Component, PropTypes } from 'react'; - -import { Button, Input } from 'react-bootstrap'; -import { Link } from 'react-router'; - -import { Table } from 'react-bootstrap'; - -class AddComentForm extends Component { - - state = { value: '' } - - static propTypes = { - addComment: PropTypes.func, - } - - onChange = (e) => { - this.setState({ value: e.target.value }); - } - - addComment = () => { - this.props.addComment(this.state.value); - this.setState({ value: '' }); - } - - render() { - return ( -
-
- - -
-
- ); - } -} - - -const CommentsList = ({ comments, removeComent }) => ( -
- {comments.map(comment => ( -
-

- {comment.userName} - -

- -

{comment.text}

-
-
- ))} -
-); - -CommentsList.propTypes = { - comments: PropTypes.array, - removeComent: PropTypes.func, -}; - - -const TaskInfo = ({ task: { title, description }, removeTask }) => ( -
-
-

{title}

-

- {description} -

-
- -
-); - -TaskInfo.propTypes = { - task: PropTypes.object, - removeTask: PropTypes.func, -}; - - -const TaskTable = ({ tasks, projectId }) => ( - - - - - - - - - - - {tasks.map(task => ( - - - - - - - ))} - -
TitleStatusAssigneeVersion
- {task.title} - - {task.status} - - {task.assigneeName} - - {task.version} -
-); - -TaskTable.propTypes = { - tasks: PropTypes.array, - projectId: PropTypes.string, -}; - -export { - AddComentForm, - CommentsList, - TaskInfo, - TaskTable, -}; diff --git a/frontend/src/components/Menu/index.jsx b/frontend/src/components/TasksMenu/index.jsx similarity index 60% rename from frontend/src/components/Menu/index.jsx rename to frontend/src/components/TasksMenu/index.jsx index e58e45b..e074a36 100644 --- a/frontend/src/components/Menu/index.jsx +++ b/frontend/src/components/TasksMenu/index.jsx @@ -4,7 +4,7 @@ import { Navbar, NavBrand, Nav, NavItem } from 'react-bootstrap'; import { LinkContainer } from 'react-router-bootstrap'; -export const Menu = ({ +const TasksMenu = ({ projectId, logout, }) => ( @@ -28,23 +28,4 @@ export const Menu = ({ ); - -export const DashboardMenu = ({ - logout, - addProject, -}) => ( - - - Task-tracker - - - - -); +export default TasksMenu; diff --git a/frontend/src/components/Users/index.jsx b/frontend/src/components/Users/index.jsx deleted file mode 100644 index a0dfe17..0000000 --- a/frontend/src/components/Users/index.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { Component } from 'react'; -import { Table, Button } from 'react-bootstrap'; - -export class UsersTable extends Component { - render() { - const { users, removeUser, currentUser } = this.props; - return ( -
-

All users

- - - - - - - - - - {users.map(user => ( - - - - - - ))} - -
NameEmail
- {user.name} - - {user.email} - -
- -
-
-
- ); - } -} diff --git a/frontend/src/components/UsersTable/index.jsx b/frontend/src/components/UsersTable/index.jsx new file mode 100644 index 0000000..4ba23b6 --- /dev/null +++ b/frontend/src/components/UsersTable/index.jsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Table, Button } from 'react-bootstrap'; + +const UsersTable = ({ + users, removeUser, currentUser, +}) => ( +
+

All users

+ + + + + + + + + + {users.map(user => ( + + + + + + ))} + +
NameEmail
+ {user.name} + + {user.email} + +
+ +
+
+
+); + + +export default UsersTable; diff --git a/frontend/src/containers/Auth.jsx b/frontend/src/containers/Application/AuthenticatedComponent.jsx similarity index 80% rename from frontend/src/containers/Auth.jsx rename to frontend/src/containers/Application/AuthenticatedComponent.jsx index 3abe969..1650c52 100644 --- a/frontend/src/containers/Auth.jsx +++ b/frontend/src/containers/Application/AuthenticatedComponent.jsx @@ -2,17 +2,9 @@ import React from 'react'; import { connect } from 'react-redux'; //import { pushState } from 'redux-router'; -import { Menu } from 'components/Menu/'; -import { checkAuth, logout } from 'reduxApp/modules/auth'; +import { checkAuth } from 'reduxApp/modules/auth'; -export const MenuContainer = connect( - state => ({ - projectId: state.router.params.projectId, - router: state.router, - }), - { logout } -)(Menu); export function requireAuthentication(Component) { class AuthenticatedComponent extends React.Component { diff --git a/frontend/src/containers/Application/DashboardMenu.jsx b/frontend/src/containers/Application/DashboardMenu.jsx new file mode 100644 index 0000000..1984dc6 --- /dev/null +++ b/frontend/src/containers/Application/DashboardMenu.jsx @@ -0,0 +1,13 @@ + +import DashboardMenuComponent from 'components/DashboardMenu/'; +import { connect } from 'react-redux'; + +import { showAddProject } from 'reduxApp/modules/projects'; +import { logout } from 'reduxApp/modules/auth'; + +const DashboardMenu = connect( + null, + { addProject: showAddProject, logout } +)(DashboardMenuComponent); + +export default DashboardMenu; diff --git a/frontend/src/containers/Application/TasksLayout.jsx b/frontend/src/containers/Application/TasksLayout.jsx new file mode 100644 index 0000000..136cd2e --- /dev/null +++ b/frontend/src/containers/Application/TasksLayout.jsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import TasksMenu from 'containers/Application/TasksMenu'; +import Main from 'components/Layouts/Main'; + +const TasksLayout = ({ children }) => ( +
} + > + {children} +
+); + + +export default TasksLayout; diff --git a/frontend/src/containers/Application/TasksMenu.jsx b/frontend/src/containers/Application/TasksMenu.jsx new file mode 100644 index 0000000..88f78a1 --- /dev/null +++ b/frontend/src/containers/Application/TasksMenu.jsx @@ -0,0 +1,15 @@ +import { connect } from 'react-redux'; +import { logout } from 'reduxApp/modules/auth'; +import Menu from 'components/TasksMenu/'; + + +const TasksMenu = connect( + state => ({ + projectId: state.router.params.projectId, + router: state.router, + }), + { logout } +)(Menu); + + +export default TasksMenu; diff --git a/frontend/src/containers/Login.jsx b/frontend/src/containers/Login.jsx deleted file mode 100644 index 8088605..0000000 --- a/frontend/src/containers/Login.jsx +++ /dev/null @@ -1,21 +0,0 @@ - -import { connect } from 'react-redux'; -import { login, registr } from 'reduxApp/modules/auth'; - -import { LoginForm, RegisterForm } from 'components/Login/'; - -const LoginFormContainer = connect( - null, - { login } -)(LoginForm); - - -const RegisterFormContainer = connect( - null, - { registr } -)(RegisterForm); - -export { - LoginFormContainer, - RegisterFormContainer, -}; diff --git a/frontend/src/containers/Login/LoginForm.jsx b/frontend/src/containers/Login/LoginForm.jsx new file mode 100644 index 0000000..9ba230a --- /dev/null +++ b/frontend/src/containers/Login/LoginForm.jsx @@ -0,0 +1,13 @@ + +import { connect } from 'react-redux'; +import { login } from 'reduxApp/modules/auth'; + +import { LoginForm as LoginFormComponent } from 'components/LoginForm/'; + +const LoginForm = connect( + null, + { login } +)(LoginFormComponent); + + +export default LoginForm; diff --git a/frontend/src/containers/Login/RegisterForm.jsx b/frontend/src/containers/Login/RegisterForm.jsx new file mode 100644 index 0000000..ab21172 --- /dev/null +++ b/frontend/src/containers/Login/RegisterForm.jsx @@ -0,0 +1,12 @@ + +import { connect } from 'react-redux'; +import { registr } from 'reduxApp/modules/auth'; + +import { RegisterForm as RegisterFormComponent } from 'components/RegisterForm/'; + +const RegisterForm = connect( + null, + { registr } +)(RegisterFormComponent); + +export default RegisterForm; diff --git a/frontend/src/containers/Login/index.jsx b/frontend/src/containers/Login/index.jsx new file mode 100644 index 0000000..feaba6b --- /dev/null +++ b/frontend/src/containers/Login/index.jsx @@ -0,0 +1,14 @@ +import React from 'react'; + +import LoginLayout from 'components/Layouts/Login'; +import LoginForm from './LoginForm'; +import RegisterForm from './RegisterForm'; + +const Login = () => ( + } + right={} + /> +); + +export default Login; diff --git a/frontend/src/containers/NoMatch/index.jsx b/frontend/src/containers/NoMatch/index.jsx new file mode 100644 index 0000000..478e6bc --- /dev/null +++ b/frontend/src/containers/NoMatch/index.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { Link } from 'react-router'; + +import Main from 'components/Layouts/Main'; + +const NoMatch = () => ( +
+

404

+
+ back to app +
+
+); + +export default NoMatch; diff --git a/frontend/src/containers/ProjectsList.jsx b/frontend/src/containers/ProjectsList.jsx deleted file mode 100644 index 09d0dd3..0000000 --- a/frontend/src/containers/ProjectsList.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { ProjectsList, ProjectPopup } from 'components/Projects/'; -import { DashboardMenu } from 'components/Menu/'; - -import { - fetchProducts, removeProject, - showAddProject, - openPopup, closePopup, addProject, fetchUsers, -} from 'reduxApp/modules/projects'; -import { logout } from 'reduxApp/modules/auth'; - - -const DashboardMenuContainer = connect( - null, - { addProject: showAddProject, logout } -)(DashboardMenu); - -const ProjectsListContainer = connect( - state => ({ - projects: state.projects.projects, - }), - { fetchProducts, removeProject, openPopup, fetchUsers } -)(ProjectsList); - -const ProjectPopupContainer = connect( - state => ({ - show: state.projects.popupOpen, - users: state.users.users, - }), - { onHide: closePopup, addProject } -)(ProjectPopup); - -export { - DashboardMenuContainer, - ProjectsListContainer, - ProjectPopupContainer, -}; diff --git a/frontend/src/containers/ProjectsList/AddProjectModal.jsx b/frontend/src/containers/ProjectsList/AddProjectModal.jsx new file mode 100644 index 0000000..a15c3f5 --- /dev/null +++ b/frontend/src/containers/ProjectsList/AddProjectModal.jsx @@ -0,0 +1,17 @@ +import { connect } from 'react-redux'; +import AddProjectModalComponent from 'components/AddProjectModal/'; + +import { + closePopup, addProject, +} from 'reduxApp/modules/projects'; + + +const AddProjectModal = connect( + state => ({ + show: state.projects.popupOpen, + users: state.users.users, + }), + { onHide: closePopup, addProject } +)(AddProjectModalComponent); + +export default AddProjectModal; diff --git a/frontend/src/containers/ProjectsList/ProjectsList.jsx b/frontend/src/containers/ProjectsList/ProjectsList.jsx new file mode 100644 index 0000000..271e059 --- /dev/null +++ b/frontend/src/containers/ProjectsList/ProjectsList.jsx @@ -0,0 +1,16 @@ +import ProjectsListComponent from 'components/ProjectsList/'; +import { connect } from 'react-redux'; + +import { + fetchProducts, removeProject, + openPopup, fetchUsers, +} from 'reduxApp/modules/projects'; + +const ProjectsList = connect( + state => ({ + projects: state.projects.projects, + }), + { fetchProducts, removeProject, openPopup, fetchUsers } +)(ProjectsListComponent); + +export default ProjectsList; diff --git a/frontend/src/containers/ProjectsList/index.jsx b/frontend/src/containers/ProjectsList/index.jsx new file mode 100644 index 0000000..9c81426 --- /dev/null +++ b/frontend/src/containers/ProjectsList/index.jsx @@ -0,0 +1,21 @@ +import React from 'react'; + +import ProjectsList from './ProjectsList'; +import AddProjectModal from './AddProjectModal'; + +import DashboardMenu from 'containers/Application/DashboardMenu'; +import Main from 'components/Layouts/Main'; + +const ProjectsListPage = () => ( +
} + > + + +
+); + +import need from 'utils/need'; +import { loadProducts } from 'reduxApp/modules/projects'; + +export default need(loadProducts)(ProjectsListPage); diff --git a/frontend/src/pages/TasksLayout/TaskAdd.jsx b/frontend/src/containers/TaskAdd/index.jsx similarity index 100% rename from frontend/src/pages/TasksLayout/TaskAdd.jsx rename to frontend/src/containers/TaskAdd/index.jsx diff --git a/frontend/src/containers/TaskDetails/AddComentForm.jsx b/frontend/src/containers/TaskDetails/AddComentForm.jsx new file mode 100644 index 0000000..9a5194d --- /dev/null +++ b/frontend/src/containers/TaskDetails/AddComentForm.jsx @@ -0,0 +1,14 @@ + +import { connect } from 'react-redux'; +import { addComment } from 'reduxApp/modules/comments'; + +import AddComentFormComponent from 'components/AddComentForm/'; + + +const AddComentForm = connect( + null, + { addComment } +)(AddComentFormComponent); + + +export default AddComentForm; diff --git a/frontend/src/containers/TaskDetails/CommentsList.jsx b/frontend/src/containers/TaskDetails/CommentsList.jsx new file mode 100644 index 0000000..57267c9 --- /dev/null +++ b/frontend/src/containers/TaskDetails/CommentsList.jsx @@ -0,0 +1,15 @@ + +import { connect } from 'react-redux'; +import { removeComent } from 'reduxApp/modules/comments'; + +import CommentsListComponent from 'components/CommentsList/'; + +const CommentsList = connect( + state => ({ + comments: state.comments.comments, + }), + { removeComent } +)(CommentsListComponent); + + +export default CommentsList; diff --git a/frontend/src/containers/TaskDetails/TaskInfo.jsx b/frontend/src/containers/TaskDetails/TaskInfo.jsx new file mode 100644 index 0000000..0258a9f --- /dev/null +++ b/frontend/src/containers/TaskDetails/TaskInfo.jsx @@ -0,0 +1,17 @@ + +import { connect } from 'react-redux'; +import { removeTask } from 'reduxApp/modules/tasks'; + +import TaskInfoComponent from 'components/TaskInfo/'; + + +const TaskInfo = connect( + state => ({ + task: state.tasks.task, + }), + { removeTask } +)(TaskInfoComponent); + + +export default TaskInfo; + diff --git a/frontend/src/pages/TasksLayout/TaskDetails.jsx b/frontend/src/containers/TaskDetails/index.jsx similarity index 50% rename from frontend/src/pages/TasksLayout/TaskDetails.jsx rename to frontend/src/containers/TaskDetails/index.jsx index 2af9927..041b09f 100644 --- a/frontend/src/pages/TasksLayout/TaskDetails.jsx +++ b/frontend/src/containers/TaskDetails/index.jsx @@ -1,14 +1,14 @@ import React from 'react'; - -import { TaskInfoContainer, CommentsListContainer, AddComentFormContainer } from 'containers/tasks'; - +import TaskInfo from './TaskInfo'; +import AddComentForm from './AddComentForm'; +import CommentsList from './CommentsList'; const TaskDetails = () => (
- - - + + +
); diff --git a/frontend/src/containers/TasksList/TaskTable.jsx b/frontend/src/containers/TasksList/TaskTable.jsx new file mode 100644 index 0000000..07fadda --- /dev/null +++ b/frontend/src/containers/TasksList/TaskTable.jsx @@ -0,0 +1,12 @@ +import { connect } from 'react-redux'; + +import { TaskTable as TaskTableComponent } from 'components/Tasks/'; + +const TaskTable = connect( + state => ({ + tasks: state.tasks.tasks, + projectId: state.router.params.projectId, + }) +)(TaskTableComponent); + +export default TaskTable; diff --git a/frontend/src/pages/TasksLayout/TaskList.jsx b/frontend/src/containers/TasksList/index.jsx similarity index 70% rename from frontend/src/pages/TasksLayout/TaskList.jsx rename to frontend/src/containers/TasksList/index.jsx index 265c675..1d33e15 100644 --- a/frontend/src/pages/TasksLayout/TaskList.jsx +++ b/frontend/src/containers/TasksList/index.jsx @@ -1,10 +1,11 @@ + import React from 'react'; -import { TaskTableContainer } from 'containers/tasks'; +import TaskTable from './TaskTable'; const TaskList = () => (
- +
); diff --git a/frontend/src/containers/Users.jsx b/frontend/src/containers/UsersList/UsersTable.jsx similarity index 56% rename from frontend/src/containers/Users.jsx rename to frontend/src/containers/UsersList/UsersTable.jsx index c26d8b2..e91fbe6 100644 --- a/frontend/src/containers/Users.jsx +++ b/frontend/src/containers/UsersList/UsersTable.jsx @@ -1,16 +1,13 @@ -import React from 'react'; import { connect } from 'react-redux'; -import { UsersTable } from 'components/Users/'; +import UsersTableComponent from 'components/UsersTable/'; import { removeUser } from 'reduxApp/modules/users'; -const UsersTableContainer = connect( +const UsersTable = connect( state => ({ users: state.users.users, currentUser: state.auth.user, }), { removeUser } -)(UsersTable); +)(UsersTableComponent); -export { - UsersTableContainer, -}; +export default UsersTable; diff --git a/frontend/src/containers/UsersList/index.jsx b/frontend/src/containers/UsersList/index.jsx new file mode 100644 index 0000000..f873468 --- /dev/null +++ b/frontend/src/containers/UsersList/index.jsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import UsersTable from './UsersTable'; +import Main from 'components/Layouts/Main'; +import DashboardMenu from 'containers/Application/DashboardMenu'; + +const UsersList = () => ( +
}> + +
+); + +import need from 'utils/need'; +import { fetchUsers } from 'reduxApp/modules/users'; +export default need(fetchUsers)(UsersList); diff --git a/frontend/src/containers/tasks.jsx b/frontend/src/containers/tasks.jsx deleted file mode 100644 index 70cf221..0000000 --- a/frontend/src/containers/tasks.jsx +++ /dev/null @@ -1,39 +0,0 @@ - -import { connect } from 'react-redux'; -import { removeTask, addComment, removeComent } from 'reduxApp/modules/tasks'; - -import { AddComentForm, CommentsList, TaskInfo, TaskTable } from 'components/Tasks/'; - - -const TaskInfoContainer = connect( - state => ({ - task: state.tasks.task, - }), - { removeTask } -)(TaskInfo); - -const CommentsListContainer = connect( - state => ({ - comments: state.tasks.comments, - }), - { removeComent } -)(CommentsList); - -const AddComentFormContainer = connect( - null, - { addComment } -)(AddComentForm); - -const TaskTableContainer = connect( - state => ({ - tasks: state.tasks.tasks, - projectId: state.router.params.projectId, - }) -)(TaskTable); - -export { - TaskInfoContainer, - CommentsListContainer, - AddComentFormContainer, - TaskTableContainer, -}; diff --git a/frontend/src/pages/.DS_Store b/frontend/src/pages/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 ( -
- - - -

404

-
- back to app -
- -
-
-
-); - -export default NoMatch; diff --git a/frontend/src/pages/ProjectsList.jsx b/frontend/src/pages/ProjectsList.jsx deleted file mode 100644 index be91822..0000000 --- a/frontend/src/pages/ProjectsList.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { - ProjectsListContainer, ProjectPopupContainer, DashboardMenuContainer, -} from 'containers/ProjectsList'; - -import { Grid, Row, Col } from 'react-bootstrap'; - -const ProjectsListPage = () => ( -
- - - - - - - - - -
-); - -import need from 'utils/need'; -import { loadProducts } from 'reduxApp/modules/projects'; - -export default need(loadProducts)(ProjectsListPage); diff --git a/frontend/src/pages/TasksLayout/index.jsx b/frontend/src/pages/TasksLayout/index.jsx deleted file mode 100644 index e546fc1..0000000 --- a/frontend/src/pages/TasksLayout/index.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { MenuContainer } from 'containers/Auth'; -import { Grid, Col, Row } from 'react-bootstrap'; - -import TaskAdd from './TaskAdd'; -import TaskDetails from './TaskDetails'; -import TaskList from './TaskList'; - -const Layout = ({ header, main }) => ( -
- {header} - - - - {main} - - - -
-); - - -const TasksLayout = ({ children }) => ( - } - main={children} - /> -); - - -export { - TasksLayout, - TaskAdd, - TaskDetails, - TaskList, -}; diff --git a/frontend/src/pages/Test.jsx b/frontend/src/pages/Test.jsx deleted file mode 100644 index 92c2ef3..0000000 --- a/frontend/src/pages/Test.jsx +++ /dev/null @@ -1,136 +0,0 @@ -import React, { Component } from 'react'; - -import { observable, computed, action } from 'mobx'; -import { observer } from "mobx-react"; - -const nextId = () => (new Date).getTime(); - - -class TodoList { - @observable todoList = []; - - @observable filter = 'all'; - - @action - changeFilter(newFilter) { - this.filter = newFilter; - } - - @computed get visableItems() { - const filter = this.filter; - if (filter === 'complieted') return this.todoList.filter(todo => !!todo.complieted); - if (filter === 'notComplieted') return this.todoList.filter(todo => !todo.complieted); - - return this.todoList; - } - - @action - addTodo(title) { - this.todoList.push({ title, id: nextId(), complieted: false }); - } - - @action removeTodo(todo) { - this.todoList.remove(todo); - } - - @action - toogleTodo(todo) { - todo.complieted = !todo.complieted; - } -} - -const todoList = new TodoList(); -todoList.addTodo('test1'); -todoList.addTodo('test2'); -todoList.addTodo('test3'); - -// const todoList = observable([ -// { -// title: 'lear mobx', -// id: 1, -// complieted: false, -// }, -// { -// title: 'write project with mobx', -// id: 2, -// complieted: false, -// }, -// ]); - - -// const addTodo = action((title) => { -// todoList.push({ title: title, id: nextId(), complieted: false }); -// }); - -// const removeTodo = action((todo) => { -// todoList.remove(todo); -// }); - -// const toogleTodo = action((todo) => { -// todo.complieted = !todo.complieted; -// }); - -const ListItem = observer(({ item, remove, toogle }) => { - return ( -
- toogle(item)}>{item.title} - -
- ); -}); - -const List = observer(({ todoList: { visableItems }, remove, toogle }) => { - return ( -
- {visableItems.map(item => ( - - ))} -
- ); -}); - -const AddForm = ({ add }) => { - let input; - - return ( -
- input = node} /> - -
- ); -}; - -const Filter = observer(({ todoList: { filter }, changeFilter }) => { - console.log('filter>', filter) - return ( -
-
changeFilter('all')}>all
-
changeFilter('complieted')}>complieted
-
changeFilter('notComplieted')}>not complited
-
- ); -}); - -import './index.css'; - -export default class Test extends Component { - render() { - return ( -
- todoList.changeFilter(filter)} - /> - todoList.removeTodo(item)} - toogle={todoList.toogleTodo} - /> - todoList.addTodo(title)} /> -
- ); - } -} diff --git a/frontend/src/pages/UsersList.jsx b/frontend/src/pages/UsersList.jsx deleted file mode 100644 index b4f0f5b..0000000 --- a/frontend/src/pages/UsersList.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; - -import { - DashboardMenuContainer, -} from 'containers/ProjectsList'; -import { Grid, Row, Col } from 'react-bootstrap'; -import { UsersTableContainer } from 'containers/Users'; - -const UsersList = () => ( -
- - - - - - - - -
-); - -import need from 'utils/need'; -import { fetchUsers } from 'reduxApp/modules/users'; -export default need(fetchUsers)(UsersList); diff --git a/frontend/src/pages/index.css b/frontend/src/pages/index.css deleted file mode 100644 index dcd996c..0000000 --- a/frontend/src/pages/index.css +++ /dev/null @@ -1,4 +0,0 @@ - -.active { - background: red; -} diff --git a/frontend/src/reduxApp/modules/comments.jsx b/frontend/src/reduxApp/modules/comments.jsx new file mode 100644 index 0000000..18dbb5f --- /dev/null +++ b/frontend/src/reduxApp/modules/comments.jsx @@ -0,0 +1,54 @@ + +const initState = { + comments: [], +}; + +export function reducer(state = initState, action) { + switch (action.type) { + case 'SET_COMMENTS': + return { ...state, comments: action.payload }; + + case 'REMOVE_COMMENTS': { + const comments = state.comments.filter(coment => coment.id !== action.payload.id); + return { ...state, comments }; + } + + default: + return state; + } +} + +import http from 'utils/http'; + +function setComments(payload) { + return { + type: 'SET_COMMENTS', + payload, + }; +} + +export function loadComments(taskId) { + return (dispatch) => http.get(`/api/tasks/${taskId}/comments`) + .then(json => dispatch(setComments(json))); +} + + +export function addComment(text) { + return (dispatch, getState) => { + const { + auth: { user: { name } }, + router: { params: { id } }, + } = getState(); + http.post(`/api/tasks/${id}/comments`, { text, userName: name }) + .then(() => dispatch(loadComments(id))); + }; +} + +export function removeComent(comment) { + return (dispatch, getState) => { + const { router: { params: { id } } } = getState(); + return http.del(`/api/tasks/${id}/comments/${comment.id}`) + .then(() => dispatch({ type: 'REMOVE_COMMENTS', payload: comment })); + }; +} + diff --git a/frontend/src/reduxApp/modules/reducer.jsx b/frontend/src/reduxApp/modules/reducer.jsx index 5a5a34a..f24768f 100644 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ b/frontend/src/reduxApp/modules/reducer.jsx @@ -6,8 +6,10 @@ import { reducer as auth } from './auth'; import { reducer as projects } from './projects'; import { reducer as tasks } from './tasks'; import { reducer as users } from './users'; +import { reducer as comments } from './comments'; export default combineReducers({ + comments, users, projects, tasks, diff --git a/frontend/src/reduxApp/modules/tasks.jsx b/frontend/src/reduxApp/modules/tasks.jsx index c7868e9..d01ac32 100644 --- a/frontend/src/reduxApp/modules/tasks.jsx +++ b/frontend/src/reduxApp/modules/tasks.jsx @@ -1,7 +1,6 @@ const initState = { tasks: [], - comments: [], task: null, statuses: ['new', 'inprogress', 'testing', 'complited'], versions: [], @@ -15,13 +14,6 @@ export function reducer(state = initState, action) { case 'SET_TASK': return { ...state, task: action.payload }; - case 'SET_COMMENTS': - return { ...state, comments: action.payload }; - - case 'REMOVE_COMMENTS': { - const comments = state.comments.filter(coment => coment.id !== action.payload.id); - return { ...state, comments }; - } case 'SET_VERSIONS': return { ...state, versions: action.payload }; @@ -33,6 +25,7 @@ export function reducer(state = initState, action) { } } +import { loadComments } from 'reduxApp/modules/comments'; import { fetchUsers } from 'reduxApp/modules/users'; import { push } from 'redux-router'; @@ -52,23 +45,10 @@ function setTask(payload) { }; } -function setComments(payload) { - return { - type: 'SET_COMMENTS', - payload, - }; -} - -export function loadComments(taskId) { - return (dispatch) => { - return http.get(`/api/tasks/${taskId}/comments`).then(json => dispatch(setComments(json))); - }; -} export function loadTasks() { - return (dispatch) => { - return http.get('/api/tasks').then(json => dispatch(setTasks(json))); - }; + return (dispatch) => http.get('/api/tasks') + .then(json => dispatch(setTasks(json))); } @@ -96,24 +76,6 @@ export function loadTask() { }; } -export function addComment(text) { - return (dispatch, getState) => { - const { - auth: { user: { name } }, - router: { params: { id } }, - } = getState(); - http.post(`/api/tasks/${id}/comments`, { text, userName: name }) - .then(() => dispatch(loadComments(id))); - }; -} - -export function removeComent(comment) { - return (dispatch, getState) => { - const { router: { params: { id } } } = getState(); - return http.del(`/api/tasks/${id}/comments/${comment.id}`) - .then(() => dispatch({ type: 'REMOVE_COMMENTS', payload: comment })); - }; -} export function addVersion() { return (dispatch) => { diff --git a/frontend/src/routes.jsx b/frontend/src/routes.jsx index 0917f84..420ec4d 100644 --- a/frontend/src/routes.jsx +++ b/frontend/src/routes.jsx @@ -2,43 +2,35 @@ import React from 'react'; import { Router, Route, IndexRoute } from 'react-router'; -import Login from 'pages/Login'; +import Login from 'containers/Login/'; +import ProjectsList from 'containers/ProjectsList/'; +import { requireAuthentication } from 'containers/Application/AuthenticatedComponent'; +import UsersList from 'containers/UsersList/'; +import NoMatch from 'containers/NoMatch/'; +import TasksLayout from 'containers/Application/TasksLayout'; +import TasksList from 'containers/TasksList/'; +import TaskAdd from 'containers/TaskAdd/'; +import TaskDetails from 'containers/TaskDetails'; -import ProjectsList from 'pages/ProjectsList'; -import { requireAuthentication } from 'containers/Auth'; -import UsersList from 'pages/UsersList'; -import NoMatch from 'pages/NoMatch'; -import { TasksLayout, TaskAdd, TaskDetails, TaskList } from 'pages/TasksLayout'; -import Test from 'pages/Test'; +import App from 'components/Layouts/App'; -const App = ({ children }) => ( +module.exports = ({ dispatch, getState }) => (
- {children} -
-); - - - -module.exports = ({ dispatch, getState }) => { - return ( -
- - - - - - - - - - - + + + + + + + + + - - - + - -
- ); -}; + + + + + +); diff --git a/test/README.md b/test/README.md deleted file mode 100644 index 839ffa9..0000000 --- a/test/README.md +++ /dev/null @@ -1,10 +0,0 @@ - *test* - - - - *test* - -- ещё один элемент ненумерованного списка -- ещё один элемент ненумерованного списка -- ещё один элемент ненумерованного списка -- ещё один элемент ненумерованного списка \ No newline at end of file From 54a70610dcac59788155674604d3d2dfe2c08a7f Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 10 Sep 2016 02:12:12 +0300 Subject: [PATCH 03/18] reafactoring loadind, able to load many actions --- .../Application/loading.jsx} | 18 ++++++++++++------ frontend/src/containers/ProjectsList/index.jsx | 5 +++-- frontend/src/containers/TaskAdd/index.jsx | 4 ++-- frontend/src/containers/TaskDetails/index.jsx | 5 +++-- frontend/src/containers/TasksList/index.jsx | 4 ++-- frontend/src/containers/UsersList/index.jsx | 4 ++-- frontend/src/reduxApp/modules/comments.jsx | 6 +++--- frontend/src/reduxApp/modules/projects.jsx | 8 +------- frontend/src/reduxApp/modules/tasks.jsx | 12 +++--------- 9 files changed, 31 insertions(+), 35 deletions(-) rename frontend/src/{utils/need.jsx => containers/Application/loading.jsx} (57%) diff --git a/frontend/src/utils/need.jsx b/frontend/src/containers/Application/loading.jsx similarity index 57% rename from frontend/src/utils/need.jsx rename to frontend/src/containers/Application/loading.jsx index 85057f1..5bbd306 100644 --- a/frontend/src/utils/need.jsx +++ b/frontend/src/containers/Application/loading.jsx @@ -4,7 +4,7 @@ import { connect } from 'react-redux'; // TODO: // inteegrate something like this // https://github.com/Rezonans/redux-async-connect -const need = (action) => (Component) => { +const loading = (action) => (Component) => { const Wrapper = React.createClass({ getInitialState() { return { @@ -16,9 +16,17 @@ const need = (action) => (Component) => { const { params, location: { query }, + dispatch, } = this.props; - this.props.action(params, query).then(() => this.setState({ loading: false })); + if (Array.isArray(action)) { + Promise.all( + action.map(oneAction => dispatch(oneAction(params, query))) + ).then(() => this.setState({ loading: false })); + + } else { + dispatch(action(params, query)).then(() => this.setState({ loading: false })); + } }, render() { @@ -28,9 +36,7 @@ const need = (action) => (Component) => { }, }); - return connect(null, { - action, - })(Wrapper); + return connect(null)(Wrapper); }; -export default need; +export default loading; diff --git a/frontend/src/containers/ProjectsList/index.jsx b/frontend/src/containers/ProjectsList/index.jsx index 9c81426..5d4c17a 100644 --- a/frontend/src/containers/ProjectsList/index.jsx +++ b/frontend/src/containers/ProjectsList/index.jsx @@ -15,7 +15,8 @@ const ProjectsListPage = () => ( ); -import need from 'utils/need'; +import loading from 'containers/Application/loading'; import { loadProducts } from 'reduxApp/modules/projects'; +import { fetchUsers } from 'reduxApp/modules/users'; -export default need(loadProducts)(ProjectsListPage); +export default loading([loadProducts, fetchUsers])(ProjectsListPage); diff --git a/frontend/src/containers/TaskAdd/index.jsx b/frontend/src/containers/TaskAdd/index.jsx index 59dd0b0..6607898 100644 --- a/frontend/src/containers/TaskAdd/index.jsx +++ b/frontend/src/containers/TaskAdd/index.jsx @@ -92,7 +92,7 @@ class TaskAdd extends Component { import { connect } from 'react-redux'; import { addTask, addVersion, showAddTaskForm } from 'reduxApp/modules/tasks'; -import need from 'utils/need'; +import loading from 'containers/Application/loading'; export default connect( state => ({ @@ -101,4 +101,4 @@ export default connect( versions: state.tasks.versions, assignee: state.auth.user ? state.auth.user.id : null, }) -, { addTask, addVersion })(need(showAddTaskForm)(TaskAdd)); +, { addTask, addVersion })(loading(showAddTaskForm)(TaskAdd)); diff --git a/frontend/src/containers/TaskDetails/index.jsx b/frontend/src/containers/TaskDetails/index.jsx index 041b09f..31cb176 100644 --- a/frontend/src/containers/TaskDetails/index.jsx +++ b/frontend/src/containers/TaskDetails/index.jsx @@ -12,7 +12,8 @@ const TaskDetails = () => ( ); -import need from 'utils/need'; +import loading from 'containers/Application/loading'; import { loadTask } from 'reduxApp/modules/tasks'; +import { loadComments } from 'reduxApp/modules/comments'; -export default need(loadTask)(TaskDetails); +export default loading([loadTask, loadComments])(TaskDetails); diff --git a/frontend/src/containers/TasksList/index.jsx b/frontend/src/containers/TasksList/index.jsx index 1d33e15..2b7287b 100644 --- a/frontend/src/containers/TasksList/index.jsx +++ b/frontend/src/containers/TasksList/index.jsx @@ -9,7 +9,7 @@ const TaskList = () => ( ); -import need from 'utils/need'; +import loading from 'containers/Application/loading'; import { loadTasks } from 'reduxApp/modules/tasks'; -export default need(loadTasks)(TaskList); +export default loading(loadTasks)(TaskList); diff --git a/frontend/src/containers/UsersList/index.jsx b/frontend/src/containers/UsersList/index.jsx index f873468..dc8d107 100644 --- a/frontend/src/containers/UsersList/index.jsx +++ b/frontend/src/containers/UsersList/index.jsx @@ -10,6 +10,6 @@ const UsersList = () => ( ); -import need from 'utils/need'; +import loading from 'containers/Application/loading'; import { fetchUsers } from 'reduxApp/modules/users'; -export default need(fetchUsers)(UsersList); +export default loading(fetchUsers)(UsersList); diff --git a/frontend/src/reduxApp/modules/comments.jsx b/frontend/src/reduxApp/modules/comments.jsx index 18dbb5f..5c82f40 100644 --- a/frontend/src/reduxApp/modules/comments.jsx +++ b/frontend/src/reduxApp/modules/comments.jsx @@ -27,8 +27,8 @@ function setComments(payload) { }; } -export function loadComments(taskId) { - return (dispatch) => http.get(`/api/tasks/${taskId}/comments`) +export function loadComments({ id }) { + return (dispatch) => http.get(`/api/tasks/${id}/comments`) .then(json => dispatch(setComments(json))); } @@ -40,7 +40,7 @@ export function addComment(text) { router: { params: { id } }, } = getState(); http.post(`/api/tasks/${id}/comments`, { text, userName: name }) - .then(() => dispatch(loadComments(id))); + .then(() => dispatch(loadComments({ id }))); }; } diff --git a/frontend/src/reduxApp/modules/projects.jsx b/frontend/src/reduxApp/modules/projects.jsx index d58a08f..f54643a 100644 --- a/frontend/src/reduxApp/modules/projects.jsx +++ b/frontend/src/reduxApp/modules/projects.jsx @@ -32,19 +32,13 @@ export function reducer(state = initState, action) { import http from 'utils/http'; import { push } from 'redux-router'; -import { fetchUsers } from './users'; export function loadProducts() { - return (dispatch) => { - const users = dispatch(fetchUsers()); - const projects = http.get('/api/projects') + return (dispatch) => http.get('/api/projects') .then(payload => dispatch({ type: 'FETCH_PRODUCTS', payload, })); - - return Promise.all([projects, users]); - }; } diff --git a/frontend/src/reduxApp/modules/tasks.jsx b/frontend/src/reduxApp/modules/tasks.jsx index d01ac32..7a2d74f 100644 --- a/frontend/src/reduxApp/modules/tasks.jsx +++ b/frontend/src/reduxApp/modules/tasks.jsx @@ -25,7 +25,6 @@ export function reducer(state = initState, action) { } } -import { loadComments } from 'reduxApp/modules/comments'; import { fetchUsers } from 'reduxApp/modules/users'; import { push } from 'redux-router'; @@ -66,14 +65,9 @@ export function removeTask() { }; } -export function loadTask() { - return (dispatch, getState) => { - const { router: { params: { id } } } = getState(); - const taskRequest = http.get(`/api/tasks/${id}`).then(data => dispatch(setTask(data))); - const commentsRequest = dispatch(loadComments(id)); - - return Promise.all([taskRequest, commentsRequest]); - }; +export function loadTask({ id }) { + return (dispatch, getState) => http.get(`/api/tasks/${id}`) + .then(data => dispatch(setTask(data))); } From 8e858dbcc1337dcc3f2509e9f454a02973d677b4 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 10 Sep 2016 02:30:01 +0300 Subject: [PATCH 04/18] fix add task from --- frontend/src/components/TaskAddForm/index.css | 8 ++ frontend/src/components/TaskAddForm/index.jsx | 94 +++++++++++++++ .../src/containers/TaskAdd/TaskAddForm.jsx | 15 +++ frontend/src/containers/TaskAdd/index.jsx | 110 ++---------------- frontend/src/reduxApp/modules/tasks.jsx | 19 +-- 5 files changed, 138 insertions(+), 108 deletions(-) create mode 100644 frontend/src/components/TaskAddForm/index.css create mode 100644 frontend/src/components/TaskAddForm/index.jsx create mode 100644 frontend/src/containers/TaskAdd/TaskAddForm.jsx diff --git a/frontend/src/components/TaskAddForm/index.css b/frontend/src/components/TaskAddForm/index.css new file mode 100644 index 0000000..4b15aa8 --- /dev/null +++ b/frontend/src/components/TaskAddForm/index.css @@ -0,0 +1,8 @@ + +.task-add-from__status .Select { + z-index: 4; +} + +.task-add-from__user-select .Select { + z-index: 3; +} diff --git a/frontend/src/components/TaskAddForm/index.jsx b/frontend/src/components/TaskAddForm/index.jsx new file mode 100644 index 0000000..ada1a35 --- /dev/null +++ b/frontend/src/components/TaskAddForm/index.jsx @@ -0,0 +1,94 @@ +import React, { Component } from 'react'; +import { Button, Input } from 'react-bootstrap'; + +import 'react-select/dist/react-select.css'; +import Select from 'react-select'; + +import './index.css'; + +class TaskAddForm extends Component { + constructor(props) { + super(props); + this.state = { status: 'new', assignee: props.assignee, version: 'week1' }; + } + + add() { + const { addTask, users } = this.props; + const title = this.refs.input.getValue(); + const description = this.refs.description.getValue(); + const { status, assignee, version } = this.state; + const assigneeUser = users.find(user => user.id === assignee); + + addTask({ + title, + description, + status, + assignee, + version, + assigneeName: assigneeUser.name, + }); + } + + render() { + const { statuses, users, addVersion, versions } = this.props; + const options = statuses.map(status => ( + { value: status, label: status } + )); + + const allUsers = users.map(user => ( + { value: user.id, label: user.name }) + ); + + const allVersion = versions.map(version => ({ value: version.title, label: version.title })); + + return ( +
+ +
+ + this.setState({ assignee })} + placeholder='-' + options={allUsers} + /> +
+
+ +
+ + + + +
+ ); + } +} + +export default TaskAddForm; diff --git a/frontend/src/containers/TaskAdd/TaskAddForm.jsx b/frontend/src/containers/TaskAdd/TaskAddForm.jsx new file mode 100644 index 0000000..78fcdd0 --- /dev/null +++ b/frontend/src/containers/TaskAdd/TaskAddForm.jsx @@ -0,0 +1,15 @@ +import TaskAddFormComponent from 'components/TaskAddForm/'; +import { connect } from 'react-redux'; +import { addTask, addVersion } from 'reduxApp/modules/tasks'; + +const TaskAddForm = connect( + state => ({ + statuses: state.tasks.statuses, + users: state.users.users, + versions: state.tasks.versions, + assignee: state.auth.user ? state.auth.user.id : null, + }) +, { addTask, addVersion })(TaskAddFormComponent); + + +export default TaskAddForm; diff --git a/frontend/src/containers/TaskAdd/index.jsx b/frontend/src/containers/TaskAdd/index.jsx index 6607898..7518d82 100644 --- a/frontend/src/containers/TaskAdd/index.jsx +++ b/frontend/src/containers/TaskAdd/index.jsx @@ -1,104 +1,14 @@ -import React, { Component } from 'react'; -import { Button, Input } from 'react-bootstrap'; +import React from 'react'; +import TaskAddForm from './TaskAddForm'; -import 'react-select/dist/react-select.css'; -import Select from 'react-select'; - -class TaskAdd extends Component { - constructor(props) { - super(props); - this.state = { status: 'new', assignee: props.assignee, version: 'week1' }; - } - - add() { - const { addTask, users } = this.props; - const title = this.refs.input.getValue(); - const description = this.refs.description.getValue(); - const { status, assignee, version } = this.state; - const assigneeUser = users.find(user => user.id === assignee); - - addTask({ - title, - description, - status, - assignee, - version, - assigneeName: assigneeUser.name, - }); - } - - render() { - const { statuses, users, addVersion, versions } = this.props; - const options = statuses.map(status => ( - { value: status, label: status } - )); - - const allUsers = users.map(user => ( - { value: user.id, label: user.name }) - ); - - const allVersion = versions.map(version => ({ value: version.title, label: version.title })); - - return ( -
- -
- - this.setState({ assignee })} - placeholder='-' - options={allUsers} - /> -
-
- -
- - - - -
- ); - } -} - -import { connect } from 'react-redux'; -import { addTask, addVersion, showAddTaskForm } from 'reduxApp/modules/tasks'; +const TaskAdd = () => ( +
+ +
+); import loading from 'containers/Application/loading'; +import { loadVersions } from 'reduxApp/modules/tasks'; +import { fetchUsers } from 'reduxApp/modules/users'; -export default connect( - state => ({ - statuses: state.tasks.statuses, - users: state.users.users, - versions: state.tasks.versions, - assignee: state.auth.user ? state.auth.user.id : null, - }) -, { addTask, addVersion })(loading(showAddTaskForm)(TaskAdd)); +export default loading([loadVersions, fetchUsers])(TaskAdd); diff --git a/frontend/src/reduxApp/modules/tasks.jsx b/frontend/src/reduxApp/modules/tasks.jsx index 7a2d74f..94631ca 100644 --- a/frontend/src/reduxApp/modules/tasks.jsx +++ b/frontend/src/reduxApp/modules/tasks.jsx @@ -25,8 +25,6 @@ export function reducer(state = initState, action) { } } -import { fetchUsers } from 'reduxApp/modules/users'; - import { push } from 'redux-router'; import http from 'utils/http'; @@ -81,12 +79,17 @@ export function addVersion() { }; } -export function showAddTaskForm() { - return (dispatch) => { - const users = dispatch(fetchUsers()); - const versions = http.get('/api/version') +export function loadVersions() { + return (dispatch) => http.get('/api/version') .then(data => dispatch({ type: 'SET_VERSIONS', payload: data })); - return Promise.all([users, versions]); - }; } +// export function showAddTaskForm() { +// return (dispatch) => { +// const users = dispatch(fetchUsers()); +// const versions = http.get('/api/version') +// .then(data => dispatch({ type: 'SET_VERSIONS', payload: data })); +// return Promise.all([users, versions]); +// }; +// } + From 39d2df3deea0208faf6abb8cc4b1813d1f157e6c Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 14 Sep 2016 23:46:42 +0300 Subject: [PATCH 05/18] fix --- frontend/src/components/TaskTable/index.jsx | 41 +++++++++++++++++++ .../src/containers/TasksList/TaskTable.jsx | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/TaskTable/index.jsx diff --git a/frontend/src/components/TaskTable/index.jsx b/frontend/src/components/TaskTable/index.jsx new file mode 100644 index 0000000..ddec424 --- /dev/null +++ b/frontend/src/components/TaskTable/index.jsx @@ -0,0 +1,41 @@ +import React, { PropTypes } from 'react'; +import { Table } from 'react-bootstrap'; +import { Link } from 'react-router'; + +const TaskTable = ({ tasks, projectId }) => ( + + + + + + + + + + + {tasks.map(task => ( + + + + + + + ))} + +
TitleStatusAssigneeVersion
+ {task.title} + + {task.status} + + {task.assigneeName} + + {task.version} +
+); + +TaskTable.propTypes = { + tasks: PropTypes.array, + projectId: PropTypes.string, +}; + +export default TaskTable; diff --git a/frontend/src/containers/TasksList/TaskTable.jsx b/frontend/src/containers/TasksList/TaskTable.jsx index 07fadda..61a9cd6 100644 --- a/frontend/src/containers/TasksList/TaskTable.jsx +++ b/frontend/src/containers/TasksList/TaskTable.jsx @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; -import { TaskTable as TaskTableComponent } from 'components/Tasks/'; +import TaskTableComponent from 'components/TaskTable/'; const TaskTable = connect( state => ({ From a45a6eb2541ee57337a39c794975f86b8eefae07 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Sep 2016 01:17:38 +0300 Subject: [PATCH 06/18] simple filter --- api/server.js | 4 +- api/services/CRUD.js | 17 +++++- frontend/src/components/TaskFilter/index.jsx | 52 +++++++++++++++++++ .../src/containers/TasksList/TaskFilter.jsx | 20 +++++++ frontend/src/containers/TasksList/index.jsx | 6 ++- frontend/src/reduxApp/modules/reducer.jsx | 2 + frontend/src/reduxApp/modules/taskFilter.jsx | 25 +++++++++ frontend/src/reduxApp/modules/tasks.jsx | 16 +++++- frontend/src/utils/http.jsx | 3 +- 9 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 frontend/src/components/TaskFilter/index.jsx create mode 100644 frontend/src/containers/TasksList/TaskFilter.jsx create mode 100644 frontend/src/reduxApp/modules/taskFilter.jsx diff --git a/api/server.js b/api/server.js index 4babe2e..d7779a2 100644 --- a/api/server.js +++ b/api/server.js @@ -39,10 +39,10 @@ ORM.init(app, function(e){ app.use('/api/projects', ORM.REST('projects')) - app.use('/api/tasks/:task/comments', CRUD.filters('task'), ORM.REST('comments')); + app.use('/api/tasks/:task/comments', CRUD.foreignKey('task'), ORM.REST('comments')); - app.use('/api/tasks', ORM.REST('tasks')) + app.use('/api/tasks', CRUD.query(['status', 'assignee', 'version']), ORM.REST('tasks')) app.use('/api/users', ORM.REST('users')) app.use('/api/version', ORM.REST('version')) diff --git a/api/services/CRUD.js b/api/services/CRUD.js index 1e8d17d..d9a618e 100644 --- a/api/services/CRUD.js +++ b/api/services/CRUD.js @@ -46,7 +46,7 @@ module.exports = function (modelName, foreignKey) { //TODO: make more complex query -module.exports.filters = function(foreignKey){ +module.exports.foreignKey = function(foreignKey){ return function (req, res, next) { if (foreignKey){ req.filters = req.filters || {}; @@ -56,13 +56,26 @@ module.exports.filters = function(foreignKey){ if (req.body){ req.body[foreignKey] = value; } - } //console.log(req.body, req.params, foreignKey) next(); } }; +module.exports.query = function(names) { + return function(req, res, next) { + req.filters = req.filters || {}; + + names.forEach(function(name){ + if (req.query[name]) { //TODO: fix 0 + req.filters[name] = req.query[name]; + } + }) + + next(); + } +} + module.exports.exec = function (req, res, next) { var cb = function(err, result) { diff --git a/frontend/src/components/TaskFilter/index.jsx b/frontend/src/components/TaskFilter/index.jsx new file mode 100644 index 0000000..714a5b7 --- /dev/null +++ b/frontend/src/components/TaskFilter/index.jsx @@ -0,0 +1,52 @@ +import React from 'react'; + +import { Button, Grid, Col, Row } from 'react-bootstrap'; + +import Select from 'react-select'; + +const TaskFilter = ({ statuses, users, userId, status, changeFilter, findTask }) => { + const options = statuses.map(str => ( + { value: str, label: str } + )); + + const allUsers = users.map(user => ( + { value: user.id, label: user.name }) + ); + + return ( +
+ + + + changeFilter('userId', newValue.value || null)} + searchable={false} + clearable={false} + options={allUsers.concat([{ value: '', label: 'All' }])} + /> + + + +
+ +
+
+ ); +}; + + +export default TaskFilter; diff --git a/frontend/src/containers/TasksList/TaskFilter.jsx b/frontend/src/containers/TasksList/TaskFilter.jsx new file mode 100644 index 0000000..5fe09c9 --- /dev/null +++ b/frontend/src/containers/TasksList/TaskFilter.jsx @@ -0,0 +1,20 @@ +import { connect } from 'react-redux'; + +import * as action from 'reduxApp/modules/taskFilter'; +import { loadTasks } from 'reduxApp/modules/tasks'; + +import TaskFilter from 'components/TaskFilter/'; + +export default connect( + state => ({ + statuses: state.tasks.statuses, + users: state.users.users, + userId: state.taskFilter.userId, + status: state.taskFilter.status, + }), + { + changeFilter: action.changeFilter, + findTask: loadTasks, + }, +)(TaskFilter); + diff --git a/frontend/src/containers/TasksList/index.jsx b/frontend/src/containers/TasksList/index.jsx index 2b7287b..10377c7 100644 --- a/frontend/src/containers/TasksList/index.jsx +++ b/frontend/src/containers/TasksList/index.jsx @@ -3,13 +3,17 @@ import React from 'react'; import TaskTable from './TaskTable'; +import TaskFilter from './TaskFilter'; + const TaskList = () => (
+
); import loading from 'containers/Application/loading'; import { loadTasks } from 'reduxApp/modules/tasks'; +import { fetchUsers } from 'reduxApp/modules/users'; -export default loading(loadTasks)(TaskList); +export default loading([loadTasks, fetchUsers])(TaskList); diff --git a/frontend/src/reduxApp/modules/reducer.jsx b/frontend/src/reduxApp/modules/reducer.jsx index f24768f..abad1e8 100644 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ b/frontend/src/reduxApp/modules/reducer.jsx @@ -7,8 +7,10 @@ import { reducer as projects } from './projects'; import { reducer as tasks } from './tasks'; import { reducer as users } from './users'; import { reducer as comments } from './comments'; +import { reducer as taskFilter } from './taskFilter'; export default combineReducers({ + taskFilter, comments, users, projects, diff --git a/frontend/src/reduxApp/modules/taskFilter.jsx b/frontend/src/reduxApp/modules/taskFilter.jsx new file mode 100644 index 0000000..20f57ef --- /dev/null +++ b/frontend/src/reduxApp/modules/taskFilter.jsx @@ -0,0 +1,25 @@ + +const initState = { + userId: null, + status: 'new', +}; + +export function reducer(state = initState, action) { + switch (action.type) { + case 'CHANGE_TASK_FILTER': { + const { field, value } = action.payload; + return { ...state, [field]: value }; + } + + default: + return state; + } +} + +export function changeFilter(field, value) { + return { + type: 'CHANGE_TASK_FILTER', + payload: { field, value }, + }; +} + diff --git a/frontend/src/reduxApp/modules/tasks.jsx b/frontend/src/reduxApp/modules/tasks.jsx index 94631ca..b036a94 100644 --- a/frontend/src/reduxApp/modules/tasks.jsx +++ b/frontend/src/reduxApp/modules/tasks.jsx @@ -42,10 +42,22 @@ function setTask(payload) { }; } +const toParams = (obj) => Object.keys(obj) + .filter(key => !!obj[key]) + .map(key => `${key}=${obj[key]}`) + .join('&'); +// TODO: page leavel loadTasks(params) export function loadTasks() { - return (dispatch) => http.get('/api/tasks') - .then(json => dispatch(setTasks(json))); + return (dispatch, getState) => { + const { userId, status } = getState().taskFilter; + const paramsStr = toParams({ + assignee: userId, + status, + }); + return http.get(`/api/tasks?${paramsStr}`) + .then(json => dispatch(setTasks(json))); + }; } diff --git a/frontend/src/utils/http.jsx b/frontend/src/utils/http.jsx index 587068e..7dcfb11 100644 --- a/frontend/src/utils/http.jsx +++ b/frontend/src/utils/http.jsx @@ -4,12 +4,13 @@ var $ = require('jquery'); -var get = function(url){ +var get = function(url, data){ return $.ajax({ url : url, type : 'GET', dataType : 'json', cache : false, + data, contentType:"application/json" }) } From 87b99228413e5fe8136c4c1f843e084981c62bac Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Sep 2016 02:30:35 +0300 Subject: [PATCH 07/18] pager --- api/services/CRUD.js | 31 +++++++++++++++++++ api/services/ORM.js | 3 +- frontend/src/components/TaskPager/index.jsx | 20 ++++++++++++ .../src/containers/TasksList/TaskFilter.jsx | 4 +-- .../src/containers/TasksList/TaskPager.jsx | 14 +++++++++ frontend/src/containers/TasksList/index.jsx | 2 ++ frontend/src/reduxApp/modules/comments.jsx | 5 +-- frontend/src/reduxApp/modules/taskFilter.jsx | 9 ++++++ frontend/src/reduxApp/modules/tasks.jsx | 29 ++++++++++++++--- 9 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 frontend/src/components/TaskPager/index.jsx create mode 100644 frontend/src/containers/TasksList/TaskPager.jsx diff --git a/api/services/CRUD.js b/api/services/CRUD.js index d9a618e..1067e65 100644 --- a/api/services/CRUD.js +++ b/api/services/CRUD.js @@ -16,6 +16,21 @@ module.exports = function (modelName, foreignKey) { req.db[modelName].find()); next(); }, + getPage: function(req, res, next) { + var page = req.param('page'); + var limit = req.param('limit') || 10; + + res.ormQuery = applyFilters( + req.filters, + req.db[modelName].find({ skip: page * limit, limit: limit })); + next(); + }, + count: function(req, res, next) { + res.count = applyFilters( + req.filters, + req.db[modelName].count()); + next(); + }, findOne: function (req, res, next) { res.ormQuery = applyFilters( req.filters, @@ -89,6 +104,22 @@ module.exports.exec = function (req, res, next) { res.ormQuery.exec(cb); }; +//TODO: create generec way for requests +module.exports.execPage = function(req, res, next) { + var cb = function(results, err){ + if (err) { + return next(err); + } + res.result = { + items: results[0], + count: results[1] + } + + next(); + } + Promise.all([res.ormQuery, res.count]).then(cb); +} + module.exports.returnJSON = function (req, res, next) { diff --git a/api/services/ORM.js b/api/services/ORM.js index 91362f7..d62a66c 100644 --- a/api/services/ORM.js +++ b/api/services/ORM.js @@ -128,9 +128,10 @@ var exports = { var service = CRUD(collectionName, foreignKey); - return router .get('/', service.findAll) + .get('/page/:page', service.getPage, service.count, CRUD.execPage, CRUD.returnJSON) + .get('/page/:page/:limit', service.getPage, service.count, CRUD.execPage, CRUD.returnJSON) .get('/:id', service.findOne) .post('/', service.add) .put('/:id', service.updateOne) diff --git a/frontend/src/components/TaskPager/index.jsx b/frontend/src/components/TaskPager/index.jsx new file mode 100644 index 0000000..acc64d7 --- /dev/null +++ b/frontend/src/components/TaskPager/index.jsx @@ -0,0 +1,20 @@ +import React, { PropTypes } from 'react'; +import { Pagination } from 'react-bootstrap'; + + +const TaskPager = (props) => ( +
+ {props.items > 1 && } +
+); + +TaskPager.propTypes = { + items: PropTypes.number, + activePage: PropTypes.number, + onSelect: PropTypes.func, +}; + +export default TaskPager; diff --git a/frontend/src/containers/TasksList/TaskFilter.jsx b/frontend/src/containers/TasksList/TaskFilter.jsx index 5fe09c9..9b48fe7 100644 --- a/frontend/src/containers/TasksList/TaskFilter.jsx +++ b/frontend/src/containers/TasksList/TaskFilter.jsx @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import * as action from 'reduxApp/modules/taskFilter'; -import { loadTasks } from 'reduxApp/modules/tasks'; +// import { loadTasks } from 'reduxApp/modules/tasks'; import TaskFilter from 'components/TaskFilter/'; @@ -14,7 +14,7 @@ export default connect( }), { changeFilter: action.changeFilter, - findTask: loadTasks, + findTask: action.findTask, }, )(TaskFilter); diff --git a/frontend/src/containers/TasksList/TaskPager.jsx b/frontend/src/containers/TasksList/TaskPager.jsx new file mode 100644 index 0000000..3997916 --- /dev/null +++ b/frontend/src/containers/TasksList/TaskPager.jsx @@ -0,0 +1,14 @@ +import { connect } from 'react-redux'; + +import TaskPager from 'components/TaskPager'; +import { changeTaskPage } from 'reduxApp/modules/tasks'; + +export default connect( + state => ({ + items: state.tasks.items, + activePage: state.tasks.page, + }), + { + onSelect: changeTaskPage, + } +)(TaskPager); diff --git a/frontend/src/containers/TasksList/index.jsx b/frontend/src/containers/TasksList/index.jsx index 10377c7..0930a4a 100644 --- a/frontend/src/containers/TasksList/index.jsx +++ b/frontend/src/containers/TasksList/index.jsx @@ -4,11 +4,13 @@ import React from 'react'; import TaskTable from './TaskTable'; import TaskFilter from './TaskFilter'; +import TaskPager from './TaskPager'; const TaskList = () => (
+
); diff --git a/frontend/src/reduxApp/modules/comments.jsx b/frontend/src/reduxApp/modules/comments.jsx index 5c82f40..ccbb568 100644 --- a/frontend/src/reduxApp/modules/comments.jsx +++ b/frontend/src/reduxApp/modules/comments.jsx @@ -28,8 +28,9 @@ function setComments(payload) { } export function loadComments({ id }) { - return (dispatch) => http.get(`/api/tasks/${id}/comments`) - .then(json => dispatch(setComments(json))); + // TODO: make load more + return (dispatch) => http.get(`/api/tasks/${id}/comments/page/0/5`) + .then(json => dispatch(setComments(json.items))); } diff --git a/frontend/src/reduxApp/modules/taskFilter.jsx b/frontend/src/reduxApp/modules/taskFilter.jsx index 20f57ef..fccdc03 100644 --- a/frontend/src/reduxApp/modules/taskFilter.jsx +++ b/frontend/src/reduxApp/modules/taskFilter.jsx @@ -16,6 +16,9 @@ export function reducer(state = initState, action) { } } +import { setPage, loadTasks } from 'reduxApp/modules/tasks'; + + export function changeFilter(field, value) { return { type: 'CHANGE_TASK_FILTER', @@ -23,3 +26,9 @@ export function changeFilter(field, value) { }; } +export function findTask() { + return (dispatch) => { + dispatch(setPage(1)); + dispatch(loadTasks()); + }; +} diff --git a/frontend/src/reduxApp/modules/tasks.jsx b/frontend/src/reduxApp/modules/tasks.jsx index b036a94..e1fc875 100644 --- a/frontend/src/reduxApp/modules/tasks.jsx +++ b/frontend/src/reduxApp/modules/tasks.jsx @@ -4,12 +4,16 @@ const initState = { task: null, statuses: ['new', 'inprogress', 'testing', 'complited'], versions: [], + page: 1, + items: 0, }; export function reducer(state = initState, action) { switch (action.type) { - case 'SET_TASKS': - return { ...state, tasks: action.payload }; + case 'SET_TASKS': { + const { count, items } = action.payload; + return { ...state, tasks: items, items: Math.ceil(count / 10) }; + } case 'SET_TASK': return { ...state, task: action.payload }; @@ -17,6 +21,9 @@ export function reducer(state = initState, action) { case 'SET_VERSIONS': return { ...state, versions: action.payload }; + case 'SET_TASKS_PAGE': + return { ...state, page: action.payload }; + case 'ADD_VERSION': return { ...state, versions: state.versions.concat([action.payload]) }; @@ -55,11 +62,25 @@ export function loadTasks() { assignee: userId, status, }); - return http.get(`/api/tasks?${paramsStr}`) - .then(json => dispatch(setTasks(json))); + const page = getState().tasks.page - 1; + return http.get(`/api/tasks/page/${page}/5?${paramsStr}`) + .then(({ items, count }) => dispatch(setTasks({ items, count }))); }; } +export function setPage(page) { + return { + type: 'SET_TASKS_PAGE', + payload: page, + }; +} + +export function changeTaskPage(e, data) { + return (dispatch) => { + dispatch(setPage(data.eventKey)); + dispatch(loadTasks()); + }; +} export function addTask(form) { return (dispatch, getState) => { From d59011bc91b1b764c99d1201d38a845a73dbc164 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 1 Oct 2016 04:15:14 +0300 Subject: [PATCH 08/18] try new structure --- frontend/src/components/TaskFilter/index.jsx | 52 --------------- frontend/src/components/TaskInfo/index.jsx | 22 ------- .../ProjectsList/AddProjectModal.jsx | 2 +- .../containers/ProjectsList/ProjectsList.jsx | 16 ----- .../ProjectsList}/ProjectsList/index.jsx | 15 ++++- .../src/containers/ProjectsList/index.jsx | 7 +- .../src/containers/ProjectsList/state.jsx | 28 ++++++++ .../containers/TaskDetails/AddComentForm.jsx | 14 ---- .../TaskDetails}/AddComentForm/index.jsx | 9 ++- .../containers/TaskDetails/CommentsList.jsx | 15 ----- .../TaskDetails}/CommentsList/index.jsx | 18 +++-- .../src/containers/TaskDetails/TaskInfo.jsx | 17 ----- .../containers/TaskDetails/TaskInfo/index.jsx | 33 ++++++++++ frontend/src/containers/TaskDetails/index.jsx | 11 ++-- frontend/src/containers/TaskDetails/state.jsx | 58 +++++++++++++++++ .../src/containers/TasksList/TaskFilter.jsx | 20 ------ .../TasksList/TaskFilter/StatusSelect.jsx | 30 +++++++++ .../TasksList/TaskFilter/UserSelect.jsx | 29 +++++++++ .../containers/TasksList/TaskFilter/index.jsx | 39 +++++++++++ .../src/containers/TasksList/TaskPager.jsx | 14 ---- .../TasksList}/TaskPager/index.jsx | 15 ++++- .../src/containers/TasksList/TaskTable.jsx | 12 ---- .../TasksList}/TaskTable/index.jsx | 8 ++- frontend/src/containers/TasksList/index.jsx | 12 ++-- frontend/src/containers/TasksList/state.jsx | 65 +++++++++++++++++++ .../src/containers/UsersList/UsersTable.jsx | 13 ---- .../containers/UsersList/UsersTable/index.jsx | 52 +++++++++++++++ frontend/src/containers/UsersList/index.jsx | 5 +- frontend/src/containers/UsersList/state.jsx | 6 ++ frontend/src/main.jsx | 6 +- frontend/src/reduxApp/modules/app.jsx | 33 ++++++++++ frontend/src/reduxApp/modules/comments.jsx | 55 ---------------- frontend/src/reduxApp/modules/reducer.jsx | 9 +++ frontend/src/reduxApp/modules/taskFilter.jsx | 34 ---------- frontend/src/utils/http.jsx | 7 +- 35 files changed, 465 insertions(+), 316 deletions(-) delete mode 100644 frontend/src/components/TaskFilter/index.jsx delete mode 100644 frontend/src/components/TaskInfo/index.jsx delete mode 100644 frontend/src/containers/ProjectsList/ProjectsList.jsx rename frontend/src/{components => containers/ProjectsList}/ProjectsList/index.jsx (83%) create mode 100644 frontend/src/containers/ProjectsList/state.jsx delete mode 100644 frontend/src/containers/TaskDetails/AddComentForm.jsx rename frontend/src/{components => containers/TaskDetails}/AddComentForm/index.jsx (85%) delete mode 100644 frontend/src/containers/TaskDetails/CommentsList.jsx rename frontend/src/{components => containers/TaskDetails}/CommentsList/index.jsx (60%) delete mode 100644 frontend/src/containers/TaskDetails/TaskInfo.jsx create mode 100644 frontend/src/containers/TaskDetails/TaskInfo/index.jsx create mode 100644 frontend/src/containers/TaskDetails/state.jsx delete mode 100644 frontend/src/containers/TasksList/TaskFilter.jsx create mode 100644 frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx create mode 100644 frontend/src/containers/TasksList/TaskFilter/UserSelect.jsx create mode 100644 frontend/src/containers/TasksList/TaskFilter/index.jsx delete mode 100644 frontend/src/containers/TasksList/TaskPager.jsx rename frontend/src/{components => containers/TasksList}/TaskPager/index.jsx (54%) delete mode 100644 frontend/src/containers/TasksList/TaskTable.jsx rename frontend/src/{components => containers/TasksList}/TaskTable/index.jsx (84%) create mode 100644 frontend/src/containers/TasksList/state.jsx delete mode 100644 frontend/src/containers/UsersList/UsersTable.jsx create mode 100644 frontend/src/containers/UsersList/UsersTable/index.jsx create mode 100644 frontend/src/containers/UsersList/state.jsx create mode 100644 frontend/src/reduxApp/modules/app.jsx delete mode 100644 frontend/src/reduxApp/modules/comments.jsx delete mode 100644 frontend/src/reduxApp/modules/taskFilter.jsx diff --git a/frontend/src/components/TaskFilter/index.jsx b/frontend/src/components/TaskFilter/index.jsx deleted file mode 100644 index 714a5b7..0000000 --- a/frontend/src/components/TaskFilter/index.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; - -import { Button, Grid, Col, Row } from 'react-bootstrap'; - -import Select from 'react-select'; - -const TaskFilter = ({ statuses, users, userId, status, changeFilter, findTask }) => { - const options = statuses.map(str => ( - { value: str, label: str } - )); - - const allUsers = users.map(user => ( - { value: user.id, label: user.name }) - ); - - return ( -
- - - - changeFilter('userId', newValue.value || null)} - searchable={false} - clearable={false} - options={allUsers.concat([{ value: '', label: 'All' }])} - /> - - - -
- -
-
- ); -}; - - -export default TaskFilter; diff --git a/frontend/src/components/TaskInfo/index.jsx b/frontend/src/components/TaskInfo/index.jsx deleted file mode 100644 index f613b49..0000000 --- a/frontend/src/components/TaskInfo/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React, { PropTypes } from 'react'; -import { Button } from 'react-bootstrap'; - -const TaskInfo = ({ task: { title, description }, removeTask }) => ( -
-
-

{title}

-

- {description} -

-
- -
-); - -TaskInfo.propTypes = { - task: PropTypes.object, - removeTask: PropTypes.func, -}; - - -export default TaskInfo; diff --git a/frontend/src/containers/ProjectsList/AddProjectModal.jsx b/frontend/src/containers/ProjectsList/AddProjectModal.jsx index a15c3f5..b454a85 100644 --- a/frontend/src/containers/ProjectsList/AddProjectModal.jsx +++ b/frontend/src/containers/ProjectsList/AddProjectModal.jsx @@ -9,7 +9,7 @@ import { const AddProjectModal = connect( state => ({ show: state.projects.popupOpen, - users: state.users.users, + users: state.app.users, }), { onHide: closePopup, addProject } )(AddProjectModalComponent); diff --git a/frontend/src/containers/ProjectsList/ProjectsList.jsx b/frontend/src/containers/ProjectsList/ProjectsList.jsx deleted file mode 100644 index 271e059..0000000 --- a/frontend/src/containers/ProjectsList/ProjectsList.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import ProjectsListComponent from 'components/ProjectsList/'; -import { connect } from 'react-redux'; - -import { - fetchProducts, removeProject, - openPopup, fetchUsers, -} from 'reduxApp/modules/projects'; - -const ProjectsList = connect( - state => ({ - projects: state.projects.projects, - }), - { fetchProducts, removeProject, openPopup, fetchUsers } -)(ProjectsListComponent); - -export default ProjectsList; diff --git a/frontend/src/components/ProjectsList/index.jsx b/frontend/src/containers/ProjectsList/ProjectsList/index.jsx similarity index 83% rename from frontend/src/components/ProjectsList/index.jsx rename to frontend/src/containers/ProjectsList/ProjectsList/index.jsx index 372d240..bcf50db 100644 --- a/frontend/src/components/ProjectsList/index.jsx +++ b/frontend/src/containers/ProjectsList/ProjectsList/index.jsx @@ -27,7 +27,7 @@ const ProductItem = ({ product, removeProject }) => { ); }; -const ProjectsList = ({ openPopup, projects, removeProject }) => ( +const ProjectsList = ({ projects, removeProject }) => (
@@ -49,4 +49,15 @@ const ProjectsList = ({ openPopup, projects, removeProject }) => ( ); -export default ProjectsList; +import { connect } from 'react-redux'; + +import { + removeProject, +} from '../state'; + +export default connect( + state => ({ + projects: state.projectsList.projects, + }), + { removeProject } +)(ProjectsList); diff --git a/frontend/src/containers/ProjectsList/index.jsx b/frontend/src/containers/ProjectsList/index.jsx index 5d4c17a..7caad73 100644 --- a/frontend/src/containers/ProjectsList/index.jsx +++ b/frontend/src/containers/ProjectsList/index.jsx @@ -1,6 +1,6 @@ import React from 'react'; -import ProjectsList from './ProjectsList'; +import ProjectsList from './ProjectsList/'; import AddProjectModal from './AddProjectModal'; import DashboardMenu from 'containers/Application/DashboardMenu'; @@ -16,7 +16,6 @@ const ProjectsListPage = () => ( ); import loading from 'containers/Application/loading'; -import { loadProducts } from 'reduxApp/modules/projects'; -import { fetchUsers } from 'reduxApp/modules/users'; +import { showPage } from './state'; -export default loading([loadProducts, fetchUsers])(ProjectsListPage); +export default loading([showPage])(ProjectsListPage); diff --git a/frontend/src/containers/ProjectsList/state.jsx b/frontend/src/containers/ProjectsList/state.jsx new file mode 100644 index 0000000..39af34d --- /dev/null +++ b/frontend/src/containers/ProjectsList/state.jsx @@ -0,0 +1,28 @@ + +const initState = { + projects: [], +}; + +export const reducer = (state = initState, action) => { + switch (action.type) { + case 'SET_PROJECTS': + return { ...state, projects: action.payload }; + + default: + return state; + } +}; + +import http from 'utils/http'; + +const setProjects = (projects) => ({ type: 'SET_PROJECTS', payload: projects }); + +const loadProjects = () => (dispatch) => + http.get('/api/projects').then(json => dispatch(setProjects(json))); + +// Publick + +export const showPage = () => (dispatch) => dispatch(loadProjects()); + +export const removeProject = ({ id }) => (dispatch) => + http.del(`/api/projects/${id}`).then(() => dispatch(loadProjects())); diff --git a/frontend/src/containers/TaskDetails/AddComentForm.jsx b/frontend/src/containers/TaskDetails/AddComentForm.jsx deleted file mode 100644 index 9a5194d..0000000 --- a/frontend/src/containers/TaskDetails/AddComentForm.jsx +++ /dev/null @@ -1,14 +0,0 @@ - -import { connect } from 'react-redux'; -import { addComment } from 'reduxApp/modules/comments'; - -import AddComentFormComponent from 'components/AddComentForm/'; - - -const AddComentForm = connect( - null, - { addComment } -)(AddComentFormComponent); - - -export default AddComentForm; diff --git a/frontend/src/components/AddComentForm/index.jsx b/frontend/src/containers/TaskDetails/AddComentForm/index.jsx similarity index 85% rename from frontend/src/components/AddComentForm/index.jsx rename to frontend/src/containers/TaskDetails/AddComentForm/index.jsx index 6e2df98..1f780af 100644 --- a/frontend/src/components/AddComentForm/index.jsx +++ b/frontend/src/containers/TaskDetails/AddComentForm/index.jsx @@ -1,3 +1,6 @@ + +import { connect } from 'react-redux'; + import React, { Component, PropTypes } from 'react'; import { Button, Input } from 'react-bootstrap'; @@ -35,5 +38,9 @@ class AddComentForm extends Component { } } +import { addComment } from '../state'; -export default AddComentForm; +export default connect( + null, + { addComment } +)(AddComentForm); diff --git a/frontend/src/containers/TaskDetails/CommentsList.jsx b/frontend/src/containers/TaskDetails/CommentsList.jsx deleted file mode 100644 index 57267c9..0000000 --- a/frontend/src/containers/TaskDetails/CommentsList.jsx +++ /dev/null @@ -1,15 +0,0 @@ - -import { connect } from 'react-redux'; -import { removeComent } from 'reduxApp/modules/comments'; - -import CommentsListComponent from 'components/CommentsList/'; - -const CommentsList = connect( - state => ({ - comments: state.comments.comments, - }), - { removeComent } -)(CommentsListComponent); - - -export default CommentsList; diff --git a/frontend/src/components/CommentsList/index.jsx b/frontend/src/containers/TaskDetails/CommentsList/index.jsx similarity index 60% rename from frontend/src/components/CommentsList/index.jsx rename to frontend/src/containers/TaskDetails/CommentsList/index.jsx index f0ab8b8..266643a 100644 --- a/frontend/src/components/CommentsList/index.jsx +++ b/frontend/src/containers/TaskDetails/CommentsList/index.jsx @@ -1,7 +1,10 @@ + +import { connect } from 'react-redux'; + import React, { PropTypes } from 'react'; import { Button } from 'react-bootstrap'; -const CommentsList = ({ comments, removeComent }) => ( +const CommentsList = ({ comments, deleteComment }) => (
{comments.map(comment => (
@@ -10,7 +13,7 @@ const CommentsList = ({ comments, removeComent }) => ( @@ -23,7 +26,14 @@ const CommentsList = ({ comments, removeComent }) => ( CommentsList.propTypes = { comments: PropTypes.array, - removeComent: PropTypes.func, + deleteComment: PropTypes.func, }; -export default CommentsList; +import { deleteComment } from '../state'; + +export default connect( + state => ({ + comments: state.tasksDetails.comments, + }), + { deleteComment } +)(CommentsList); diff --git a/frontend/src/containers/TaskDetails/TaskInfo.jsx b/frontend/src/containers/TaskDetails/TaskInfo.jsx deleted file mode 100644 index 0258a9f..0000000 --- a/frontend/src/containers/TaskDetails/TaskInfo.jsx +++ /dev/null @@ -1,17 +0,0 @@ - -import { connect } from 'react-redux'; -import { removeTask } from 'reduxApp/modules/tasks'; - -import TaskInfoComponent from 'components/TaskInfo/'; - - -const TaskInfo = connect( - state => ({ - task: state.tasks.task, - }), - { removeTask } -)(TaskInfoComponent); - - -export default TaskInfo; - diff --git a/frontend/src/containers/TaskDetails/TaskInfo/index.jsx b/frontend/src/containers/TaskDetails/TaskInfo/index.jsx new file mode 100644 index 0000000..90cf29a --- /dev/null +++ b/frontend/src/containers/TaskDetails/TaskInfo/index.jsx @@ -0,0 +1,33 @@ + +import { connect } from 'react-redux'; + +import React, { PropTypes } from 'react'; +import { Button } from 'react-bootstrap'; + +const TaskInfo = ({ task: { title, description }, deleteTask }) => ( +
+
+

{title}

+

+ {description} +

+
+ +
+); + +TaskInfo.propTypes = { + task: PropTypes.object, + deleteTask: PropTypes.func, +}; + +import { deleteTask } from '../state'; + +export default connect( + state => ({ + task: state.tasksDetails.task, + }), + { deleteTask } +)(TaskInfo); + + diff --git a/frontend/src/containers/TaskDetails/index.jsx b/frontend/src/containers/TaskDetails/index.jsx index 31cb176..d6fc169 100644 --- a/frontend/src/containers/TaskDetails/index.jsx +++ b/frontend/src/containers/TaskDetails/index.jsx @@ -1,8 +1,8 @@ import React from 'react'; -import TaskInfo from './TaskInfo'; -import AddComentForm from './AddComentForm'; -import CommentsList from './CommentsList'; +import TaskInfo from './TaskInfo/'; +import AddComentForm from './AddComentForm/'; +import CommentsList from './CommentsList/'; const TaskDetails = () => (
@@ -13,7 +13,6 @@ const TaskDetails = () => ( ); import loading from 'containers/Application/loading'; -import { loadTask } from 'reduxApp/modules/tasks'; -import { loadComments } from 'reduxApp/modules/comments'; +import { showPage } from './state'; -export default loading([loadTask, loadComments])(TaskDetails); +export default loading([showPage])(TaskDetails); diff --git a/frontend/src/containers/TaskDetails/state.jsx b/frontend/src/containers/TaskDetails/state.jsx new file mode 100644 index 0000000..e650e87 --- /dev/null +++ b/frontend/src/containers/TaskDetails/state.jsx @@ -0,0 +1,58 @@ + +const initState = { + task: null, + comments: [], +}; + +export const reducer = (state = initState, action) => { + switch (action.type) { + case 'SET_COMMENTS': + return { ...state, comments: action.payload }; + case 'SET_TASK': + return { ...state, task: action.payload }; + default: + return state; + } +}; + +import http from 'utils/http'; +import { push } from 'redux-router'; + +const setComments = (comments) => ({ type: 'SET_COMMENTS', payload: comments }); +const setTask = (task) => ({ type: 'SET_TASK', payload: task }); + +const loadComments = (id) => (dispatch) => + http.get(`/api/tasks/${id}/comments/page/0/5`) + .then(json => dispatch(setComments(json.items))); + +const loadTask = (id) => (dispatch) => + http.get(`/api/tasks/${id}`) + .then(json => dispatch(setTask(json))); + +// PUBLICK + +export const deleteTask = () => (dispatch, getState) => { + const { router: { params: { id, projectId } } } = getState(); + return http.del(`/api/tasks/${id}`).then(() => dispatch(push(`/projects/${projectId}/tasks`))); +}; + +export const addComment = (text) => (dispatch, getState) => { + const { + auth: { user: { name } }, + router: { params: { id } }, + } = getState(); + return http.post(`/api/tasks/${id}/comments`, { text, userName: name }) + .then(() => dispatch(loadComments(id))); +}; + +export const deleteComment = (comment) => (dispatch, getState) => { + const { router: { params: { id } } } = getState(); + return http.del(`/api/tasks/${id}/comments/${comment.id}`) + .then(() => dispatch(loadComments(id))); +}; + +export const showPage = ({ id }) => (dispatch) => + Promise.all([ + dispatch(loadComments(id)), + dispatch(loadTask(id)), + ]); diff --git a/frontend/src/containers/TasksList/TaskFilter.jsx b/frontend/src/containers/TasksList/TaskFilter.jsx deleted file mode 100644 index 9b48fe7..0000000 --- a/frontend/src/containers/TasksList/TaskFilter.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import { connect } from 'react-redux'; - -import * as action from 'reduxApp/modules/taskFilter'; -// import { loadTasks } from 'reduxApp/modules/tasks'; - -import TaskFilter from 'components/TaskFilter/'; - -export default connect( - state => ({ - statuses: state.tasks.statuses, - users: state.users.users, - userId: state.taskFilter.userId, - status: state.taskFilter.status, - }), - { - changeFilter: action.changeFilter, - findTask: action.findTask, - }, -)(TaskFilter); - diff --git a/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx b/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx new file mode 100644 index 0000000..e4d94aa --- /dev/null +++ b/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx @@ -0,0 +1,30 @@ + +import React from 'react'; +import Select from 'react-select'; +import { connect } from 'react-redux'; + +const StatusSelect = ({ statuses, status, changeStatus }) => ( + changeUserId(newValue.value || null)} + searchable={false} + clearable={false} + options={users.concat([{ value: '', label: 'All' }])} + /> +); + +import { changeUserId } from '../state'; + +import { + getUserOptions, +} from 'reduxApp/modules/app'; + + +export default connect( + state => ({ + users: getUserOptions(state), + userId: state.tasksList.userId, + }), + { changeUserId }, +)(UserSelect); diff --git a/frontend/src/containers/TasksList/TaskFilter/index.jsx b/frontend/src/containers/TasksList/TaskFilter/index.jsx new file mode 100644 index 0000000..0ac571f --- /dev/null +++ b/frontend/src/containers/TasksList/TaskFilter/index.jsx @@ -0,0 +1,39 @@ +import { connect } from 'react-redux'; + +import React from 'react'; + +import { Button, Grid, Col, Row } from 'react-bootstrap'; + +import UserSelect from './UserSelect'; +import StatusSelect from './StatusSelect'; + +const TaskFilter = ({ makeSearch }) => ( +
+ + + + + + + + + + +
+ +
+
+); + + +import { + makeSearch, +} from '../state'; + +export default connect( + null, + { + makeSearch, + }, +)(TaskFilter); + diff --git a/frontend/src/containers/TasksList/TaskPager.jsx b/frontend/src/containers/TasksList/TaskPager.jsx deleted file mode 100644 index 3997916..0000000 --- a/frontend/src/containers/TasksList/TaskPager.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import { connect } from 'react-redux'; - -import TaskPager from 'components/TaskPager'; -import { changeTaskPage } from 'reduxApp/modules/tasks'; - -export default connect( - state => ({ - items: state.tasks.items, - activePage: state.tasks.page, - }), - { - onSelect: changeTaskPage, - } -)(TaskPager); diff --git a/frontend/src/components/TaskPager/index.jsx b/frontend/src/containers/TasksList/TaskPager/index.jsx similarity index 54% rename from frontend/src/components/TaskPager/index.jsx rename to frontend/src/containers/TasksList/TaskPager/index.jsx index acc64d7..bdbf647 100644 --- a/frontend/src/components/TaskPager/index.jsx +++ b/frontend/src/containers/TasksList/TaskPager/index.jsx @@ -1,12 +1,13 @@ +import { connect } from 'react-redux'; import React, { PropTypes } from 'react'; import { Pagination } from 'react-bootstrap'; - const TaskPager = (props) => (
{props.items > 1 && props.onSelect(data.eventKey)} />}
); @@ -17,4 +18,14 @@ TaskPager.propTypes = { onSelect: PropTypes.func, }; -export default TaskPager; +import { changePage } from '../state'; + +export default connect( + state => ({ + items: state.tasksList.items, + activePage: state.tasksList.page, + }), + { + onSelect: changePage, + } +)(TaskPager); diff --git a/frontend/src/containers/TasksList/TaskTable.jsx b/frontend/src/containers/TasksList/TaskTable.jsx deleted file mode 100644 index 61a9cd6..0000000 --- a/frontend/src/containers/TasksList/TaskTable.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import { connect } from 'react-redux'; - -import TaskTableComponent from 'components/TaskTable/'; - -const TaskTable = connect( - state => ({ - tasks: state.tasks.tasks, - projectId: state.router.params.projectId, - }) -)(TaskTableComponent); - -export default TaskTable; diff --git a/frontend/src/components/TaskTable/index.jsx b/frontend/src/containers/TasksList/TaskTable/index.jsx similarity index 84% rename from frontend/src/components/TaskTable/index.jsx rename to frontend/src/containers/TasksList/TaskTable/index.jsx index ddec424..0b06b8d 100644 --- a/frontend/src/components/TaskTable/index.jsx +++ b/frontend/src/containers/TasksList/TaskTable/index.jsx @@ -38,4 +38,10 @@ TaskTable.propTypes = { projectId: PropTypes.string, }; -export default TaskTable; +import { connect } from 'react-redux'; +export default connect( + state => ({ + tasks: state.tasksList.tasks, + projectId: state.router.params.projectId, + }) +)(TaskTable); diff --git a/frontend/src/containers/TasksList/index.jsx b/frontend/src/containers/TasksList/index.jsx index 0930a4a..36bd386 100644 --- a/frontend/src/containers/TasksList/index.jsx +++ b/frontend/src/containers/TasksList/index.jsx @@ -1,10 +1,10 @@ import React from 'react'; -import TaskTable from './TaskTable'; +import TaskTable from './TaskTable/'; -import TaskFilter from './TaskFilter'; -import TaskPager from './TaskPager'; +import TaskFilter from './TaskFilter/'; +import TaskPager from './TaskPager/'; const TaskList = () => (
@@ -15,7 +15,7 @@ const TaskList = () => ( ); import loading from 'containers/Application/loading'; -import { loadTasks } from 'reduxApp/modules/tasks'; -import { fetchUsers } from 'reduxApp/modules/users'; -export default loading([loadTasks, fetchUsers])(TaskList); +import { showPage } from './state'; + +export default loading([showPage])(TaskList); diff --git a/frontend/src/containers/TasksList/state.jsx b/frontend/src/containers/TasksList/state.jsx new file mode 100644 index 0000000..373ddf6 --- /dev/null +++ b/frontend/src/containers/TasksList/state.jsx @@ -0,0 +1,65 @@ + +const initState = { + userId: null, + status: 'new', + + tasks: [], + page: 1, + items: 0, +}; + +export function reducer(state = initState, action) { + switch (action.type) { + case 'CHANGE_USER_ID': + return { ...state, userId: action.payload }; + case 'CHANGE_STATUS': + return { ...state, status: action.payload }; + case 'SET_PAGE': + return { ...state, page: action.payload }; + + case 'SET_TASKS': { + const { count, items } = action.payload; + return { ...state, tasks: items, items: Math.ceil(count / 10) }; + } + + default: + return state; + } +} + +import http, { toParams } from 'utils/http'; + +const setTasks = (payload) => ({ type: 'SET_TASKS', payload }); + +const loadTasks = () => (dispatch, getState) => { + const { + tasksList: { + userId, + status, + page, + }, +// router: { params: { projectId } }, + } = getState(); + const query = toParams({ + assignee: userId, + status, + }); + return http.get(`/api/tasks/page/${page}/5?${query}`) + .then(payload => dispatch(setTasks(payload))); +}; + +const setPage = (page) => ({ type: 'SET_PAGE', payload: page }); + +// PUBLIC + +export const changeUserId = (id) => ({ type: 'CHANGE_USER_ID', payload: id }); +export const changeStatus = (status) => ({ type: 'CHANGE_STATUS', payload: status }); + +export const changePage = (page) => (dispatch) => { + dispatch(setPage(page)); + return dispatch(loadTasks()); +}; + +export const makeSearch = () => (dispatch) => dispatch(loadTasks()); +export const showPage = () => (dispatch) => dispatch(loadTasks()); + diff --git a/frontend/src/containers/UsersList/UsersTable.jsx b/frontend/src/containers/UsersList/UsersTable.jsx deleted file mode 100644 index e91fbe6..0000000 --- a/frontend/src/containers/UsersList/UsersTable.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import { connect } from 'react-redux'; -import UsersTableComponent from 'components/UsersTable/'; -import { removeUser } from 'reduxApp/modules/users'; - -const UsersTable = connect( - state => ({ - users: state.users.users, - currentUser: state.auth.user, - }), - { removeUser } -)(UsersTableComponent); - -export default UsersTable; diff --git a/frontend/src/containers/UsersList/UsersTable/index.jsx b/frontend/src/containers/UsersList/UsersTable/index.jsx new file mode 100644 index 0000000..6accd6d --- /dev/null +++ b/frontend/src/containers/UsersList/UsersTable/index.jsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { Table, Button } from 'react-bootstrap'; + +const UsersTable = ({ + users, removeUser, currentUser, +}) => ( +
+

All users

+ + + + + + + + + + {users.map(user => ( + + + + + + ))} + +
NameEmail
+ {user.name} + + {user.email} + +
+ +
+
+
+); + + +import { connect } from 'react-redux'; +import { removeUser } from '../state'; + + +export default connect( + state => ({ + users: state.app.users, + currentUser: state.auth.user, + }), + { removeUser } +)(UsersTable); diff --git a/frontend/src/containers/UsersList/index.jsx b/frontend/src/containers/UsersList/index.jsx index dc8d107..45e157d 100644 --- a/frontend/src/containers/UsersList/index.jsx +++ b/frontend/src/containers/UsersList/index.jsx @@ -1,6 +1,6 @@ import React from 'react'; -import UsersTable from './UsersTable'; +import UsersTable from './UsersTable/'; import Main from 'components/Layouts/Main'; import DashboardMenu from 'containers/Application/DashboardMenu'; @@ -11,5 +11,4 @@ const UsersList = () => ( ); import loading from 'containers/Application/loading'; -import { fetchUsers } from 'reduxApp/modules/users'; -export default loading(fetchUsers)(UsersList); +export default loading([])(UsersList); diff --git a/frontend/src/containers/UsersList/state.jsx b/frontend/src/containers/UsersList/state.jsx new file mode 100644 index 0000000..38b3adf --- /dev/null +++ b/frontend/src/containers/UsersList/state.jsx @@ -0,0 +1,6 @@ + +import http from 'utils/http'; +import { loadUsers } from 'reduxApp/modules/app'; + +export const removeUser = ({ id }) => (dispatch) => + http.del(`/api/users/${id}`).then(dispatch(loadUsers())); diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index e27e58b..253d200 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -20,12 +20,16 @@ Root.propTypes = { store: PropTypes.object.isRequired, }; +import { appStart } from 'reduxApp/modules/app'; + window.onload = () => { const root = document.getElementById('app'); try { + const store = createStore(); render(( - + ), root); + store.dispatch(appStart()); } catch (e) { const RedBox = require('redbox-react').default; render(, root); diff --git a/frontend/src/reduxApp/modules/app.jsx b/frontend/src/reduxApp/modules/app.jsx new file mode 100644 index 0000000..45d1f29 --- /dev/null +++ b/frontend/src/reduxApp/modules/app.jsx @@ -0,0 +1,33 @@ + +const initState = { + users: [], + statuses: ['new', 'inprogress', 'testing', 'complited'], +}; + +export function reducer(state = initState, action) { + switch (action.type) { + case 'SET_USERS': + return { ...state, users: action.payload }; + + default: + return state; + } +} + +import http from 'utils/http'; + + +const setUsers = (users) => ({ type: 'SET_USERS', payload: users }); + +export const loadUsers = () => (dispatch) => + http.get('/api/users') + .then(users => dispatch(setUsers(users))); + +export const getStatusesOptions = (state) => + state.app.statuses.map(str => ({ value: str, label: str })); + +export const getUserOptions = (state) => + state.app.users.map(user => ({ value: user.id, label: user.name })); + +export const appStart = () => (dispatch) => dispatch(loadUsers()); + diff --git a/frontend/src/reduxApp/modules/comments.jsx b/frontend/src/reduxApp/modules/comments.jsx deleted file mode 100644 index ccbb568..0000000 --- a/frontend/src/reduxApp/modules/comments.jsx +++ /dev/null @@ -1,55 +0,0 @@ - -const initState = { - comments: [], -}; - -export function reducer(state = initState, action) { - switch (action.type) { - case 'SET_COMMENTS': - return { ...state, comments: action.payload }; - - case 'REMOVE_COMMENTS': { - const comments = state.comments.filter(coment => coment.id !== action.payload.id); - return { ...state, comments }; - } - - default: - return state; - } -} - -import http from 'utils/http'; - -function setComments(payload) { - return { - type: 'SET_COMMENTS', - payload, - }; -} - -export function loadComments({ id }) { - // TODO: make load more - return (dispatch) => http.get(`/api/tasks/${id}/comments/page/0/5`) - .then(json => dispatch(setComments(json.items))); -} - - -export function addComment(text) { - return (dispatch, getState) => { - const { - auth: { user: { name } }, - router: { params: { id } }, - } = getState(); - http.post(`/api/tasks/${id}/comments`, { text, userName: name }) - .then(() => dispatch(loadComments({ id }))); - }; -} - -export function removeComent(comment) { - return (dispatch, getState) => { - const { router: { params: { id } } } = getState(); - return http.del(`/api/tasks/${id}/comments/${comment.id}`) - .then(() => dispatch({ type: 'REMOVE_COMMENTS', payload: comment })); - }; -} - diff --git a/frontend/src/reduxApp/modules/reducer.jsx b/frontend/src/reduxApp/modules/reducer.jsx index abad1e8..f20156c 100644 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ b/frontend/src/reduxApp/modules/reducer.jsx @@ -8,8 +8,17 @@ import { reducer as tasks } from './tasks'; import { reducer as users } from './users'; import { reducer as comments } from './comments'; import { reducer as taskFilter } from './taskFilter'; +import { reducer as app } from './app'; +import { reducer as tasksList } from 'containers/TasksList/state'; +import { reducer as tasksDetails } from 'containers/TaskDetails/state'; +import { reducer as projectsList } from 'containers/ProjectsList/state'; export default combineReducers({ + app, + tasksDetails, + tasksList, + projectsList, + taskFilter, comments, users, diff --git a/frontend/src/reduxApp/modules/taskFilter.jsx b/frontend/src/reduxApp/modules/taskFilter.jsx deleted file mode 100644 index fccdc03..0000000 --- a/frontend/src/reduxApp/modules/taskFilter.jsx +++ /dev/null @@ -1,34 +0,0 @@ - -const initState = { - userId: null, - status: 'new', -}; - -export function reducer(state = initState, action) { - switch (action.type) { - case 'CHANGE_TASK_FILTER': { - const { field, value } = action.payload; - return { ...state, [field]: value }; - } - - default: - return state; - } -} - -import { setPage, loadTasks } from 'reduxApp/modules/tasks'; - - -export function changeFilter(field, value) { - return { - type: 'CHANGE_TASK_FILTER', - payload: { field, value }, - }; -} - -export function findTask() { - return (dispatch) => { - dispatch(setPage(1)); - dispatch(loadTasks()); - }; -} diff --git a/frontend/src/utils/http.jsx b/frontend/src/utils/http.jsx index 7dcfb11..324235d 100644 --- a/frontend/src/utils/http.jsx +++ b/frontend/src/utils/http.jsx @@ -2,6 +2,10 @@ var $ = require('jquery'); +const toParams = (obj) => Object.keys(obj) + .filter(key => !!obj[key]) + .map(key => `${key}=${obj[key]}`) + .join('&'); var get = function(url, data){ @@ -36,5 +40,6 @@ var del = function(url){ module.exports = { get : get, post : post, - del : del + del : del, + toParams, } From 94ffbce5be8c843919a351188e26bb70e7694694 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 2 Oct 2016 23:58:37 +0300 Subject: [PATCH 09/18] try new struct --- .../AuthenticatedComponent.jsx | 0 .../Application => HOC}/loading.jsx | 0 frontend/src/components/UsersTable/index.jsx | 42 ------ .../DashboardLayout/DashboardMenu.jsx} | 11 +- .../Application/DashboardLayout/index.jsx | 15 ++ .../containers/Application/DashboardMenu.jsx | 13 -- .../Application/TasksLayout/TasksMenu.jsx} | 13 +- .../index.jsx} | 2 +- .../src/containers/Application/TasksMenu.jsx | 15 -- frontend/src/containers/Login/LoginForm.jsx | 13 -- .../Login/LoginForm}/index.css | 0 .../Login}/LoginForm/index.jsx | 13 +- .../src/containers/Login/RegisterForm.jsx | 12 -- .../Login/RegisterForm}/index.css | 2 +- .../Login}/RegisterForm/index.jsx | 12 +- frontend/src/containers/Login/index.jsx | 4 +- .../ProjectsList/AddProjectModal.jsx | 17 --- .../ProjectsList}/AddProjectModal/index.jsx | 16 ++- .../src/containers/ProjectsList/index.jsx | 13 +- .../src/containers/ProjectsList/state.jsx | 20 +++ .../src/containers/TaskAdd/TaskAddForm.jsx | 15 -- .../TaskAdd}/TaskAddForm/index.css | 0 .../TaskAdd}/TaskAddForm/index.jsx | 34 +++-- frontend/src/containers/TaskAdd/index.jsx | 9 +- frontend/src/containers/TaskAdd/state.jsx | 38 ++++++ frontend/src/containers/TaskDetails/index.jsx | 2 +- frontend/src/containers/TasksList/index.jsx | 2 +- frontend/src/containers/UsersList/index.jsx | 8 +- frontend/src/reduxApp/modules/app.jsx | 7 +- frontend/src/reduxApp/modules/projects.jsx | 87 ------------ frontend/src/reduxApp/modules/reducer.jsx | 12 +- frontend/src/reduxApp/modules/tasks.jsx | 128 ------------------ frontend/src/reduxApp/modules/users.jsx | 36 ----- frontend/src/routes.jsx | 13 +- 34 files changed, 183 insertions(+), 441 deletions(-) rename frontend/src/{containers/Application => HOC}/AuthenticatedComponent.jsx (100%) rename frontend/src/{containers/Application => HOC}/loading.jsx (100%) delete mode 100644 frontend/src/components/UsersTable/index.jsx rename frontend/src/{components/DashboardMenu/index.jsx => containers/Application/DashboardLayout/DashboardMenu.jsx} (74%) create mode 100644 frontend/src/containers/Application/DashboardLayout/index.jsx delete mode 100644 frontend/src/containers/Application/DashboardMenu.jsx rename frontend/src/{components/TasksMenu/index.jsx => containers/Application/TasksLayout/TasksMenu.jsx} (77%) rename frontend/src/containers/Application/{TasksLayout.jsx => TasksLayout/index.jsx} (79%) delete mode 100644 frontend/src/containers/Application/TasksMenu.jsx delete mode 100644 frontend/src/containers/Login/LoginForm.jsx rename frontend/src/{components/RegisterForm => containers/Login/LoginForm}/index.css (100%) rename frontend/src/{components => containers/Login}/LoginForm/index.jsx (85%) delete mode 100644 frontend/src/containers/Login/RegisterForm.jsx rename frontend/src/{components/LoginForm => containers/Login/RegisterForm}/index.css (88%) rename frontend/src/{components => containers/Login}/RegisterForm/index.jsx (85%) delete mode 100644 frontend/src/containers/ProjectsList/AddProjectModal.jsx rename frontend/src/{components => containers/ProjectsList}/AddProjectModal/index.jsx (85%) delete mode 100644 frontend/src/containers/TaskAdd/TaskAddForm.jsx rename frontend/src/{components => containers/TaskAdd}/TaskAddForm/index.css (100%) rename frontend/src/{components => containers/TaskAdd}/TaskAddForm/index.jsx (82%) create mode 100644 frontend/src/containers/TaskAdd/state.jsx delete mode 100644 frontend/src/reduxApp/modules/projects.jsx delete mode 100644 frontend/src/reduxApp/modules/tasks.jsx delete mode 100644 frontend/src/reduxApp/modules/users.jsx diff --git a/frontend/src/containers/Application/AuthenticatedComponent.jsx b/frontend/src/HOC/AuthenticatedComponent.jsx similarity index 100% rename from frontend/src/containers/Application/AuthenticatedComponent.jsx rename to frontend/src/HOC/AuthenticatedComponent.jsx diff --git a/frontend/src/containers/Application/loading.jsx b/frontend/src/HOC/loading.jsx similarity index 100% rename from frontend/src/containers/Application/loading.jsx rename to frontend/src/HOC/loading.jsx diff --git a/frontend/src/components/UsersTable/index.jsx b/frontend/src/components/UsersTable/index.jsx deleted file mode 100644 index 4ba23b6..0000000 --- a/frontend/src/components/UsersTable/index.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import { Table, Button } from 'react-bootstrap'; - -const UsersTable = ({ - users, removeUser, currentUser, -}) => ( -
-

All users

- - - - - - - - - - {users.map(user => ( - - - - - - ))} - -
NameEmail
- {user.name} - - {user.email} - -
- -
-
-
-); - - -export default UsersTable; diff --git a/frontend/src/components/DashboardMenu/index.jsx b/frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx similarity index 74% rename from frontend/src/components/DashboardMenu/index.jsx rename to frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx index a996e14..2aa4e0a 100644 --- a/frontend/src/components/DashboardMenu/index.jsx +++ b/frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx @@ -1,3 +1,4 @@ + import React from 'react'; import { Link } from 'react-router'; import { Navbar, NavBrand, Nav, NavItem } from 'react-bootstrap'; @@ -24,4 +25,12 @@ const DashboardMenu = ({ ); -export default DashboardMenu; +import { connect } from 'react-redux'; + +import { showAddProject } from 'reduxApp/modules/app'; +import { logout } from 'reduxApp/modules/auth'; + +export default connect( + null, + { addProject: showAddProject, logout } +)(DashboardMenu); diff --git a/frontend/src/containers/Application/DashboardLayout/index.jsx b/frontend/src/containers/Application/DashboardLayout/index.jsx new file mode 100644 index 0000000..f670c0b --- /dev/null +++ b/frontend/src/containers/Application/DashboardLayout/index.jsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import DashboardMenu from './DashboardMenu'; +import Main from 'components/Layouts/Main'; + +const TasksLayout = ({ children }) => ( +
} + > + {children} +
+); + + +export default TasksLayout; diff --git a/frontend/src/containers/Application/DashboardMenu.jsx b/frontend/src/containers/Application/DashboardMenu.jsx deleted file mode 100644 index 1984dc6..0000000 --- a/frontend/src/containers/Application/DashboardMenu.jsx +++ /dev/null @@ -1,13 +0,0 @@ - -import DashboardMenuComponent from 'components/DashboardMenu/'; -import { connect } from 'react-redux'; - -import { showAddProject } from 'reduxApp/modules/projects'; -import { logout } from 'reduxApp/modules/auth'; - -const DashboardMenu = connect( - null, - { addProject: showAddProject, logout } -)(DashboardMenuComponent); - -export default DashboardMenu; diff --git a/frontend/src/components/TasksMenu/index.jsx b/frontend/src/containers/Application/TasksLayout/TasksMenu.jsx similarity index 77% rename from frontend/src/components/TasksMenu/index.jsx rename to frontend/src/containers/Application/TasksLayout/TasksMenu.jsx index e074a36..90b41dc 100644 --- a/frontend/src/components/TasksMenu/index.jsx +++ b/frontend/src/containers/Application/TasksLayout/TasksMenu.jsx @@ -1,3 +1,4 @@ + import React from 'react'; import { Link } from 'react-router'; import { Navbar, NavBrand, Nav, NavItem } from 'react-bootstrap'; @@ -28,4 +29,14 @@ const TasksMenu = ({ ); -export default TasksMenu; +import { connect } from 'react-redux'; +import { logout } from 'reduxApp/modules/auth'; + + +export default connect( + state => ({ + projectId: state.router.params.projectId, + router: state.router, + }), + { logout } +)(TasksMenu);; diff --git a/frontend/src/containers/Application/TasksLayout.jsx b/frontend/src/containers/Application/TasksLayout/index.jsx similarity index 79% rename from frontend/src/containers/Application/TasksLayout.jsx rename to frontend/src/containers/Application/TasksLayout/index.jsx index 136cd2e..75ed7c6 100644 --- a/frontend/src/containers/Application/TasksLayout.jsx +++ b/frontend/src/containers/Application/TasksLayout/index.jsx @@ -1,6 +1,6 @@ import React from 'react'; -import TasksMenu from 'containers/Application/TasksMenu'; +import TasksMenu from './TasksMenu'; import Main from 'components/Layouts/Main'; const TasksLayout = ({ children }) => ( diff --git a/frontend/src/containers/Application/TasksMenu.jsx b/frontend/src/containers/Application/TasksMenu.jsx deleted file mode 100644 index 88f78a1..0000000 --- a/frontend/src/containers/Application/TasksMenu.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import { connect } from 'react-redux'; -import { logout } from 'reduxApp/modules/auth'; -import Menu from 'components/TasksMenu/'; - - -const TasksMenu = connect( - state => ({ - projectId: state.router.params.projectId, - router: state.router, - }), - { logout } -)(Menu); - - -export default TasksMenu; diff --git a/frontend/src/containers/Login/LoginForm.jsx b/frontend/src/containers/Login/LoginForm.jsx deleted file mode 100644 index 9ba230a..0000000 --- a/frontend/src/containers/Login/LoginForm.jsx +++ /dev/null @@ -1,13 +0,0 @@ - -import { connect } from 'react-redux'; -import { login } from 'reduxApp/modules/auth'; - -import { LoginForm as LoginFormComponent } from 'components/LoginForm/'; - -const LoginForm = connect( - null, - { login } -)(LoginFormComponent); - - -export default LoginForm; diff --git a/frontend/src/components/RegisterForm/index.css b/frontend/src/containers/Login/LoginForm/index.css similarity index 100% rename from frontend/src/components/RegisterForm/index.css rename to frontend/src/containers/Login/LoginForm/index.css diff --git a/frontend/src/components/LoginForm/index.jsx b/frontend/src/containers/Login/LoginForm/index.jsx similarity index 85% rename from frontend/src/components/LoginForm/index.jsx rename to frontend/src/containers/Login/LoginForm/index.jsx index a1f15ff..a86951e 100644 --- a/frontend/src/components/LoginForm/index.jsx +++ b/frontend/src/containers/Login/LoginForm/index.jsx @@ -1,3 +1,6 @@ + +import { connect } from 'react-redux'; + import React, { PropTypes } from 'react'; import { Button, Input } from 'react-bootstrap'; @@ -36,6 +39,10 @@ LoginForm.propTypes = { login: PropTypes.func, }; -export { - LoginForm, -}; + +import { login } from 'reduxApp/modules/auth'; + +export default connect( + null, + { login } +)(LoginForm); diff --git a/frontend/src/containers/Login/RegisterForm.jsx b/frontend/src/containers/Login/RegisterForm.jsx deleted file mode 100644 index ab21172..0000000 --- a/frontend/src/containers/Login/RegisterForm.jsx +++ /dev/null @@ -1,12 +0,0 @@ - -import { connect } from 'react-redux'; -import { registr } from 'reduxApp/modules/auth'; - -import { RegisterForm as RegisterFormComponent } from 'components/RegisterForm/'; - -const RegisterForm = connect( - null, - { registr } -)(RegisterFormComponent); - -export default RegisterForm; diff --git a/frontend/src/components/LoginForm/index.css b/frontend/src/containers/Login/RegisterForm/index.css similarity index 88% rename from frontend/src/components/LoginForm/index.css rename to frontend/src/containers/Login/RegisterForm/index.css index f184bde..1d79e14 100644 --- a/frontend/src/components/LoginForm/index.css +++ b/frontend/src/containers/Login/RegisterForm/index.css @@ -1,5 +1,5 @@ -.login-form { +.register-form { padding: 10px; border: 1px solid #ddd; border-top: none; diff --git a/frontend/src/components/RegisterForm/index.jsx b/frontend/src/containers/Login/RegisterForm/index.jsx similarity index 85% rename from frontend/src/components/RegisterForm/index.jsx rename to frontend/src/containers/Login/RegisterForm/index.jsx index e449f7f..da91957 100644 --- a/frontend/src/components/RegisterForm/index.jsx +++ b/frontend/src/containers/Login/RegisterForm/index.jsx @@ -1,3 +1,7 @@ + +import { connect } from 'react-redux'; +import { registr } from 'reduxApp/modules/auth'; + import React, { Component, PropTypes } from 'react'; import { Button, Input } from 'react-bootstrap'; @@ -28,7 +32,7 @@ class RegisterForm extends Component { } } - -export { - RegisterForm, -}; +export default connect( + null, + { registr } +)(RegisterForm); diff --git a/frontend/src/containers/Login/index.jsx b/frontend/src/containers/Login/index.jsx index feaba6b..b87d6a4 100644 --- a/frontend/src/containers/Login/index.jsx +++ b/frontend/src/containers/Login/index.jsx @@ -1,8 +1,8 @@ import React from 'react'; import LoginLayout from 'components/Layouts/Login'; -import LoginForm from './LoginForm'; -import RegisterForm from './RegisterForm'; +import LoginForm from './LoginForm/'; +import RegisterForm from './RegisterForm/'; const Login = () => ( ({ - show: state.projects.popupOpen, - users: state.app.users, - }), - { onHide: closePopup, addProject } -)(AddProjectModalComponent); - -export default AddProjectModal; diff --git a/frontend/src/components/AddProjectModal/index.jsx b/frontend/src/containers/ProjectsList/AddProjectModal/index.jsx similarity index 85% rename from frontend/src/components/AddProjectModal/index.jsx rename to frontend/src/containers/ProjectsList/AddProjectModal/index.jsx index 98ad260..ad2154a 100644 --- a/frontend/src/components/AddProjectModal/index.jsx +++ b/frontend/src/containers/ProjectsList/AddProjectModal/index.jsx @@ -61,5 +61,19 @@ const AddProjectModal = React.createClass({ }, }); -export default AddProjectModal; +import { connect } from 'react-redux'; + + +import { + closePopup, addProject, +} from '../state'; + + +export default connect( + state => ({ + show: state.projectsList.popupOpen, + users: state.app.users, + }), + { onHide: closePopup, addProject } +)(AddProjectModal); diff --git a/frontend/src/containers/ProjectsList/index.jsx b/frontend/src/containers/ProjectsList/index.jsx index 7caad73..8a3fd30 100644 --- a/frontend/src/containers/ProjectsList/index.jsx +++ b/frontend/src/containers/ProjectsList/index.jsx @@ -1,21 +1,16 @@ import React from 'react'; import ProjectsList from './ProjectsList/'; -import AddProjectModal from './AddProjectModal'; - -import DashboardMenu from 'containers/Application/DashboardMenu'; -import Main from 'components/Layouts/Main'; +import AddProjectModal from './AddProjectModal/'; const ProjectsListPage = () => ( -
} - > +
-
+
); -import loading from 'containers/Application/loading'; +import loading from 'HOC/loading'; import { showPage } from './state'; export default loading([showPage])(ProjectsListPage); diff --git a/frontend/src/containers/ProjectsList/state.jsx b/frontend/src/containers/ProjectsList/state.jsx index 39af34d..475754a 100644 --- a/frontend/src/containers/ProjectsList/state.jsx +++ b/frontend/src/containers/ProjectsList/state.jsx @@ -5,6 +5,12 @@ const initState = { export const reducer = (state = initState, action) => { switch (action.type) { + case 'PROJECTS_OPEN_POPUP': + return { ...state, popupOpen: true }; + + case 'PROJECTS_CLOSE_POPUP': + return { ...state, popupOpen: false }; + case 'SET_PROJECTS': return { ...state, projects: action.payload }; @@ -26,3 +32,17 @@ export const showPage = () => (dispatch) => dispatch(loadProjects()); export const removeProject = ({ id }) => (dispatch) => http.del(`/api/projects/${id}`).then(() => dispatch(loadProjects())); + + +export const openPopup = () => ({ type: 'PROJECTS_OPEN_POPUP' }); +export const closePopup = () => ({ type: 'PROJECTS_CLOSE_POPUP' }); + +export const addProject = (title, userIds) => (dispatch) => + http.post('/api/projects', { title }) + .then(payload => { + dispatch({ + type: 'ADD_PROJECT', + payload, + }); + dispatch(closePopup()); + }); diff --git a/frontend/src/containers/TaskAdd/TaskAddForm.jsx b/frontend/src/containers/TaskAdd/TaskAddForm.jsx deleted file mode 100644 index 78fcdd0..0000000 --- a/frontend/src/containers/TaskAdd/TaskAddForm.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import TaskAddFormComponent from 'components/TaskAddForm/'; -import { connect } from 'react-redux'; -import { addTask, addVersion } from 'reduxApp/modules/tasks'; - -const TaskAddForm = connect( - state => ({ - statuses: state.tasks.statuses, - users: state.users.users, - versions: state.tasks.versions, - assignee: state.auth.user ? state.auth.user.id : null, - }) -, { addTask, addVersion })(TaskAddFormComponent); - - -export default TaskAddForm; diff --git a/frontend/src/components/TaskAddForm/index.css b/frontend/src/containers/TaskAdd/TaskAddForm/index.css similarity index 100% rename from frontend/src/components/TaskAddForm/index.css rename to frontend/src/containers/TaskAdd/TaskAddForm/index.css diff --git a/frontend/src/components/TaskAddForm/index.jsx b/frontend/src/containers/TaskAdd/TaskAddForm/index.jsx similarity index 82% rename from frontend/src/components/TaskAddForm/index.jsx rename to frontend/src/containers/TaskAdd/TaskAddForm/index.jsx index ada1a35..a57c8b6 100644 --- a/frontend/src/components/TaskAddForm/index.jsx +++ b/frontend/src/containers/TaskAdd/TaskAddForm/index.jsx @@ -17,7 +17,7 @@ class TaskAddForm extends Component { const title = this.refs.input.getValue(); const description = this.refs.description.getValue(); const { status, assignee, version } = this.state; - const assigneeUser = users.find(user => user.id === assignee); + const assigneeUser = users.find(user => user.value === assignee); addTask({ title, @@ -25,19 +25,12 @@ class TaskAddForm extends Component { status, assignee, version, - assigneeName: assigneeUser.name, + assigneeName: assigneeUser.label, }); } render() { const { statuses, users, addVersion, versions } = this.props; - const options = statuses.map(status => ( - { value: status, label: status } - )); - - const allUsers = users.map(user => ( - { value: user.id, label: user.name }) - ); const allVersion = versions.map(version => ({ value: version.title, label: version.title })); @@ -52,7 +45,7 @@ class TaskAddForm extends Component { searchable={false} clearable={false} placeholder='-' - options={options} + options={statuses} />
@@ -61,7 +54,7 @@ class TaskAddForm extends Component { value={this.state.assignee} onChange={assignee => this.setState({ assignee })} placeholder='-' - options={allUsers} + options={users} />
@@ -91,4 +84,21 @@ class TaskAddForm extends Component { } } -export default TaskAddForm; + +import { connect } from 'react-redux'; +import { addTask, addVersion } from '../state'; + + +import { + getStatusesOptions, + getUserOptions, +} from 'reduxApp/modules/app'; + +export default connect( + state => ({ + statuses: getStatusesOptions(state), + users: getUserOptions(state), + versions: state.tasksAdd.versions, + assignee: state.auth.user ? state.auth.user.id : null, + }) +, { addTask, addVersion })(TaskAddForm); diff --git a/frontend/src/containers/TaskAdd/index.jsx b/frontend/src/containers/TaskAdd/index.jsx index 7518d82..2a9f766 100644 --- a/frontend/src/containers/TaskAdd/index.jsx +++ b/frontend/src/containers/TaskAdd/index.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import TaskAddForm from './TaskAddForm'; +import TaskAddForm from './TaskAddForm/'; const TaskAdd = () => (
@@ -7,8 +7,7 @@ const TaskAdd = () => (
); -import loading from 'containers/Application/loading'; -import { loadVersions } from 'reduxApp/modules/tasks'; -import { fetchUsers } from 'reduxApp/modules/users'; +import loading from 'HOC/loading'; +import { showPage } from './state'; -export default loading([loadVersions, fetchUsers])(TaskAdd); +export default loading([showPage])(TaskAdd); diff --git a/frontend/src/containers/TaskAdd/state.jsx b/frontend/src/containers/TaskAdd/state.jsx new file mode 100644 index 0000000..f5d38b8 --- /dev/null +++ b/frontend/src/containers/TaskAdd/state.jsx @@ -0,0 +1,38 @@ +const initState = { + versions: [], +}; + + +export const reducer = (state = initState, action) => { + switch (action.type) { + case 'SET_VERSIONS': + return { ...state, versions: action.payload }; + + default: + return state; + } +}; + +import http from 'utils/http'; +import { push } from 'redux-router'; + +export const showPage = () => (dispatch) => http.get('/api/version') + .then(data => dispatch({ type: 'SET_VERSIONS', payload: data })); + + +export function addVersion() { + return (dispatch) => { + const title = prompt('Create new Version', ''); + if (title) { + http.post('/api/version', { title }) + .then(newVersion => dispatch({ type: 'ADD_VERSION', payload: newVersion })); + } + }; +} + +export function addTask(form) { + return (dispatch, getState) => { + const { router: { params: { projectId } } } = getState(); + http.post('/api/tasks', form).then(() => dispatch(push(`/projects/${projectId}/tasks`))); + }; +} diff --git a/frontend/src/containers/TaskDetails/index.jsx b/frontend/src/containers/TaskDetails/index.jsx index d6fc169..aa90264 100644 --- a/frontend/src/containers/TaskDetails/index.jsx +++ b/frontend/src/containers/TaskDetails/index.jsx @@ -12,7 +12,7 @@ const TaskDetails = () => (
); -import loading from 'containers/Application/loading'; +import loading from 'HOC/loading'; import { showPage } from './state'; export default loading([showPage])(TaskDetails); diff --git a/frontend/src/containers/TasksList/index.jsx b/frontend/src/containers/TasksList/index.jsx index 36bd386..786543f 100644 --- a/frontend/src/containers/TasksList/index.jsx +++ b/frontend/src/containers/TasksList/index.jsx @@ -14,7 +14,7 @@ const TaskList = () => (
); -import loading from 'containers/Application/loading'; +import loading from 'HOC/loading'; import { showPage } from './state'; diff --git a/frontend/src/containers/UsersList/index.jsx b/frontend/src/containers/UsersList/index.jsx index 45e157d..a6c6c89 100644 --- a/frontend/src/containers/UsersList/index.jsx +++ b/frontend/src/containers/UsersList/index.jsx @@ -1,14 +1,12 @@ import React from 'react'; import UsersTable from './UsersTable/'; -import Main from 'components/Layouts/Main'; -import DashboardMenu from 'containers/Application/DashboardMenu'; const UsersList = () => ( -
}> +
-
+
); -import loading from 'containers/Application/loading'; +import loading from 'HOC/loading'; export default loading([])(UsersList); diff --git a/frontend/src/reduxApp/modules/app.jsx b/frontend/src/reduxApp/modules/app.jsx index 45d1f29..ced8ce7 100644 --- a/frontend/src/reduxApp/modules/app.jsx +++ b/frontend/src/reduxApp/modules/app.jsx @@ -15,7 +15,8 @@ export function reducer(state = initState, action) { } import http from 'utils/http'; - +import { push } from 'redux-router'; +import { openPopup } from 'containers/ProjectsList/state'; const setUsers = (users) => ({ type: 'SET_USERS', payload: users }); @@ -31,3 +32,7 @@ export const getUserOptions = (state) => export const appStart = () => (dispatch) => dispatch(loadUsers()); +export const showAddProject = () => (dispatch) => { + dispatch(push('/')); + dispatch(openPopup()); +}; diff --git a/frontend/src/reduxApp/modules/projects.jsx b/frontend/src/reduxApp/modules/projects.jsx deleted file mode 100644 index f54643a..0000000 --- a/frontend/src/reduxApp/modules/projects.jsx +++ /dev/null @@ -1,87 +0,0 @@ - -const initState = { - projects: [], - popupOpen: false, -}; - -export function reducer(state = initState, action) { - switch (action.type) { - case 'FETCH_PRODUCTS': - return { ...state, projects: action.payload }; - - case 'REMOVE_PROJECT': { - const projects = state.projects.filter(product => product.id !== action.payload.id); - return { ...state, projects }; - } - - case 'PROJECTS_OPEN_POPUP': - return { ...state, popupOpen: true }; - - case 'PROJECTS_CLOSE_POPUP': - return { ...state, popupOpen: false }; - - case 'ADD_PROJECT': { - const projects = state.projects.concat([action.payload]); - return { ...state, projects }; - } - - default: - return state; - } -} - -import http from 'utils/http'; -import { push } from 'redux-router'; - -export function loadProducts() { - return (dispatch) => http.get('/api/projects') - .then(payload => dispatch({ - type: 'FETCH_PRODUCTS', - payload, - })); -} - - -export function openPopup() { - return { - type: 'PROJECTS_OPEN_POPUP', - }; -} - -export function closePopup() { - return { - type: 'PROJECTS_CLOSE_POPUP', - }; -} - - -export function removeProject(product) { - return (dispatch) => - http.del(`/api/projects/${product.id}`) - .then(() => dispatch({ - type: 'REMOVE_PROJECT', - payload: product, - })); -} - -export function showAddProject() { - return (dispatch) => { - dispatch(push('/')); - dispatch(openPopup()); - }; -} - -export function addProject(title, userIds) { - return (dispatch) => { - http.post('/api/projects', { title }) - // .then(project => http.post(`/api/projects/${project.id}/users`, { users: userIds })) - .then(payload => { - dispatch({ - type: 'ADD_PROJECT', - payload, - }); - dispatch(closePopup()); - // this.setState({ title : '', userIds : [] }) - }); - }; -} diff --git a/frontend/src/reduxApp/modules/reducer.jsx b/frontend/src/reduxApp/modules/reducer.jsx index f20156c..f9265d3 100644 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ b/frontend/src/reduxApp/modules/reducer.jsx @@ -3,12 +3,8 @@ import { routerStateReducer } from 'redux-router'; import { reducer as formReducer } from 'redux-form'; import { reducer as auth } from './auth'; -import { reducer as projects } from './projects'; -import { reducer as tasks } from './tasks'; -import { reducer as users } from './users'; -import { reducer as comments } from './comments'; -import { reducer as taskFilter } from './taskFilter'; import { reducer as app } from './app'; +import { reducer as tasksAdd } from 'containers/TaskAdd/state'; import { reducer as tasksList } from 'containers/TasksList/state'; import { reducer as tasksDetails } from 'containers/TaskDetails/state'; import { reducer as projectsList } from 'containers/ProjectsList/state'; @@ -18,12 +14,8 @@ export default combineReducers({ tasksDetails, tasksList, projectsList, + tasksAdd, - taskFilter, - comments, - users, - projects, - tasks, auth, router: routerStateReducer, form: formReducer, diff --git a/frontend/src/reduxApp/modules/tasks.jsx b/frontend/src/reduxApp/modules/tasks.jsx deleted file mode 100644 index e1fc875..0000000 --- a/frontend/src/reduxApp/modules/tasks.jsx +++ /dev/null @@ -1,128 +0,0 @@ - -const initState = { - tasks: [], - task: null, - statuses: ['new', 'inprogress', 'testing', 'complited'], - versions: [], - page: 1, - items: 0, -}; - -export function reducer(state = initState, action) { - switch (action.type) { - case 'SET_TASKS': { - const { count, items } = action.payload; - return { ...state, tasks: items, items: Math.ceil(count / 10) }; - } - - case 'SET_TASK': - return { ...state, task: action.payload }; - - case 'SET_VERSIONS': - return { ...state, versions: action.payload }; - - case 'SET_TASKS_PAGE': - return { ...state, page: action.payload }; - - case 'ADD_VERSION': - return { ...state, versions: state.versions.concat([action.payload]) }; - - default: - return state; - } -} - -import { push } from 'redux-router'; -import http from 'utils/http'; - -function setTasks(payload) { - return { - type: 'SET_TASKS', - payload, - }; -} - -function setTask(payload) { - return { - type: 'SET_TASK', - payload, - }; -} - -const toParams = (obj) => Object.keys(obj) - .filter(key => !!obj[key]) - .map(key => `${key}=${obj[key]}`) - .join('&'); - -// TODO: page leavel loadTasks(params) -export function loadTasks() { - return (dispatch, getState) => { - const { userId, status } = getState().taskFilter; - const paramsStr = toParams({ - assignee: userId, - status, - }); - const page = getState().tasks.page - 1; - return http.get(`/api/tasks/page/${page}/5?${paramsStr}`) - .then(({ items, count }) => dispatch(setTasks({ items, count }))); - }; -} - -export function setPage(page) { - return { - type: 'SET_TASKS_PAGE', - payload: page, - }; -} - -export function changeTaskPage(e, data) { - return (dispatch) => { - dispatch(setPage(data.eventKey)); - dispatch(loadTasks()); - }; -} - -export function addTask(form) { - return (dispatch, getState) => { - const { router: { params: { projectId } } } = getState(); - http.post('/api/tasks', form).then(() => dispatch(push(`/projects/${projectId}/tasks`))); - }; -} - -export function removeTask() { - return (dispatch, getState) => { - const { router: { params: { id, projectId } } } = getState(); - http.del(`/api/tasks/${id}`).then(() => dispatch(push(`/projects/${projectId}/tasks`))); - }; -} - -export function loadTask({ id }) { - return (dispatch, getState) => http.get(`/api/tasks/${id}`) - .then(data => dispatch(setTask(data))); -} - - -export function addVersion() { - return (dispatch) => { - const title = prompt('Create new Version', ''); - if (title) { - http.post('/api/version', { title }) - .then(newVersion => dispatch({ type: 'ADD_VERSION', payload: newVersion })); - } - }; -} - -export function loadVersions() { - return (dispatch) => http.get('/api/version') - .then(data => dispatch({ type: 'SET_VERSIONS', payload: data })); -} - -// export function showAddTaskForm() { -// return (dispatch) => { -// const users = dispatch(fetchUsers()); -// const versions = http.get('/api/version') -// .then(data => dispatch({ type: 'SET_VERSIONS', payload: data })); -// return Promise.all([users, versions]); -// }; -// } - diff --git a/frontend/src/reduxApp/modules/users.jsx b/frontend/src/reduxApp/modules/users.jsx deleted file mode 100644 index 992186d..0000000 --- a/frontend/src/reduxApp/modules/users.jsx +++ /dev/null @@ -1,36 +0,0 @@ - -const initState = { - users: [], -}; - -export function reducer(state = initState, action) { - switch (action.type) { - case 'FETCH_USERS': - return { ...state, users: action.payload }; - - case 'REMOVE_USER': { - const users = state.users.filter(user => user.id !== action.payload.id); - return { ...state, users }; - } - - default: - return state; - } -} - -import http from 'utils/http'; - -export function fetchUsers() { - return (dispatch) => http.get('/api/users') - .then(payload => dispatch({ - type: 'FETCH_USERS', - payload, - })); -} - -export function removeUser(user) { - return (dispatch) => http.del(`/api/users/${user.id}`) - .then(() => { - dispatch({ type: 'REMOVE_USER', payload: user }); - }); -} diff --git a/frontend/src/routes.jsx b/frontend/src/routes.jsx index 420ec4d..ad6b8be 100644 --- a/frontend/src/routes.jsx +++ b/frontend/src/routes.jsx @@ -4,10 +4,11 @@ import { Router, Route, IndexRoute } from 'react-router'; import Login from 'containers/Login/'; import ProjectsList from 'containers/ProjectsList/'; -import { requireAuthentication } from 'containers/Application/AuthenticatedComponent'; +import { requireAuthentication } from 'HOC/AuthenticatedComponent'; import UsersList from 'containers/UsersList/'; import NoMatch from 'containers/NoMatch/'; -import TasksLayout from 'containers/Application/TasksLayout'; +import TasksLayout from 'containers/Application/TasksLayout/'; +import DashboardLayout from 'containers/Application/DashboardLayout/'; import TasksList from 'containers/TasksList/'; import TaskAdd from 'containers/TaskAdd/'; import TaskDetails from 'containers/TaskDetails'; @@ -19,14 +20,16 @@ module.exports = ({ dispatch, getState }) => ( - - - + + + + + From 4a66d4b790ffff9bbc193d08852134268a19cd10 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 5 Oct 2016 11:27:05 +0300 Subject: [PATCH 10/18] axios --- frontend/package.json | 2 + .../src/containers/ProjectsList/state.jsx | 50 +++++++++---- frontend/src/containers/TaskAdd/state.jsx | 41 +++++++++-- frontend/src/containers/TaskDetails/state.jsx | 66 ++++++++++++----- frontend/src/containers/TasksList/state.jsx | 16 +++-- frontend/src/containers/UsersList/state.jsx | 13 +++- frontend/src/reduxApp/create.jsx | 14 +++- frontend/src/reduxApp/modules/app.jsx | 14 ++-- frontend/src/reduxApp/modules/auth.jsx | 71 +++++++++++-------- frontend/src/utils/http.jsx | 45 ------------ 10 files changed, 203 insertions(+), 129 deletions(-) delete mode 100644 frontend/src/utils/http.jsx diff --git a/frontend/package.json b/frontend/package.json index 1641887..9c41f9d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "author": "", "license": "ISC", "dependencies": { + "axios": "^0.14.0", "babel-core": "^6.3.26", "babel-eslint": "6.0.4", "babel-loader": "^6.2.1", @@ -47,6 +48,7 @@ "react-slider": "^0.5.1", "redbox-react": "1.3.0", "redux": "3.5.2", + "redux-axios-middleware": "^3.0.0", "redux-form": "5.2.3", "redux-router": "2.0.0", "redux-thunk": "2.0.1", diff --git a/frontend/src/containers/ProjectsList/state.jsx b/frontend/src/containers/ProjectsList/state.jsx index 475754a..cd8670c 100644 --- a/frontend/src/containers/ProjectsList/state.jsx +++ b/frontend/src/containers/ProjectsList/state.jsx @@ -11,7 +11,7 @@ export const reducer = (state = initState, action) => { case 'PROJECTS_CLOSE_POPUP': return { ...state, popupOpen: false }; - case 'SET_PROJECTS': + case 'LOAD_PRODUCTS_SUCCESS': return { ...state, projects: action.payload }; default: @@ -19,30 +19,50 @@ export const reducer = (state = initState, action) => { } }; -import http from 'utils/http'; -const setProjects = (projects) => ({ type: 'SET_PROJECTS', payload: projects }); +const getAllProjects = () => ({ + type: 'LOAD_PRODUCTS', + payload: { + request: '/api/projects', + }, +}); -const loadProjects = () => (dispatch) => - http.get('/api/projects').then(json => dispatch(setProjects(json))); +const deleteProjects = (id) => ({ + type: 'DELETE_PRODUCT', + payload: { + request: { + url: `/api/projects/${id}`, + method: 'delete', + }, + }, +}); + +const createProject = (data) => ({ + type: 'CREATE_PROJECT', + payload: { + request: { + url: '/api/projects', + data, + method: 'post', + }, + }, +}); + + +const loadProjects = () => (dispatch) => dispatch(getAllProjects()); // Publick export const showPage = () => (dispatch) => dispatch(loadProjects()); -export const removeProject = ({ id }) => (dispatch) => - http.del(`/api/projects/${id}`).then(() => dispatch(loadProjects())); +export const removeProject = ({ id }) => (dispatch) => dispatch(deleteProjects(id)) + .then(() => dispatch(loadProjects())); export const openPopup = () => ({ type: 'PROJECTS_OPEN_POPUP' }); export const closePopup = () => ({ type: 'PROJECTS_CLOSE_POPUP' }); export const addProject = (title, userIds) => (dispatch) => - http.post('/api/projects', { title }) - .then(payload => { - dispatch({ - type: 'ADD_PROJECT', - payload, - }); - dispatch(closePopup()); - }); + dispatch(createProject({ title })) + .then(() => dispatch(getAllProjects())) + .then(() => dispatch(closePopup())); diff --git a/frontend/src/containers/TaskAdd/state.jsx b/frontend/src/containers/TaskAdd/state.jsx index f5d38b8..fa10d4a 100644 --- a/frontend/src/containers/TaskAdd/state.jsx +++ b/frontend/src/containers/TaskAdd/state.jsx @@ -5,7 +5,7 @@ const initState = { export const reducer = (state = initState, action) => { switch (action.type) { - case 'SET_VERSIONS': + case 'LOAD_VERSIONS_SUCCESS': return { ...state, versions: action.payload }; default: @@ -13,19 +13,46 @@ export const reducer = (state = initState, action) => { } }; -import http from 'utils/http'; import { push } from 'redux-router'; -export const showPage = () => (dispatch) => http.get('/api/version') - .then(data => dispatch({ type: 'SET_VERSIONS', payload: data })); +const getAllVersions = () => ({ + type: 'LOAD_VERSIONS', + payload: { + request: '/api/version', + }, +}); + +const createVersion = (data) => ({ + type: 'CREATE_VERSION', + payload: { + request: { + url: '/api/version', + method: 'post', + data, + }, + }, +}); + +const createTask = (data) => ({ + type: 'CREATE_TASK', + payload: { + request: { + url: '/api/tasks', + method: 'post', + data, + }, + }, +}); + +export const showPage = () => (dispatch) => dispatch(getAllVersions()); export function addVersion() { return (dispatch) => { const title = prompt('Create new Version', ''); if (title) { - http.post('/api/version', { title }) - .then(newVersion => dispatch({ type: 'ADD_VERSION', payload: newVersion })); + dispatch(createVersion({ title })) + .then(() => dispatch(getAllVersions())); } }; } @@ -33,6 +60,6 @@ export function addVersion() { export function addTask(form) { return (dispatch, getState) => { const { router: { params: { projectId } } } = getState(); - http.post('/api/tasks', form).then(() => dispatch(push(`/projects/${projectId}/tasks`))); + dispatch(createTask(form)).then(() => dispatch(push(`/projects/${projectId}/tasks`))); }; } diff --git a/frontend/src/containers/TaskDetails/state.jsx b/frontend/src/containers/TaskDetails/state.jsx index e650e87..25abd88 100644 --- a/frontend/src/containers/TaskDetails/state.jsx +++ b/frontend/src/containers/TaskDetails/state.jsx @@ -6,34 +6,67 @@ const initState = { export const reducer = (state = initState, action) => { switch (action.type) { - case 'SET_COMMENTS': - return { ...state, comments: action.payload }; - case 'SET_TASK': + case 'LOAD_COMMENTS_SUCCESS': + return { ...state, comments: action.payload.items }; + case 'LOAD_TASK_SUCCESS': return { ...state, task: action.payload }; default: return state; } }; -import http from 'utils/http'; import { push } from 'redux-router'; -const setComments = (comments) => ({ type: 'SET_COMMENTS', payload: comments }); -const setTask = (task) => ({ type: 'SET_TASK', payload: task }); +const loadComments = (id) => ({ + type: 'LOAD_COMMENTS', + payload: { + request: `/api/tasks/${id}/comments/page/0/5`, + }, +}); -const loadComments = (id) => (dispatch) => - http.get(`/api/tasks/${id}/comments/page/0/5`) - .then(json => dispatch(setComments(json.items))); +const loadTask = (id) => ({ + type: 'LOAD_TASK', + payload: { + request: `/api/tasks/${id}`, + }, +}); -const loadTask = (id) => (dispatch) => - http.get(`/api/tasks/${id}`) - .then(json => dispatch(setTask(json))); +const deleteTaskApiCall = (id) => ({ + type: 'DELETE_TASK', + payload: { + request: { + url: `/api/tasks/${id}`, + method: 'delete', + }, + }, +}); -// PUBLICK +const deleteCommentApiCall = (taskId, commentId) => ({ + type: 'DEELTE_COMMENT', + payload: { + request: { + url: `/api/tasks/${taskId}/comments/${commentId}`, + method: 'delete', + }, + }, +}); + +const addCommentApiCall = (taskId, data) => ({ + type: 'ADD_COMMENT', + payload: { + request: { + url: `/api/tasks/${taskId}/comments`, + method: 'post', + data, + }, + }, +}); +// PUBLICK export const deleteTask = () => (dispatch, getState) => { const { router: { params: { id, projectId } } } = getState(); - return http.del(`/api/tasks/${id}`).then(() => dispatch(push(`/projects/${projectId}/tasks`))); + return dispatch(deleteTaskApiCall(id)) + .then(() => dispatch(push(`/projects/${projectId}/tasks`))); }; export const addComment = (text) => (dispatch, getState) => { @@ -41,13 +74,14 @@ export const addComment = (text) => (dispatch, getState) => { auth: { user: { name } }, router: { params: { id } }, } = getState(); - return http.post(`/api/tasks/${id}/comments`, { text, userName: name }) + + return dispatch(addCommentApiCall(id, { text, userName: name })) .then(() => dispatch(loadComments(id))); }; export const deleteComment = (comment) => (dispatch, getState) => { const { router: { params: { id } } } = getState(); - return http.del(`/api/tasks/${id}/comments/${comment.id}`) + return dispatch(deleteCommentApiCall(id, comment.id)) .then(() => dispatch(loadComments(id))); }; diff --git a/frontend/src/containers/TasksList/state.jsx b/frontend/src/containers/TasksList/state.jsx index 373ddf6..615be41 100644 --- a/frontend/src/containers/TasksList/state.jsx +++ b/frontend/src/containers/TasksList/state.jsx @@ -10,6 +10,7 @@ const initState = { export function reducer(state = initState, action) { switch (action.type) { + case 'CHANGE_USER_ID': return { ...state, userId: action.payload }; case 'CHANGE_STATUS': @@ -17,7 +18,7 @@ export function reducer(state = initState, action) { case 'SET_PAGE': return { ...state, page: action.payload }; - case 'SET_TASKS': { + case 'LOAD_TASKS_SUCCESS': { const { count, items } = action.payload; return { ...state, tasks: items, items: Math.ceil(count / 10) }; } @@ -27,9 +28,7 @@ export function reducer(state = initState, action) { } } -import http, { toParams } from 'utils/http'; - -const setTasks = (payload) => ({ type: 'SET_TASKS', payload }); +import { toParams } from 'utils/http'; const loadTasks = () => (dispatch, getState) => { const { @@ -44,8 +43,13 @@ const loadTasks = () => (dispatch, getState) => { assignee: userId, status, }); - return http.get(`/api/tasks/page/${page}/5?${query}`) - .then(payload => dispatch(setTasks(payload))); + + return dispatch({ + type: 'LOAD_TASKS', + payload: { + request: `/api/tasks/page/${page}/5?${query}`, + }, + }); }; const setPage = (page) => ({ type: 'SET_PAGE', payload: page }); diff --git a/frontend/src/containers/UsersList/state.jsx b/frontend/src/containers/UsersList/state.jsx index 38b3adf..bcf8cd2 100644 --- a/frontend/src/containers/UsersList/state.jsx +++ b/frontend/src/containers/UsersList/state.jsx @@ -1,6 +1,15 @@ -import http from 'utils/http'; +const deleteUser = (id) => ({ + type: 'DELETE_USER', + payload: { + request: { + url: `/api/users/${id}`, + method: 'delete', + }, + }, +}); + import { loadUsers } from 'reduxApp/modules/app'; export const removeUser = ({ id }) => (dispatch) => - http.del(`/api/users/${id}`).then(dispatch(loadUsers())); + dispatch(deleteUser(id)).then(dispatch(loadUsers())); diff --git a/frontend/src/reduxApp/create.jsx b/frontend/src/reduxApp/create.jsx index 0003a8a..aac3ab6 100644 --- a/frontend/src/reduxApp/create.jsx +++ b/frontend/src/reduxApp/create.jsx @@ -8,8 +8,20 @@ import { applyMiddleware, compose, createStore as _createStore } from 'redux'; import thunk from 'redux-thunk'; import rootReducer from './modules/reducer'; +import axios from 'axios'; +import axiosMiddleware from 'redux-axios-middleware'; + +const client = axios.create({ + responseType: 'json', +}); + +client.interceptors.response.use( + (response) => response.data, + (error) => Promise.reject(error.response.data), +); + export default function createStore() { - const middleware = applyMiddleware(thunk); + const middleware = applyMiddleware(thunk, axiosMiddleware(client)); const createHistoryCustom = (options) => useScroll(createHistory({ queryKey: false, ...options, diff --git a/frontend/src/reduxApp/modules/app.jsx b/frontend/src/reduxApp/modules/app.jsx index ced8ce7..eecfb35 100644 --- a/frontend/src/reduxApp/modules/app.jsx +++ b/frontend/src/reduxApp/modules/app.jsx @@ -6,7 +6,7 @@ const initState = { export function reducer(state = initState, action) { switch (action.type) { - case 'SET_USERS': + case 'LOAD_USERS_SUCCESS': return { ...state, users: action.payload }; default: @@ -14,15 +14,17 @@ export function reducer(state = initState, action) { } } -import http from 'utils/http'; import { push } from 'redux-router'; import { openPopup } from 'containers/ProjectsList/state'; -const setUsers = (users) => ({ type: 'SET_USERS', payload: users }); +const getAllUsers = () => ({ + type: 'LOAD_USERS', + payload: { + request: '/api/users', + }, +}); -export const loadUsers = () => (dispatch) => - http.get('/api/users') - .then(users => dispatch(setUsers(users))); +export const loadUsers = () => (dispatch) => dispatch(getAllUsers()); export const getStatusesOptions = (state) => state.app.statuses.map(str => ({ value: str, label: str })); diff --git a/frontend/src/reduxApp/modules/auth.jsx b/frontend/src/reduxApp/modules/auth.jsx index 80603dc..ca8c3d4 100644 --- a/frontend/src/reduxApp/modules/auth.jsx +++ b/frontend/src/reduxApp/modules/auth.jsx @@ -18,8 +18,6 @@ export function reducer(state = initState, action) { } } -import http from 'utils/http'; - import { push } from 'redux-router'; @@ -30,36 +28,47 @@ export function startSession(payload) { }; } -export function logout() { - return (dispatch) => { - http.del('/api/session').then(() => { - dispatch({ type: 'LOGOUT' }); - dispatch(push('/login')); - }); - }; -} +export const logout = () => dispatch => + dispatch({ + types: ['LOGOUT', 'LOGOUT'], + payload: { + request: { + url: '/api/session', + mehtod: 'delete', + }, + }, + }).then(() => dispatch(push('/login'))); -export function login(form) { - return (dispatch) => http.post('/api/login', form) - .then(user => { - dispatch(startSession(user)); - dispatch(push('/')); - }); -} -export function registr(form) { - return (dispatch) => http.post('/api/users', form) - .then(user => { - dispatch(login(form)); - }); -} +export const login = (form) => (dispatch) => + dispatch({ + types: ['START_SESSION', 'LOGIN_FAIL'], + payload: { + request: { + url: '/api/login', + method: 'post', + data: form, + }, + }, + }).then(() => dispatch(push('/'))); -export function checkAuth() { - return (dispatch) => { - http.get('/api/session') - .then(user => dispatch(startSession(user))) - .fail(() => dispatch(logout())); - }; -} -// TODO +export const registr = (form) => dispatch => + dispatch({ + type: 'REGISTR', + payload: { + request: { + url: '/api/users', + method: 'post', + }, + }, + }).then(data => dispatch(login(data))); + + +export const checkAuth = () => ({ + types: ['START_SESSION', 'LOGOUT'], + payload: { + request: '/api/session', + }, +}); + diff --git a/frontend/src/utils/http.jsx b/frontend/src/utils/http.jsx deleted file mode 100644 index 324235d..0000000 --- a/frontend/src/utils/http.jsx +++ /dev/null @@ -1,45 +0,0 @@ -// TODO: axios - -var $ = require('jquery'); - -const toParams = (obj) => Object.keys(obj) - .filter(key => !!obj[key]) - .map(key => `${key}=${obj[key]}`) - .join('&'); - - -var get = function(url, data){ - return $.ajax({ - url : url, - type : 'GET', - dataType : 'json', - cache : false, - data, - contentType:"application/json" - }) -} - -var post = function(url, data){ -// return $.post(url, data); - return $.ajax({ - url : url, - type : 'POST', - contentType:"application/json", - data : JSON.stringify(data) - }) -} - -var del = function(url){ - return $.ajax({ - url : url, - type : 'DELETE' - }) -} - - -module.exports = { - get : get, - post : post, - del : del, - toParams, -} From 0555fc07d15510c0d3933b65e2e72cef7d513b17 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 10 Oct 2016 00:59:01 +0300 Subject: [PATCH 11/18] try cancel --- api/server.js | 5 ++ frontend/src/containers/TasksList/state.jsx | 7 +- frontend/src/containers/Test/index.jsx | 75 +++++++++++++++++++++ frontend/src/routes.jsx | 2 + 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 frontend/src/containers/Test/index.jsx diff --git a/api/server.js b/api/server.js index d7779a2..2cfa7a3 100644 --- a/api/server.js +++ b/api/server.js @@ -46,6 +46,11 @@ ORM.init(app, function(e){ app.use('/api/users', ORM.REST('users')) app.use('/api/version', ORM.REST('version')) + app.get('/api/test', function(req, res){ + setTimeout(function(){ + res.json({ title: 'test' }); + }, 4000); + }) // html5 history api app.use(fallback('index.html', { root: root })) diff --git a/frontend/src/containers/TasksList/state.jsx b/frontend/src/containers/TasksList/state.jsx index 615be41..a24d830 100644 --- a/frontend/src/containers/TasksList/state.jsx +++ b/frontend/src/containers/TasksList/state.jsx @@ -28,7 +28,12 @@ export function reducer(state = initState, action) { } } -import { toParams } from 'utils/http'; + +const toParams = (obj) => Object.keys(obj) + .filter(key => !!obj[key]) + .map(key => `${key}=${obj[key]}`) + .join('&'); + const loadTasks = () => (dispatch, getState) => { const { diff --git a/frontend/src/containers/Test/index.jsx b/frontend/src/containers/Test/index.jsx new file mode 100644 index 0000000..8ad6b29 --- /dev/null +++ b/frontend/src/containers/Test/index.jsx @@ -0,0 +1,75 @@ +import React, { Component } from 'react'; + +//import axios from 'axios'; + +import axios from 'axios'; +import cancelXhrAdapter from 'axios-cancel'; +import {Cancellation} from 'axios-cancel/cancel'; + +//let request; +let cancelRequest; +class Test extends Component { + click = () => { + //axios.abort('q'); + // if (request) request.abort(); + + // request = axios({ + // url: '/api/test', + // method: 'get', + // requestId: 'q' + // }); + + // request.then(function(){ + // alert(1) + // }); + + // if (cancelRequest) cancelRequest(); + + // let promise = new Promise((resolve) => { + // cancelRequest = resolve + // }); + + // // window.addEventListener('beforeunload', (e) => { + // // cancelRequest() + // // }) + + // axios.get('/api/test', { + // timeout: 1000 + // }).then(function(){ + // alert(1); + // }); + if (this.cancellation) this.cancellation.cancel('test'); + + this.cancellation = new Cancellation(); + axios('/api/test', { + adapter: cancelXhrAdapter, + cancellation: this.cancellation + }).then(({data}) => { + this.cancellation = null; + alert('ok') + }).catch((e) => { + console.log(e) + }); + + + // var config = { + // method: 'get', + // url: '/api/test' + // }; + // axios(config); + // console.log(config); + + } + + render() { + return ( +
+
+ +
+
+ ); + } +} + +export default Test; diff --git a/frontend/src/routes.jsx b/frontend/src/routes.jsx index ad6b8be..a756f98 100644 --- a/frontend/src/routes.jsx +++ b/frontend/src/routes.jsx @@ -12,6 +12,7 @@ import DashboardLayout from 'containers/Application/DashboardLayout/'; import TasksList from 'containers/TasksList/'; import TaskAdd from 'containers/TaskAdd/'; import TaskDetails from 'containers/TaskDetails'; +import Test from 'containers/Test/'; import App from 'components/Layouts/App'; @@ -19,6 +20,7 @@ module.exports = ({ dispatch, getState }) => (
+ From e8343aaa813a498f78379fe1fa1da8a114d3d1cc Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 10 Oct 2016 01:52:33 +0300 Subject: [PATCH 12/18] rewrite task list --- .../TasksList/TaskFilter/StatusSelect.jsx | 18 +--- .../TasksList/TaskFilter/UserSelect.jsx | 17 +--- .../containers/TasksList/TaskFilter/index.jsx | 26 ++--- .../containers/TasksList/TaskPager/index.jsx | 14 +-- .../containers/TasksList/TaskTable/index.jsx | 13 +-- frontend/src/containers/TasksList/index.jsx | 79 ++++++++++++--- frontend/src/containers/TasksList/state.jsx | 98 ++++++++----------- frontend/src/reduxApp/modules/reducer.jsx | 2 - 8 files changed, 121 insertions(+), 146 deletions(-) diff --git a/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx b/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx index e4d94aa..7ea3b0b 100644 --- a/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx +++ b/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx @@ -1,9 +1,8 @@ import React from 'react'; import Select from 'react-select'; -import { connect } from 'react-redux'; -const StatusSelect = ({ statuses, status, changeStatus }) => ( +export const StatusSelect = ({ statuses, status, changeStatus }) => ( changeUserId(newValue.value || null)} @@ -13,17 +12,3 @@ const UserSelect = ({ users, userId, changeUserId }) => ( /> ); -import { changeUserId } from '../state'; - -import { - getUserOptions, -} from 'reduxApp/modules/app'; - - -export default connect( - state => ({ - users: getUserOptions(state), - userId: state.tasksList.userId, - }), - { changeUserId }, -)(UserSelect); diff --git a/frontend/src/containers/TasksList/TaskFilter/index.jsx b/frontend/src/containers/TasksList/TaskFilter/index.jsx index 0ac571f..095d5bf 100644 --- a/frontend/src/containers/TasksList/TaskFilter/index.jsx +++ b/frontend/src/containers/TasksList/TaskFilter/index.jsx @@ -1,21 +1,24 @@ -import { connect } from 'react-redux'; import React from 'react'; import { Button, Grid, Col, Row } from 'react-bootstrap'; -import UserSelect from './UserSelect'; -import StatusSelect from './StatusSelect'; +import { UserSelect } from './UserSelect'; +import { StatusSelect } from './StatusSelect'; -const TaskFilter = ({ makeSearch }) => ( +export const TaskFilter = ({ + makeSearch, + users, userId, changeUserId, + statuses, status, changeStatus, +}) => (
- + - + @@ -26,14 +29,3 @@ const TaskFilter = ({ makeSearch }) => ( ); -import { - makeSearch, -} from '../state'; - -export default connect( - null, - { - makeSearch, - }, -)(TaskFilter); - diff --git a/frontend/src/containers/TasksList/TaskPager/index.jsx b/frontend/src/containers/TasksList/TaskPager/index.jsx index bdbf647..05963c4 100644 --- a/frontend/src/containers/TasksList/TaskPager/index.jsx +++ b/frontend/src/containers/TasksList/TaskPager/index.jsx @@ -1,8 +1,7 @@ -import { connect } from 'react-redux'; import React, { PropTypes } from 'react'; import { Pagination } from 'react-bootstrap'; -const TaskPager = (props) => ( +export const TaskPager = (props) => (
{props.items > 1 && ({ - items: state.tasksList.items, - activePage: state.tasksList.page, - }), - { - onSelect: changePage, - } -)(TaskPager); diff --git a/frontend/src/containers/TasksList/TaskTable/index.jsx b/frontend/src/containers/TasksList/TaskTable/index.jsx index 0b06b8d..9208581 100644 --- a/frontend/src/containers/TasksList/TaskTable/index.jsx +++ b/frontend/src/containers/TasksList/TaskTable/index.jsx @@ -2,7 +2,9 @@ import React, { PropTypes } from 'react'; import { Table } from 'react-bootstrap'; import { Link } from 'react-router'; -const TaskTable = ({ tasks, projectId }) => ( +import { observer } from 'mobx-react'; + +export const TaskTable = observer(({ tasks, projectId }) => ( @@ -31,17 +33,10 @@ const TaskTable = ({ tasks, projectId }) => ( ))}
-); +)); TaskTable.propTypes = { tasks: PropTypes.array, projectId: PropTypes.string, }; -import { connect } from 'react-redux'; -export default connect( - state => ({ - tasks: state.tasksList.tasks, - projectId: state.router.params.projectId, - }) -)(TaskTable); diff --git a/frontend/src/containers/TasksList/index.jsx b/frontend/src/containers/TasksList/index.jsx index 786543f..c9c0379 100644 --- a/frontend/src/containers/TasksList/index.jsx +++ b/frontend/src/containers/TasksList/index.jsx @@ -1,21 +1,72 @@ -import React from 'react'; +import React, { Component } from 'react'; -import TaskTable from './TaskTable/'; +import { TaskTable } from './TaskTable/'; -import TaskFilter from './TaskFilter/'; -import TaskPager from './TaskPager/'; +import { TaskFilter } from './TaskFilter/'; +import { TaskPager } from './TaskPager/'; -const TaskList = () => ( -
- - - -
-); +import Store from './state'; -import loading from 'HOC/loading'; +import { observer } from 'mobx-react'; +import { connect } from 'react-redux'; -import { showPage } from './state'; +// TODO: add global app state in mobx +import { + getStatusesOptions, + getUserOptions, +} from 'reduxApp/modules/app'; -export default loading([showPage])(TaskList); +@connect( + state => ({ + users: getUserOptions(state), + statuses: getStatusesOptions(state), + }) +) +// ------------------- +@observer +class TaskList extends Component { + constructor(props) { + super(props); + this.store = new Store(); + } + componentDidMount() { + this.store.showPage(); + } + render() { + const { + users, + statuses, + params: { projectId }, + } = this.props; + + const { + tasks, + items, + page, + onSelect, + makeSearch, + userId, changeUserId, + status, changeStatus, + } = this.store; + + return ( +
+ + + +
+ ); + } +} + + +export default TaskList; diff --git a/frontend/src/containers/TasksList/state.jsx b/frontend/src/containers/TasksList/state.jsx index a24d830..49e8da6 100644 --- a/frontend/src/containers/TasksList/state.jsx +++ b/frontend/src/containers/TasksList/state.jsx @@ -1,74 +1,56 @@ -const initState = { - userId: null, - status: 'new', - - tasks: [], - page: 1, - items: 0, -}; - -export function reducer(state = initState, action) { - switch (action.type) { - - case 'CHANGE_USER_ID': - return { ...state, userId: action.payload }; - case 'CHANGE_STATUS': - return { ...state, status: action.payload }; - case 'SET_PAGE': - return { ...state, page: action.payload }; - - case 'LOAD_TASKS_SUCCESS': { - const { count, items } = action.payload; - return { ...state, tasks: items, items: Math.ceil(count / 10) }; - } - - default: - return state; - } -} - - const toParams = (obj) => Object.keys(obj) .filter(key => !!obj[key]) .map(key => `${key}=${obj[key]}`) .join('&'); -const loadTasks = () => (dispatch, getState) => { - const { - tasksList: { +import axios from 'axios'; +import { observable } from 'mobx'; + +class Store { + @observable userId = null; + @observable status = 'new'; + + @observable tasks = []; + @observable page = 1; + @observable items = 0; + + showPage = () => { + const { + page, userId, status, - page, - }, -// router: { params: { projectId } }, - } = getState(); - const query = toParams({ - assignee: userId, - status, - }); + } = this; - return dispatch({ - type: 'LOAD_TASKS', - payload: { - request: `/api/tasks/page/${page}/5?${query}`, - }, - }); -}; + const query = toParams({ + assignee: userId, + status, + }); + return axios.get(`/api/tasks/page/${page}/5?${query}`).then(response => { + const { items, count } = response.data; + this.tasks.replace(items); + this.items = Math.ceil(count / 10); + }); + } -const setPage = (page) => ({ type: 'SET_PAGE', payload: page }); + onSelect = (page) => { + this.page = page; + this.showPage(); + } -// PUBLIC + makeSearch = () => { + this.showPage(); + } -export const changeUserId = (id) => ({ type: 'CHANGE_USER_ID', payload: id }); -export const changeStatus = (status) => ({ type: 'CHANGE_STATUS', payload: status }); + changeUserId = (userId) => { + this.userId = userId; + } -export const changePage = (page) => (dispatch) => { - dispatch(setPage(page)); - return dispatch(loadTasks()); -}; + changeStatus = (status) => { + this.status = status; + } +} -export const makeSearch = () => (dispatch) => dispatch(loadTasks()); -export const showPage = () => (dispatch) => dispatch(loadTasks()); +export default Store; diff --git a/frontend/src/reduxApp/modules/reducer.jsx b/frontend/src/reduxApp/modules/reducer.jsx index f9265d3..3d51cd6 100644 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ b/frontend/src/reduxApp/modules/reducer.jsx @@ -5,14 +5,12 @@ import { reducer as formReducer } from 'redux-form'; import { reducer as auth } from './auth'; import { reducer as app } from './app'; import { reducer as tasksAdd } from 'containers/TaskAdd/state'; -import { reducer as tasksList } from 'containers/TasksList/state'; import { reducer as tasksDetails } from 'containers/TaskDetails/state'; import { reducer as projectsList } from 'containers/ProjectsList/state'; export default combineReducers({ app, tasksDetails, - tasksList, projectsList, tasksAdd, From 6ec6db21d832078fda0228a2d3c77f288ed064ac Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 10 Oct 2016 01:55:44 +0300 Subject: [PATCH 13/18] fix filter --- frontend/src/containers/TasksList/state.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/containers/TasksList/state.jsx b/frontend/src/containers/TasksList/state.jsx index 49e8da6..80d5c06 100644 --- a/frontend/src/containers/TasksList/state.jsx +++ b/frontend/src/containers/TasksList/state.jsx @@ -40,6 +40,7 @@ class Store { } makeSearch = () => { + this.page = 1; this.showPage(); } From 4f5d3b6d47948c11674c3c4141b56047d2636993 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 16 Oct 2016 16:09:29 +0300 Subject: [PATCH 14/18] tasks --- frontend/package.json | 2 +- frontend/src/App.jsx | 24 ++++ .../TaskDetails/AddComentForm/index.jsx | 14 +- .../TaskDetails/CommentsList/index.jsx | 22 ++- .../containers/TaskDetails/TaskInfo/index.jsx | 37 +++-- frontend/src/containers/TaskDetails/index.jsx | 47 +++++-- frontend/src/containers/TaskDetails/state.jsx | 129 ++++++------------ .../TasksList/TaskFilter/StatusSelect.jsx | 26 ++-- .../TasksList/TaskFilter/UserSelect.jsx | 25 ++-- .../containers/TasksList/TaskFilter/index.jsx | 42 +++--- .../containers/TasksList/TaskPager/index.jsx | 23 ++-- .../containers/TasksList/TaskTable/index.jsx | 63 +++++---- frontend/src/containers/TasksList/index.jsx | 52 +------ frontend/src/containers/TasksList/state.jsx | 5 +- frontend/src/containers/Test/index.jsx | 4 + frontend/src/main.jsx | 49 +++++-- frontend/src/reduxApp/modules/reducer.jsx | 4 +- frontend/src/routes.jsx | 5 +- 18 files changed, 294 insertions(+), 279 deletions(-) create mode 100644 frontend/src/App.jsx diff --git a/frontend/package.json b/frontend/package.json index 9c41f9d..1e7e6a6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,7 +34,7 @@ "json-loader": "^0.5.4", "lodash": "^3.10.1", "mobx": "^2.1.3", - "mobx-react": "^3.0.4", + "mobx-react": "^3.5.6", "mobx-react-devtools": "^4.0.2", "moment": "^2.10.6", "react": "0.14.3", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 0000000..d32003e --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,24 @@ +import axios from 'axios'; +import { observable, computed } from 'mobx'; + +class App { + @observable users = []; + @observable statuses = ['new', 'inprogress', 'testing', 'complited']; + + @computed get getStatusesOptions() { + return this.statuses.map(str => ({ value: str, label: str })); + } + + @computed get getUserOptions() { + return this.users.map(user => ({ value: user.id, label: user.name })); + } + + appStart = () => { + axios.get('/api/users') + .then(response => { + this.users.replace(response.data); + }); + } +} + +export default App; diff --git a/frontend/src/containers/TaskDetails/AddComentForm/index.jsx b/frontend/src/containers/TaskDetails/AddComentForm/index.jsx index 1f780af..d33fa69 100644 --- a/frontend/src/containers/TaskDetails/AddComentForm/index.jsx +++ b/frontend/src/containers/TaskDetails/AddComentForm/index.jsx @@ -1,9 +1,10 @@ -import { connect } from 'react-redux'; - import React, { Component, PropTypes } from 'react'; import { Button, Input } from 'react-bootstrap'; +import { observer } from 'mobx-react'; + +@observer(['taskDetails']) class AddComentForm extends Component { state = { value: '' } @@ -17,7 +18,7 @@ class AddComentForm extends Component { } addComment = () => { - this.props.addComment(this.state.value); + this.props.taskDetails.addComment(this.props.taskId, this.state.value); this.setState({ value: '' }); } @@ -38,9 +39,4 @@ class AddComentForm extends Component { } } -import { addComment } from '../state'; - -export default connect( - null, - { addComment } -)(AddComentForm); +export default AddComentForm; diff --git a/frontend/src/containers/TaskDetails/CommentsList/index.jsx b/frontend/src/containers/TaskDetails/CommentsList/index.jsx index 266643a..d6964db 100644 --- a/frontend/src/containers/TaskDetails/CommentsList/index.jsx +++ b/frontend/src/containers/TaskDetails/CommentsList/index.jsx @@ -1,10 +1,13 @@ -import { connect } from 'react-redux'; - import React, { PropTypes } from 'react'; import { Button } from 'react-bootstrap'; -const CommentsList = ({ comments, deleteComment }) => ( +import { observer } from 'mobx-react'; + +const CommentsList = observer(['taskDetails'], ({ + taskDetails: { comments, deleteComment }, + taskId, +}) => (
{comments.map(comment => (
@@ -13,7 +16,7 @@ const CommentsList = ({ comments, deleteComment }) => ( @@ -22,18 +25,11 @@ const CommentsList = ({ comments, deleteComment }) => (
))}
-); +)); CommentsList.propTypes = { comments: PropTypes.array, deleteComment: PropTypes.func, }; -import { deleteComment } from '../state'; - -export default connect( - state => ({ - comments: state.tasksDetails.comments, - }), - { deleteComment } -)(CommentsList); +export default CommentsList; diff --git a/frontend/src/containers/TaskDetails/TaskInfo/index.jsx b/frontend/src/containers/TaskDetails/TaskInfo/index.jsx index 90cf29a..7288fcc 100644 --- a/frontend/src/containers/TaskDetails/TaskInfo/index.jsx +++ b/frontend/src/containers/TaskDetails/TaskInfo/index.jsx @@ -1,33 +1,32 @@ -import { connect } from 'react-redux'; - import React, { PropTypes } from 'react'; import { Button } from 'react-bootstrap'; -const TaskInfo = ({ task: { title, description }, deleteTask }) => ( -
+import { observer } from 'mobx-react'; + +const TaskInfo = observer(['taskDetails'], ({ + taskDetails: { task: { title, description }, deleteTask }, + projectId, +}) => { + return (
-

{title}

-

- {description} -

+
+

{title}

+

+ {description} +

+
+
- -
-); + ); +}); + TaskInfo.propTypes = { task: PropTypes.object, deleteTask: PropTypes.func, }; -import { deleteTask } from '../state'; - -export default connect( - state => ({ - task: state.tasksDetails.task, - }), - { deleteTask } -)(TaskInfo); +export default TaskInfo; diff --git a/frontend/src/containers/TaskDetails/index.jsx b/frontend/src/containers/TaskDetails/index.jsx index aa90264..59d99c9 100644 --- a/frontend/src/containers/TaskDetails/index.jsx +++ b/frontend/src/containers/TaskDetails/index.jsx @@ -1,18 +1,43 @@ -import React from 'react'; +import React, { Component } from 'react'; import TaskInfo from './TaskInfo/'; import AddComentForm from './AddComentForm/'; import CommentsList from './CommentsList/'; -const TaskDetails = () => ( -
- - - -
-); +import { observer } from 'mobx-react'; -import loading from 'HOC/loading'; -import { showPage } from './state'; +@observer(['taskDetails']) +class TaskDetails extends Component { + state = { + loading: true, + } -export default loading([showPage])(TaskDetails); + componentDidMount() { + const { + params, + taskDetails, + } = this.props; + + taskDetails.showPage(params).then(() => { + this.setState({ loading: false }); + }); + } + + render() { + const { + params: { projectId, id }, + } = this.props; + + return ( +
+ {!this.state.loading &&
+ + + +
} +
+ ); + } +} + +export default TaskDetails; diff --git a/frontend/src/containers/TaskDetails/state.jsx b/frontend/src/containers/TaskDetails/state.jsx index 25abd88..b5ae75e 100644 --- a/frontend/src/containers/TaskDetails/state.jsx +++ b/frontend/src/containers/TaskDetails/state.jsx @@ -1,92 +1,53 @@ -const initState = { - task: null, - comments: [], -}; - -export const reducer = (state = initState, action) => { - switch (action.type) { - case 'LOAD_COMMENTS_SUCCESS': - return { ...state, comments: action.payload.items }; - case 'LOAD_TASK_SUCCESS': - return { ...state, task: action.payload }; - default: - return state; +import axios from 'axios'; + +import { observable } from 'mobx'; +class Store { + @observable task = null; + @observable comments = []; + + showPage = ({ id }) => { + Promise.all([ + this.loadComments(id), + this.loadTask(id), + ]); + return axios.get(`/api/tasks/${id}`) + .then(response => { + this.task = response.data; + }); } -}; - -import { push } from 'redux-router'; - -const loadComments = (id) => ({ - type: 'LOAD_COMMENTS', - payload: { - request: `/api/tasks/${id}/comments/page/0/5`, - }, -}); - -const loadTask = (id) => ({ - type: 'LOAD_TASK', - payload: { - request: `/api/tasks/${id}`, - }, -}); - -const deleteTaskApiCall = (id) => ({ - type: 'DELETE_TASK', - payload: { - request: { - url: `/api/tasks/${id}`, - method: 'delete', - }, - }, -}); - -const deleteCommentApiCall = (taskId, commentId) => ({ - type: 'DEELTE_COMMENT', - payload: { - request: { - url: `/api/tasks/${taskId}/comments/${commentId}`, - method: 'delete', - }, - }, -}); -const addCommentApiCall = (taskId, data) => ({ - type: 'ADD_COMMENT', - payload: { - request: { - url: `/api/tasks/${taskId}/comments`, - method: 'post', - data, - }, - }, -}); + loadTask = (id) => { + return axios.get(`/api/tasks/${id}`) + .then(response => { + this.task = response.data; + }); + } -// PUBLICK -export const deleteTask = () => (dispatch, getState) => { - const { router: { params: { id, projectId } } } = getState(); - return dispatch(deleteTaskApiCall(id)) - .then(() => dispatch(push(`/projects/${projectId}/tasks`))); -}; + deleteTask = (projectId) => { + const { id } = this.task; + axios.delete(`/api/tasks/${id}`) + .then(() => { + window.location = `/projects/${projectId}/tasks`; + }); + } -export const addComment = (text) => (dispatch, getState) => { - const { - auth: { user: { name } }, - router: { params: { id } }, - } = getState(); + addComment = (taskId, text) => { + axios.post(`/api/tasks/${taskId}/comments`, { text, userName: 'vasa' }) + .then(() => this.loadComments(taskId)); + } - return dispatch(addCommentApiCall(id, { text, userName: name })) - .then(() => dispatch(loadComments(id))); -}; + loadComments = (taskId) => { + return axios.get(`/api/tasks/${taskId}/comments/page/0/5`) + .then((response) => { + this.comments.replace(response.data.items); + }); + } -export const deleteComment = (comment) => (dispatch, getState) => { - const { router: { params: { id } } } = getState(); - return dispatch(deleteCommentApiCall(id, comment.id)) - .then(() => dispatch(loadComments(id))); -}; + deleteComment = (taskId, comment) => { + axios.delete(`/api/tasks/${taskId}/comments/${comment.id}`) + .then(() => this.loadComments(taskId)); + } +} -export const showPage = ({ id }) => (dispatch) => - Promise.all([ - dispatch(loadComments(id)), - dispatch(loadTask(id)), - ]); +export default Store; diff --git a/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx b/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx index 7ea3b0b..0413a39 100644 --- a/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx +++ b/frontend/src/containers/TasksList/TaskFilter/StatusSelect.jsx @@ -2,13 +2,21 @@ import React from 'react'; import Select from 'react-select'; -export const StatusSelect = ({ statuses, status, changeStatus }) => ( - changeStatus(newValue.value)} + searchable={false} + clearable={false} + options={getStatusesOptions} + /> + ); +}); diff --git a/frontend/src/containers/TasksList/TaskFilter/UserSelect.jsx b/frontend/src/containers/TasksList/TaskFilter/UserSelect.jsx index 3d8e1ff..83c9b31 100644 --- a/frontend/src/containers/TasksList/TaskFilter/UserSelect.jsx +++ b/frontend/src/containers/TasksList/TaskFilter/UserSelect.jsx @@ -2,13 +2,20 @@ import React from 'react'; import Select from 'react-select'; -export const UserSelect = ({ users, userId, changeUserId }) => ( - changeUserId(newValue.value || null)} + searchable={false} + clearable={false} + options={getUserOptions.concat([{ value: '', label: 'All' }])} + /> + ); +}); diff --git a/frontend/src/containers/TasksList/TaskFilter/index.jsx b/frontend/src/containers/TasksList/TaskFilter/index.jsx index 095d5bf..513ae81 100644 --- a/frontend/src/containers/TasksList/TaskFilter/index.jsx +++ b/frontend/src/containers/TasksList/TaskFilter/index.jsx @@ -5,27 +5,27 @@ import { Button, Grid, Col, Row } from 'react-bootstrap'; import { UserSelect } from './UserSelect'; import { StatusSelect } from './StatusSelect'; +import { observer } from 'mobx-react'; -export const TaskFilter = ({ - makeSearch, - users, userId, changeUserId, - statuses, status, changeStatus, -}) => ( -
- - - - - - - - - - -
- +export const TaskFilter = observer(['taskList'], ({ + taskList: { makeSearch }, +}) => { + return ( +
+ + + + + + + + + + +
+ +
-
-); - + ); +}); diff --git a/frontend/src/containers/TasksList/TaskPager/index.jsx b/frontend/src/containers/TasksList/TaskPager/index.jsx index 05963c4..822abe4 100644 --- a/frontend/src/containers/TasksList/TaskPager/index.jsx +++ b/frontend/src/containers/TasksList/TaskPager/index.jsx @@ -1,15 +1,20 @@ import React, { PropTypes } from 'react'; import { Pagination } from 'react-bootstrap'; +import { observer } from 'mobx-react'; -export const TaskPager = (props) => ( -
- {props.items > 1 && props.onSelect(data.eventKey)} - />} -
-); +export const TaskPager = observer(['taskList'], ({ taskList: { items, onSelect, page } }) => { + console.log('render pager'); + return ( +
+ {items > 1 && onSelect(data.eventKey)} + />} +
+ ); +}); TaskPager.propTypes = { items: PropTypes.number, diff --git a/frontend/src/containers/TasksList/TaskTable/index.jsx b/frontend/src/containers/TasksList/TaskTable/index.jsx index 9208581..8ee28fa 100644 --- a/frontend/src/containers/TasksList/TaskTable/index.jsx +++ b/frontend/src/containers/TasksList/TaskTable/index.jsx @@ -4,36 +4,39 @@ import { Link } from 'react-router'; import { observer } from 'mobx-react'; -export const TaskTable = observer(({ tasks, projectId }) => ( - - - - - - - - - - - {tasks.map(task => ( - - - - - - - ))} - -
TitleStatusAssigneeVersion
- {task.title} - - {task.status} - - {task.assigneeName} - - {task.version} -
-)); +export const TaskTable = observer(['taskList'], ({ taskList: { tasks }, projectId }) => { + console.log('render table'); + return ( + + + + + + + + + + + {tasks.map(task => ( + + + + + + + ))} + +
TitleStatusAssigneeVersion
+ {task.title} + + {task.status} + + {task.assigneeName} + + {task.version} +
+ ); +}); TaskTable.propTypes = { tasks: PropTypes.array, diff --git a/frontend/src/containers/TasksList/index.jsx b/frontend/src/containers/TasksList/index.jsx index c9c0379..c55af9a 100644 --- a/frontend/src/containers/TasksList/index.jsx +++ b/frontend/src/containers/TasksList/index.jsx @@ -6,63 +6,25 @@ import { TaskTable } from './TaskTable/'; import { TaskFilter } from './TaskFilter/'; import { TaskPager } from './TaskPager/'; -import Store from './state'; - import { observer } from 'mobx-react'; -import { connect } from 'react-redux'; - -// TODO: add global app state in mobx -import { - getStatusesOptions, - getUserOptions, -} from 'reduxApp/modules/app'; -@connect( - state => ({ - users: getUserOptions(state), - statuses: getStatusesOptions(state), - }) -) -// ------------------- -@observer +@observer(['taskList']) class TaskList extends Component { - constructor(props) { - super(props); - this.store = new Store(); - } + componentDidMount() { - this.store.showPage(); + this.props.taskList.showPage(); } + render() { const { - users, - statuses, params: { projectId }, } = this.props; - const { - tasks, - items, - page, - onSelect, - makeSearch, - userId, changeUserId, - status, changeStatus, - } = this.store; - return (
- - - + + +
); } diff --git a/frontend/src/containers/TasksList/state.jsx b/frontend/src/containers/TasksList/state.jsx index 80d5c06..91ee8c7 100644 --- a/frontend/src/containers/TasksList/state.jsx +++ b/frontend/src/containers/TasksList/state.jsx @@ -6,7 +6,7 @@ const toParams = (obj) => Object.keys(obj) import axios from 'axios'; -import { observable } from 'mobx'; +import { observable, action, computed } from 'mobx'; class Store { @observable userId = null; @@ -16,7 +16,7 @@ class Store { @observable page = 1; @observable items = 0; - showPage = () => { + @action showPage = () => { const { page, userId, @@ -53,5 +53,6 @@ class Store { } } + export default Store; diff --git a/frontend/src/containers/Test/index.jsx b/frontend/src/containers/Test/index.jsx index 8ad6b29..3dbc00d 100644 --- a/frontend/src/containers/Test/index.jsx +++ b/frontend/src/containers/Test/index.jsx @@ -6,6 +6,8 @@ import axios from 'axios'; import cancelXhrAdapter from 'axios-cancel'; import {Cancellation} from 'axios-cancel/cancel'; +import { observer, Provider } from 'mobx-react'; + //let request; let cancelRequest; class Test extends Component { @@ -64,9 +66,11 @@ class Test extends Component { render() { return (
+
+
); } diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 253d200..ddcdd2e 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -6,33 +6,58 @@ import { render } from 'react-dom'; import { Provider } from 'react-redux'; import { ReduxRouter } from 'redux-router'; import createStore from './reduxApp/create.jsx'; +import { Provider as MOBXPRovider } from 'mobx-react'; + +import App from 'App'; +import TaskList from 'containers/TasksList/state'; +import TaskDetails from 'containers/TaskDetails/state'; + const getRoutes = require('./routes'); -const Root = ({ store }) => ( - - - {getRoutes(store)} - - +const Root = ({ store, stores }) => ( +
+ +
+ +
+ + + {getRoutes(store)} + + +
+ +
+
+
); + Root.propTypes = { store: PropTypes.object.isRequired, }; import { appStart } from 'reduxApp/modules/app'; + window.onload = () => { const root = document.getElementById('app'); - try { + //try { const store = createStore(); + const app = new App(); + app.appStart(); + const stores = { + app, + taskList: new TaskList(), + taskDetails: new TaskDetails(), + }; render(( - + ), root); store.dispatch(appStart()); - } catch (e) { - const RedBox = require('redbox-react').default; - render(, root); - } + // } catch (e) { + // const RedBox = require('redbox-react').default; + // render(, root); + // } }; diff --git a/frontend/src/reduxApp/modules/reducer.jsx b/frontend/src/reduxApp/modules/reducer.jsx index 3d51cd6..631f0bb 100644 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ b/frontend/src/reduxApp/modules/reducer.jsx @@ -5,12 +5,12 @@ import { reducer as formReducer } from 'redux-form'; import { reducer as auth } from './auth'; import { reducer as app } from './app'; import { reducer as tasksAdd } from 'containers/TaskAdd/state'; -import { reducer as tasksDetails } from 'containers/TaskDetails/state'; +//import { reducer as tasksDetails } from 'containers/TaskDetails/state'; import { reducer as projectsList } from 'containers/ProjectsList/state'; export default combineReducers({ app, - tasksDetails, +// tasksDetails, projectsList, tasksAdd, diff --git a/frontend/src/routes.jsx b/frontend/src/routes.jsx index a756f98..63215da 100644 --- a/frontend/src/routes.jsx +++ b/frontend/src/routes.jsx @@ -16,9 +16,9 @@ import Test from 'containers/Test/'; import App from 'components/Layouts/App'; -module.exports = ({ dispatch, getState }) => ( + +module.exports = ({ dispatch, getState, app }) => (
- @@ -36,6 +36,5 @@ module.exports = ({ dispatch, getState }) => ( -
); From e20f190dbfdf892f6b9f24e6417f9938da01fdeb Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 16 Oct 2016 17:39:24 +0300 Subject: [PATCH 15/18] add taks --- frontend/src/App.jsx | 12 ++- .../containers/TaskAdd/TaskAddForm/index.jsx | 36 +++----- frontend/src/containers/TaskAdd/index.jsx | 30 +++++-- frontend/src/containers/TaskAdd/state.jsx | 84 ++++++------------ .../containers/UsersList/UsersTable/index.jsx | 86 +++++++++---------- frontend/src/containers/UsersList/index.jsx | 5 +- frontend/src/containers/UsersList/state.jsx | 15 ---- frontend/src/main.jsx | 2 + frontend/src/reduxApp/modules/auth.jsx | 37 ++++---- frontend/src/reduxApp/modules/reducer.jsx | 4 +- 10 files changed, 136 insertions(+), 175 deletions(-) delete mode 100644 frontend/src/containers/UsersList/state.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index d32003e..c490e91 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -4,6 +4,7 @@ import { observable, computed } from 'mobx'; class App { @observable users = []; @observable statuses = ['new', 'inprogress', 'testing', 'complited']; + @observable currentUser = {}; @computed get getStatusesOptions() { return this.statuses.map(str => ({ value: str, label: str })); @@ -14,11 +15,20 @@ class App { } appStart = () => { - axios.get('/api/users') + this.loadUsers(); + } + + loadUsers = () => { + return axios.get('/api/users') .then(response => { this.users.replace(response.data); }); } + + removeUser = ({ id }) => { + return axios.delete(`/api/users/${id}`) + .then(() => this.loadUsers()); + } } export default App; diff --git a/frontend/src/containers/TaskAdd/TaskAddForm/index.jsx b/frontend/src/containers/TaskAdd/TaskAddForm/index.jsx index a57c8b6..ef446d3 100644 --- a/frontend/src/containers/TaskAdd/TaskAddForm/index.jsx +++ b/frontend/src/containers/TaskAdd/TaskAddForm/index.jsx @@ -6,6 +6,9 @@ import Select from 'react-select'; import './index.css'; +import { observer } from 'mobx-react'; + +@observer(['taskAdd', 'app']) class TaskAddForm extends Component { constructor(props) { super(props); @@ -13,24 +16,26 @@ class TaskAddForm extends Component { } add() { - const { addTask, users } = this.props; + const { getUserOptions } = this.props.app; + const { addTask } = this.props.taskAdd; const title = this.refs.input.getValue(); const description = this.refs.description.getValue(); const { status, assignee, version } = this.state; - const assigneeUser = users.find(user => user.value === assignee); + const assigneeUser = getUserOptions.find(user => user.value === assignee.value); addTask({ title, description, status, - assignee, + assignee: assignee.value, version, assigneeName: assigneeUser.label, }); } render() { - const { statuses, users, addVersion, versions } = this.props; + const { getStatusesOptions, getUserOptions } = this.props.app; + const { addVersion, versions } = this.props.taskAdd; const allVersion = versions.map(version => ({ value: version.title, label: version.title })); @@ -45,7 +50,7 @@ class TaskAddForm extends Component { searchable={false} clearable={false} placeholder='-' - options={statuses} + options={getStatusesOptions} />
@@ -54,7 +59,7 @@ class TaskAddForm extends Component { value={this.state.assignee} onChange={assignee => this.setState({ assignee })} placeholder='-' - options={users} + options={getUserOptions} />
@@ -84,21 +89,4 @@ class TaskAddForm extends Component { } } - -import { connect } from 'react-redux'; -import { addTask, addVersion } from '../state'; - - -import { - getStatusesOptions, - getUserOptions, -} from 'reduxApp/modules/app'; - -export default connect( - state => ({ - statuses: getStatusesOptions(state), - users: getUserOptions(state), - versions: state.tasksAdd.versions, - assignee: state.auth.user ? state.auth.user.id : null, - }) -, { addTask, addVersion })(TaskAddForm); +export default TaskAddForm; diff --git a/frontend/src/containers/TaskAdd/index.jsx b/frontend/src/containers/TaskAdd/index.jsx index 2a9f766..2a37e15 100644 --- a/frontend/src/containers/TaskAdd/index.jsx +++ b/frontend/src/containers/TaskAdd/index.jsx @@ -1,13 +1,25 @@ -import React from 'react'; +import React, { Component } from 'react'; import TaskAddForm from './TaskAddForm/'; -const TaskAdd = () => ( -
- -
-); +import { observer } from 'mobx-react'; -import loading from 'HOC/loading'; -import { showPage } from './state'; +@observer(['taskAdd']) +class TaskAdd extends Component { + componentDidMount() { + const { + params, + taskAdd, + } = this.props; -export default loading([showPage])(TaskAdd); + taskAdd.showPage(params); + } + render() { + return ( +
+ +
+ ); + } +} + +export default TaskAdd; diff --git a/frontend/src/containers/TaskAdd/state.jsx b/frontend/src/containers/TaskAdd/state.jsx index fa10d4a..0b8b660 100644 --- a/frontend/src/containers/TaskAdd/state.jsx +++ b/frontend/src/containers/TaskAdd/state.jsx @@ -1,65 +1,35 @@ -const initState = { - versions: [], -}; +import axios from 'axios'; +import { observable } from 'mobx'; +class Store { + @observable versions = []; + params = null; -export const reducer = (state = initState, action) => { - switch (action.type) { - case 'LOAD_VERSIONS_SUCCESS': - return { ...state, versions: action.payload }; - - default: - return state; + showPage = (params) => { + this.params = params; + this.getAllVersions(); } -}; - -import { push } from 'redux-router'; - -const getAllVersions = () => ({ - type: 'LOAD_VERSIONS', - payload: { - request: '/api/version', - }, -}); - -const createVersion = (data) => ({ - type: 'CREATE_VERSION', - payload: { - request: { - url: '/api/version', - method: 'post', - data, - }, - }, -}); - -const createTask = (data) => ({ - type: 'CREATE_TASK', - payload: { - request: { - url: '/api/tasks', - method: 'post', - data, - }, - }, -}); - -export const showPage = () => (dispatch) => dispatch(getAllVersions()); + getAllVersions = () => { + return axios.get('/api/version') + .then(response => { + this.versions.replace(response.data); + }); + } -export function addVersion() { - return (dispatch) => { + addVersion = () => { const title = prompt('Create new Version', ''); - if (title) { - dispatch(createVersion({ title })) - .then(() => dispatch(getAllVersions())); - } - }; -} + axios.post('/api/version', { title }) + .then(() => this.getAllVersions()); + } -export function addTask(form) { - return (dispatch, getState) => { - const { router: { params: { projectId } } } = getState(); - dispatch(createTask(form)).then(() => dispatch(push(`/projects/${projectId}/tasks`))); - }; + addTask = (data) => { + const { projectId } = this.params; + axios.post('/api/tasks', data) + .then(() => { + window.location = `/projects/${projectId}/tasks`; + }); + } } + +export default Store; diff --git a/frontend/src/containers/UsersList/UsersTable/index.jsx b/frontend/src/containers/UsersList/UsersTable/index.jsx index 6accd6d..139d697 100644 --- a/frontend/src/containers/UsersList/UsersTable/index.jsx +++ b/frontend/src/containers/UsersList/UsersTable/index.jsx @@ -1,52 +1,44 @@ import React from 'react'; import { Table, Button } from 'react-bootstrap'; -const UsersTable = ({ - users, removeUser, currentUser, -}) => ( -
-

All users

- - - - - - - - - - {users.map(user => ( - - - - - - ))} - -
NameEmail
- {user.name} - - {user.email} - -
- -
-
-
-); +import { observer } from 'mobx-react'; +export default observer(['app'], ({ + app: { users, removeUser, currentUser }, +}) => { + return ( +
+

All users

+ + + + + + + + + + {users.map(user => ( + + + + + + ))} + +
NameEmail
+ {user.name} + + {user.email} + +
+ +
+
+
+ ); +}); -import { connect } from 'react-redux'; -import { removeUser } from '../state'; - - -export default connect( - state => ({ - users: state.app.users, - currentUser: state.auth.user, - }), - { removeUser } -)(UsersTable); diff --git a/frontend/src/containers/UsersList/index.jsx b/frontend/src/containers/UsersList/index.jsx index a6c6c89..0463d9b 100644 --- a/frontend/src/containers/UsersList/index.jsx +++ b/frontend/src/containers/UsersList/index.jsx @@ -8,5 +8,6 @@ const UsersList = () => (
); -import loading from 'HOC/loading'; -export default loading([])(UsersList); +export default UsersList; +// import loading from 'HOC/loading'; +// export default loading([])(UsersList); diff --git a/frontend/src/containers/UsersList/state.jsx b/frontend/src/containers/UsersList/state.jsx deleted file mode 100644 index bcf8cd2..0000000 --- a/frontend/src/containers/UsersList/state.jsx +++ /dev/null @@ -1,15 +0,0 @@ - -const deleteUser = (id) => ({ - type: 'DELETE_USER', - payload: { - request: { - url: `/api/users/${id}`, - method: 'delete', - }, - }, -}); - -import { loadUsers } from 'reduxApp/modules/app'; - -export const removeUser = ({ id }) => (dispatch) => - dispatch(deleteUser(id)).then(dispatch(loadUsers())); diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index ddcdd2e..8ee7269 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -11,6 +11,7 @@ import { Provider as MOBXPRovider } from 'mobx-react'; import App from 'App'; import TaskList from 'containers/TasksList/state'; import TaskDetails from 'containers/TaskDetails/state'; +import TaskAdd from 'containers/TaskAdd/state'; const getRoutes = require('./routes'); @@ -50,6 +51,7 @@ window.onload = () => { app, taskList: new TaskList(), taskDetails: new TaskDetails(), + taskAdd: new TaskAdd(), }; render(( diff --git a/frontend/src/reduxApp/modules/auth.jsx b/frontend/src/reduxApp/modules/auth.jsx index ca8c3d4..fc77fae 100644 --- a/frontend/src/reduxApp/modules/auth.jsx +++ b/frontend/src/reduxApp/modules/auth.jsx @@ -4,6 +4,7 @@ const initState = { }; export const LOGIN = 'LOGIN'; +import axios from 'axios'; export function reducer(state = initState, action) { switch (action.type) { @@ -28,17 +29,10 @@ export function startSession(payload) { }; } -export const logout = () => dispatch => - dispatch({ - types: ['LOGOUT', 'LOGOUT'], - payload: { - request: { - url: '/api/session', - mehtod: 'delete', - }, - }, - }).then(() => dispatch(push('/login'))); - +export const logout = () => dispatch => { + axios.delete('/api/session') + .then(() => dispatch(push('/login'))); +} export const login = (form) => (dispatch) => dispatch({ @@ -60,15 +54,22 @@ export const registr = (form) => dispatch => request: { url: '/api/users', method: 'post', + data: form }, }, - }).then(data => dispatch(login(data))); + }).then(data => dispatch(login(data.payload))); -export const checkAuth = () => ({ - types: ['START_SESSION', 'LOGOUT'], - payload: { - request: '/api/session', - }, -}); +export const checkAuth = () => (dispatch) => { + return axios.get('/api/session') + .then((response) => dispatch(startSession(response.data))) + .catch(() => dispatch(logout())); +}; + +// ({ +// types: ['START_SESSION', 'LOGOUT'], +// payload: { +// request: '/api/session', +// }, +// }); diff --git a/frontend/src/reduxApp/modules/reducer.jsx b/frontend/src/reduxApp/modules/reducer.jsx index 631f0bb..f1834be 100644 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ b/frontend/src/reduxApp/modules/reducer.jsx @@ -4,7 +4,7 @@ import { reducer as formReducer } from 'redux-form'; import { reducer as auth } from './auth'; import { reducer as app } from './app'; -import { reducer as tasksAdd } from 'containers/TaskAdd/state'; +//import { reducer as tasksAdd } from 'containers/TaskAdd/state'; //import { reducer as tasksDetails } from 'containers/TaskDetails/state'; import { reducer as projectsList } from 'containers/ProjectsList/state'; @@ -12,7 +12,7 @@ export default combineReducers({ app, // tasksDetails, projectsList, - tasksAdd, + //tasksAdd, auth, router: routerStateReducer, From 8a5a85eddc8d096d4b35e849e6845b6244d002b7 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 17 Oct 2016 00:25:45 +0300 Subject: [PATCH 16/18] auth project list --- frontend/src/App.jsx | 13 ++- frontend/src/Auth.jsx | 48 ++++++++++ frontend/src/HOC/AuthenticatedComponent.jsx | 45 ++++++---- .../DashboardLayout/DashboardMenu.jsx | 15 ++-- .../Application/TasksLayout/TasksMenu.jsx | 8 +- .../src/containers/Login/LoginForm/index.jsx | 17 ++-- .../containers/Login/RegisterForm/index.jsx | 13 ++- .../ProjectsList/AddProjectModal/index.jsx | 32 +++---- .../ProjectsList/ProjectsList/index.jsx | 30 ++++--- .../src/containers/ProjectsList/index.jsx | 31 ++++--- .../src/containers/ProjectsList/state.jsx | 87 +++++++------------ frontend/src/containers/TaskDetails/state.jsx | 6 +- frontend/src/containers/Test/index.jsx | 79 ----------------- .../containers/UsersList/UsersTable/index.jsx | 7 +- frontend/src/main.jsx | 4 + frontend/src/reduxApp/modules/app.jsx | 4 +- frontend/src/reduxApp/modules/reducer.jsx | 8 +- frontend/src/routes.jsx | 30 +++---- 18 files changed, 219 insertions(+), 258 deletions(-) create mode 100644 frontend/src/Auth.jsx delete mode 100644 frontend/src/containers/Test/index.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index c490e91..201478e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -3,15 +3,20 @@ import { observable, computed } from 'mobx'; class App { @observable users = []; - @observable statuses = ['new', 'inprogress', 'testing', 'complited']; - @observable currentUser = {}; + @computed get getUserOptions() { + return this.users.map(user => ({ value: user.id, label: user.name })); + } + @observable statuses = ['new', 'inprogress', 'testing', 'complited']; @computed get getStatusesOptions() { return this.statuses.map(str => ({ value: str, label: str })); } - @computed get getUserOptions() { - return this.users.map(user => ({ value: user.id, label: user.name })); + @observable currentUser = null; + @computed get userId() { + if (!this.currentUser) return null; + + return this.currentUser.id; } appStart = () => { diff --git a/frontend/src/Auth.jsx b/frontend/src/Auth.jsx new file mode 100644 index 0000000..15e7217 --- /dev/null +++ b/frontend/src/Auth.jsx @@ -0,0 +1,48 @@ +import axios from 'axios'; +import { observable, computed } from 'mobx'; + +class App { + @observable user = null; + @computed get userId() { + if (!this.user) return null; + + return this.user.id; + } + + @computed get isAuthenticated() { + return !!this.user; + } + + + checkAuth = () => { + return axios.get('/api/session') + .then((response) => { + this.user = response.data; + }) + .catch(() => this.logout()); + } + + + login = (form) => { + return axios.post('/api/login', form) + .then(response => { + this.user = response.data; + window.location = '/'; + }); + } + + registr = (form) => { + return axios.post('/api/users', form) + .then(response => this.login(response.data)); + } + + logout = () => { + return axios.delete('/api/session') + .then(() => { + this.user = null; + window.location = '/login'; + }); + } +} + +export default App; diff --git a/frontend/src/HOC/AuthenticatedComponent.jsx b/frontend/src/HOC/AuthenticatedComponent.jsx index 1650c52..f06cf28 100644 --- a/frontend/src/HOC/AuthenticatedComponent.jsx +++ b/frontend/src/HOC/AuthenticatedComponent.jsx @@ -1,15 +1,17 @@ import React from 'react'; -import { connect } from 'react-redux'; +//import { connect } from 'react-redux'; //import { pushState } from 'redux-router'; -import { checkAuth } from 'reduxApp/modules/auth'; - +//import { checkAuth } from 'reduxApp/modules/auth'; +import { observer } from 'mobx-react'; export function requireAuthentication(Component) { + + @observer(['auth']) class AuthenticatedComponent extends React.Component { componentWillMount() { - this.props.checkAuth(); + this.props.auth.checkAuth(); } // componentWillReceiveProps(nextProps) { @@ -17,18 +19,26 @@ export function requireAuthentication(Component) { // } // checkAuth() { - // if (!this.props.isAuthenticated) { - // let redirectAfterLogin = this.props.location.pathname - // this.props.dispatch(pushState(null, `/login?next=${redirectAfterLogin}`)) - // } + // // if (!this.props.isAuthenticated) { + // // let redirectAfterLogin = this.props.location.pathname + // // this.props.dispatch(pushState(null, `/login?next=${redirectAfterLogin}`)) + // // } + // this.props.auth.checkAuth() + // // .then(() => { + // // debugger + // // }) + // .catch(() => { + // debugger + // this.props.router.push('/login'); + // }); // } render() { - + const { isAuthenticated } = this.props.auth; return (
- {this.props.isAuthenticated === true - ? + {isAuthenticated === true + ? : null }
@@ -36,11 +46,12 @@ export function requireAuthentication(Component) { } } + return AuthenticatedComponent; - return connect( - (state) => ({ - isAuthenticated: !!state.auth.user, - }), - { checkAuth } - )(AuthenticatedComponent); + // return connect( + // (state) => ({ + // isAuthenticated: !!state.auth.user, + // }), + // { checkAuth } + // )(AuthenticatedComponent); }; diff --git a/frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx b/frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx index 2aa4e0a..c36fb68 100644 --- a/frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx +++ b/frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx @@ -5,16 +5,18 @@ import { Navbar, NavBrand, Nav, NavItem } from 'react-bootstrap'; import { LinkContainer } from 'react-router-bootstrap'; -const DashboardMenu = ({ - logout, - addProject, +import { observer } from 'mobx-react'; + +const DashboardMenu = observer(['projectsList', 'auth'], ({ + auth: { logout }, + projectsList: { openPopup }, }) => ( Task-tracker -); +)); import { connect } from 'react-redux'; -import { showAddProject } from 'reduxApp/modules/app'; import { logout } from 'reduxApp/modules/auth'; export default connect( null, - { addProject: showAddProject, logout } + { logout } )(DashboardMenu); diff --git a/frontend/src/containers/Application/TasksLayout/TasksMenu.jsx b/frontend/src/containers/Application/TasksLayout/TasksMenu.jsx index 90b41dc..e8cc667 100644 --- a/frontend/src/containers/Application/TasksLayout/TasksMenu.jsx +++ b/frontend/src/containers/Application/TasksLayout/TasksMenu.jsx @@ -5,9 +5,11 @@ import { Navbar, NavBrand, Nav, NavItem } from 'react-bootstrap'; import { LinkContainer } from 'react-router-bootstrap'; -const TasksMenu = ({ +import { observer } from 'mobx-react'; + +const TasksMenu = observer(['auth'], ({ projectId, - logout, + auth: { logout }, }) => ( @@ -27,7 +29,7 @@ const TasksMenu = ({ logout()}>logout -); +)); import { connect } from 'react-redux'; import { logout } from 'reduxApp/modules/auth'; diff --git a/frontend/src/containers/Login/LoginForm/index.jsx b/frontend/src/containers/Login/LoginForm/index.jsx index a86951e..d72da43 100644 --- a/frontend/src/containers/Login/LoginForm/index.jsx +++ b/frontend/src/containers/Login/LoginForm/index.jsx @@ -1,12 +1,13 @@ -import { connect } from 'react-redux'; - import React, { PropTypes } from 'react'; import { Button, Input } from 'react-bootstrap'; import './index.css'; -const LoginForm = ({ login }) => { + +import { observer } from 'mobx-react'; + +const LoginForm = observer(['auth'], ({ auth: { login } }) => { let email; let password; return ( @@ -33,16 +34,10 @@ const LoginForm = ({ login }) => { >login ); -}; +}); LoginForm.propTypes = { login: PropTypes.func, }; - -import { login } from 'reduxApp/modules/auth'; - -export default connect( - null, - { login } -)(LoginForm); +export default LoginForm; diff --git a/frontend/src/containers/Login/RegisterForm/index.jsx b/frontend/src/containers/Login/RegisterForm/index.jsx index da91957..66fbafd 100644 --- a/frontend/src/containers/Login/RegisterForm/index.jsx +++ b/frontend/src/containers/Login/RegisterForm/index.jsx @@ -1,19 +1,19 @@ -import { connect } from 'react-redux'; -import { registr } from 'reduxApp/modules/auth'; - import React, { Component, PropTypes } from 'react'; import { Button, Input } from 'react-bootstrap'; import './index.css'; +import { observer } from 'mobx-react'; + +@observer(['auth']) class RegisterForm extends Component { static propTypes = { registr: PropTypes.func, } regist = () => { - this.props.registr({ + this.props.auth.registr({ email: this.refs.email.getValue(), password: this.refs.password.getValue(), name: this.refs.name.getValue(), @@ -32,7 +32,4 @@ class RegisterForm extends Component { } } -export default connect( - null, - { registr } -)(RegisterForm); +export default RegisterForm; diff --git a/frontend/src/containers/ProjectsList/AddProjectModal/index.jsx b/frontend/src/containers/ProjectsList/AddProjectModal/index.jsx index ad2154a..0798dd7 100644 --- a/frontend/src/containers/ProjectsList/AddProjectModal/index.jsx +++ b/frontend/src/containers/ProjectsList/AddProjectModal/index.jsx @@ -7,7 +7,9 @@ import Select from 'react-select'; import 'react-select/dist/react-select.css'; const LinkedStateMixin = require('react-addons-linked-state-mixin'); -const AddProjectModal = React.createClass({ +import { observer } from 'mobx-react'; + +const AddProjectModal = observer(['projectsList', 'app'], React.createClass({ mixins: [LinkedStateMixin], getInitialState() { return { @@ -23,9 +25,12 @@ const AddProjectModal = React.createClass({ render() { const { - users, addProject, - } = this.props; + popupOpen, + closePopup, + } = this.props.projectsList; + + const { users } = this.props.app; const allUsers = users.map(user => ( { value: user.id, label: user.name }) @@ -35,12 +40,12 @@ const AddProjectModal = React.createClass({ userIds, title, } = this.state; - const ids = userIds.map(id => parseInt(id, 10)); + const ids = userIds.map(item => parseInt(item.value, 10)); addProject(title, ids); }; return ( - + Team: @@ -59,21 +64,8 @@ const AddProjectModal = React.createClass({ ); }, -}); - - -import { connect } from 'react-redux'; - +})); -import { - closePopup, addProject, -} from '../state'; +export default AddProjectModal; -export default connect( - state => ({ - show: state.projectsList.popupOpen, - users: state.app.users, - }), - { onHide: closePopup, addProject } -)(AddProjectModal); diff --git a/frontend/src/containers/ProjectsList/ProjectsList/index.jsx b/frontend/src/containers/ProjectsList/ProjectsList/index.jsx index bcf50db..2086062 100644 --- a/frontend/src/containers/ProjectsList/ProjectsList/index.jsx +++ b/frontend/src/containers/ProjectsList/ProjectsList/index.jsx @@ -26,8 +26,12 @@ const ProductItem = ({ product, removeProject }) => { ); }; +import { observer } from 'mobx-react'; -const ProjectsList = ({ projects, removeProject }) => ( + +const ProjectsList = observer(['projectsList'], ({ + projectsList: { projects, removeProject }, +}) => (
@@ -47,17 +51,19 @@ const ProjectsList = ({ projects, removeProject }) => ( )} -); +)); + +export default ProjectsList; -import { connect } from 'react-redux'; +// import { connect } from 'react-redux'; -import { - removeProject, -} from '../state'; +// import { +// removeProject, +// } from '../state'; -export default connect( - state => ({ - projects: state.projectsList.projects, - }), - { removeProject } -)(ProjectsList); +// export default connect( +// state => ({ +// projects: state.projectsList.projects, +// }), +// { removeProject } +// )(ProjectsList); diff --git a/frontend/src/containers/ProjectsList/index.jsx b/frontend/src/containers/ProjectsList/index.jsx index 8a3fd30..3045486 100644 --- a/frontend/src/containers/ProjectsList/index.jsx +++ b/frontend/src/containers/ProjectsList/index.jsx @@ -1,16 +1,27 @@ -import React from 'react'; +import React, { Component } from 'react'; import ProjectsList from './ProjectsList/'; import AddProjectModal from './AddProjectModal/'; +import { observer } from 'mobx-react'; -const ProjectsListPage = () => ( -
- - -
-); +@observer(['projectsList']) +class ProjectsListPage extends Component { + componentDidMount() { + this.props.projectsList.showPage(); + } -import loading from 'HOC/loading'; -import { showPage } from './state'; + render() { + return ( +
+ + +
+ ); + } +} -export default loading([showPage])(ProjectsListPage); +// import loading from 'HOC/loading'; +// import { showPage } from './state'; + +export default ProjectsListPage; +// loading([showPage])(ProjectsListPage); diff --git a/frontend/src/containers/ProjectsList/state.jsx b/frontend/src/containers/ProjectsList/state.jsx index cd8670c..47ba727 100644 --- a/frontend/src/containers/ProjectsList/state.jsx +++ b/frontend/src/containers/ProjectsList/state.jsx @@ -1,68 +1,41 @@ -const initState = { - projects: [], -}; +import axios from 'axios'; +import { observable, action, computed } from 'mobx'; -export const reducer = (state = initState, action) => { - switch (action.type) { - case 'PROJECTS_OPEN_POPUP': - return { ...state, popupOpen: true }; +class Store { + @observable projects = []; + @observable popupOpen = false; - case 'PROJECTS_CLOSE_POPUP': - return { ...state, popupOpen: false }; - - case 'LOAD_PRODUCTS_SUCCESS': - return { ...state, projects: action.payload }; - - default: - return state; + showPage = () => { + this.getAllProjects(); } -}; - - -const getAllProjects = () => ({ - type: 'LOAD_PRODUCTS', - payload: { - request: '/api/projects', - }, -}); - -const deleteProjects = (id) => ({ - type: 'DELETE_PRODUCT', - payload: { - request: { - url: `/api/projects/${id}`, - method: 'delete', - }, - }, -}); - -const createProject = (data) => ({ - type: 'CREATE_PROJECT', - payload: { - request: { - url: '/api/projects', - data, - method: 'post', - }, - }, -}); + openPopup = () => { + this.popupOpen = true; + } -const loadProjects = () => (dispatch) => dispatch(getAllProjects()); - -// Publick + closePopup = () => { + this.popupOpen = false; + } -export const showPage = () => (dispatch) => dispatch(loadProjects()); + addProject = (title, userIds) => { + return axios.post('/api/projects', { title }) + .then(() => this.getAllProjects()) + .then(() => this.closePopup()); + } -export const removeProject = ({ id }) => (dispatch) => dispatch(deleteProjects(id)) - .then(() => dispatch(loadProjects())); + removeProject = ({ id }) => { + return axios.delete(`/api/projects/${id}`) + .then(() => this.getAllProjects()); + } + getAllProjects = () => { + return axios.get('/api/projects') + .then(response => { + this.projects.replace(response.data); + }); + } +} -export const openPopup = () => ({ type: 'PROJECTS_OPEN_POPUP' }); -export const closePopup = () => ({ type: 'PROJECTS_CLOSE_POPUP' }); -export const addProject = (title, userIds) => (dispatch) => - dispatch(createProject({ title })) - .then(() => dispatch(getAllProjects())) - .then(() => dispatch(closePopup())); +export default Store; diff --git a/frontend/src/containers/TaskDetails/state.jsx b/frontend/src/containers/TaskDetails/state.jsx index b5ae75e..f88907a 100644 --- a/frontend/src/containers/TaskDetails/state.jsx +++ b/frontend/src/containers/TaskDetails/state.jsx @@ -7,14 +7,10 @@ class Store { @observable comments = []; showPage = ({ id }) => { - Promise.all([ + return Promise.all([ this.loadComments(id), this.loadTask(id), ]); - return axios.get(`/api/tasks/${id}`) - .then(response => { - this.task = response.data; - }); } loadTask = (id) => { diff --git a/frontend/src/containers/Test/index.jsx b/frontend/src/containers/Test/index.jsx deleted file mode 100644 index 3dbc00d..0000000 --- a/frontend/src/containers/Test/index.jsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, { Component } from 'react'; - -//import axios from 'axios'; - -import axios from 'axios'; -import cancelXhrAdapter from 'axios-cancel'; -import {Cancellation} from 'axios-cancel/cancel'; - -import { observer, Provider } from 'mobx-react'; - -//let request; -let cancelRequest; -class Test extends Component { - click = () => { - //axios.abort('q'); - // if (request) request.abort(); - - // request = axios({ - // url: '/api/test', - // method: 'get', - // requestId: 'q' - // }); - - // request.then(function(){ - // alert(1) - // }); - - // if (cancelRequest) cancelRequest(); - - // let promise = new Promise((resolve) => { - // cancelRequest = resolve - // }); - - // // window.addEventListener('beforeunload', (e) => { - // // cancelRequest() - // // }) - - // axios.get('/api/test', { - // timeout: 1000 - // }).then(function(){ - // alert(1); - // }); - if (this.cancellation) this.cancellation.cancel('test'); - - this.cancellation = new Cancellation(); - axios('/api/test', { - adapter: cancelXhrAdapter, - cancellation: this.cancellation - }).then(({data}) => { - this.cancellation = null; - alert('ok') - }).catch((e) => { - console.log(e) - }); - - - // var config = { - // method: 'get', - // url: '/api/test' - // }; - // axios(config); - // console.log(config); - - } - - render() { - return ( -
- -
- -
-
-
- ); - } -} - -export default Test; diff --git a/frontend/src/containers/UsersList/UsersTable/index.jsx b/frontend/src/containers/UsersList/UsersTable/index.jsx index 139d697..2221db8 100644 --- a/frontend/src/containers/UsersList/UsersTable/index.jsx +++ b/frontend/src/containers/UsersList/UsersTable/index.jsx @@ -3,8 +3,9 @@ import { Table, Button } from 'react-bootstrap'; import { observer } from 'mobx-react'; -export default observer(['app'], ({ - app: { users, removeUser, currentUser }, +export default observer(['app', 'auth'], ({ + app: { users, removeUser }, + auth: { userId }, }) => { return (
@@ -29,7 +30,7 @@ export default observer(['app'], ({
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 8ee7269..cfa5067 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -9,9 +9,11 @@ import createStore from './reduxApp/create.jsx'; import { Provider as MOBXPRovider } from 'mobx-react'; import App from 'App'; +import Auth from 'Auth'; import TaskList from 'containers/TasksList/state'; import TaskDetails from 'containers/TaskDetails/state'; import TaskAdd from 'containers/TaskAdd/state'; +import ProjectsList from 'containers/ProjectsList/state'; const getRoutes = require('./routes'); @@ -52,6 +54,8 @@ window.onload = () => { taskList: new TaskList(), taskDetails: new TaskDetails(), taskAdd: new TaskAdd(), + projectsList: new ProjectsList(), + auth: new Auth(), }; render(( diff --git a/frontend/src/reduxApp/modules/app.jsx b/frontend/src/reduxApp/modules/app.jsx index eecfb35..3bdfd48 100644 --- a/frontend/src/reduxApp/modules/app.jsx +++ b/frontend/src/reduxApp/modules/app.jsx @@ -15,7 +15,7 @@ export function reducer(state = initState, action) { } import { push } from 'redux-router'; -import { openPopup } from 'containers/ProjectsList/state'; +//import { openPopup } from 'containers/ProjectsList/state'; const getAllUsers = () => ({ type: 'LOAD_USERS', @@ -36,5 +36,5 @@ export const appStart = () => (dispatch) => dispatch(loadUsers()); export const showAddProject = () => (dispatch) => { dispatch(push('/')); - dispatch(openPopup()); + // dispatch(openPopup()); }; diff --git a/frontend/src/reduxApp/modules/reducer.jsx b/frontend/src/reduxApp/modules/reducer.jsx index f1834be..033600a 100644 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ b/frontend/src/reduxApp/modules/reducer.jsx @@ -3,15 +3,15 @@ import { routerStateReducer } from 'redux-router'; import { reducer as formReducer } from 'redux-form'; import { reducer as auth } from './auth'; -import { reducer as app } from './app'; +//import { reducer as app } from './app'; //import { reducer as tasksAdd } from 'containers/TaskAdd/state'; //import { reducer as tasksDetails } from 'containers/TaskDetails/state'; -import { reducer as projectsList } from 'containers/ProjectsList/state'; +//import { reducer as projectsList } from 'containers/ProjectsList/state'; export default combineReducers({ - app, +// app, // tasksDetails, - projectsList, +// projectsList, //tasksAdd, auth, diff --git a/frontend/src/routes.jsx b/frontend/src/routes.jsx index 63215da..a4407dc 100644 --- a/frontend/src/routes.jsx +++ b/frontend/src/routes.jsx @@ -12,29 +12,27 @@ import DashboardLayout from 'containers/Application/DashboardLayout/'; import TasksList from 'containers/TasksList/'; import TaskAdd from 'containers/TaskAdd/'; import TaskDetails from 'containers/TaskDetails'; -import Test from 'containers/Test/'; import App from 'components/Layouts/App'; module.exports = ({ dispatch, getState, app }) => (
- - - - - - - - - - - - - + + + + + + + + + + + - - + + +
); From 813941e6dd004034088ffa8fed6fe830bedf2dc5 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 17 Oct 2016 00:52:04 +0300 Subject: [PATCH 17/18] remove redux --- .DS_Store | Bin 8196 -> 8196 bytes frontend/.DS_Store | Bin 6148 -> 6148 bytes frontend/src/.DS_Store | Bin 6148 -> 6148 bytes frontend/src/HOC/AuthenticatedComponent.jsx | 10 --- frontend/src/{ => Stores}/App.jsx | 2 +- frontend/src/{ => Stores}/Auth.jsx | 0 frontend/src/Stores/index.jsx | 22 +++++ .../DashboardLayout/DashboardMenu.jsx | 9 +-- .../Application/TasksLayout/TasksMenu.jsx | 54 ++++++------- .../Application/TasksLayout/index.jsx | 4 +- .../ProjectsList/ProjectsList/index.jsx | 12 --- .../src/containers/ProjectsList/index.jsx | 4 - frontend/src/containers/UsersList/index.jsx | 2 - frontend/src/main.jsx | 67 ++++------------ frontend/src/reduxApp/create.jsx | 38 --------- frontend/src/reduxApp/middleware/errors.jsx | 1 - frontend/src/reduxApp/modules/app.jsx | 40 ---------- frontend/src/reduxApp/modules/auth.jsx | 75 ------------------ frontend/src/reduxApp/modules/reducer.jsx | 20 ----- frontend/src/routes.jsx | 4 +- 20 files changed, 66 insertions(+), 298 deletions(-) rename frontend/src/{ => Stores}/App.jsx (96%) rename frontend/src/{ => Stores}/Auth.jsx (100%) create mode 100644 frontend/src/Stores/index.jsx delete mode 100644 frontend/src/reduxApp/create.jsx delete mode 100644 frontend/src/reduxApp/middleware/errors.jsx delete mode 100644 frontend/src/reduxApp/modules/app.jsx delete mode 100644 frontend/src/reduxApp/modules/auth.jsx delete mode 100644 frontend/src/reduxApp/modules/reducer.jsx diff --git a/.DS_Store b/.DS_Store index 0d09ff42d5693da0d4190b3f1c401a40acddd067..549bdf7689a8041b329e48afcc318d5af85f2d25 100644 GIT binary patch delta 33 ocmZp1XmQvODkNfJVyUBGWNA^Wqfl*OYN4ZGVr;fKQ)o6X0GxRU>;M1& delta 33 ocmZp1XmQvODkNfLVy>fLW?rkKP;F>nY@(xJVQ8>9Q)o6X0Go#h(EtDd diff --git a/frontend/.DS_Store b/frontend/.DS_Store index 1d3ce65947c4aeda0f17136392b0101846b66caa..5eea83b181c98b4c807dc0e3cc1c838aca337984 100644 GIT binary patch delta 103 zcmZoMXffEZorUq|grFoZC$w*&$Eleh$`F&cY&F*Y?UAT2RAIejc3doeOKGAtlBH#U8J5bFpC l8!0tGO<8b#ilU>WrKYE-#mAG81s?&(v#|wK0<#AQ{SP&i97X^D diff --git a/frontend/src/.DS_Store b/frontend/src/.DS_Store index 16cc086eb9c00aeb31822c75c2f0932238b0664f..255486b5a81f2b48802cc0aeae62e2247e73f2e1 100644 GIT binary patch delta 65 zcmZoMXfc=|#>CJ*u~2NHo+2aD!~pBb1|lqz`I%BSe`6M8*~G%kyqTSYp9837^GD|I V%#-;=3>g_F+we$ljuBbG3;B)qu~2NHo+2aL!~p9}j17!HMo)S@D?@JXoXnC+4o(n^ z7m%v1HZ(KUQP4Fsv#ixos5Ue*tvAw9FgG`=trd99!6B__>gXF;P}{X=@sg#>majN^ z9PDuhMn(wDAPl8p)CnL1DOk#ai}G^v^U{H$ARVj>r3@tunG8ATLCf?6D68=w1Q`Es n7UYm(+04zs%>j&}jUT@=Pv#df6aeXO0Ae5wwtRDd$QEV*bRc{^ diff --git a/frontend/src/HOC/AuthenticatedComponent.jsx b/frontend/src/HOC/AuthenticatedComponent.jsx index f06cf28..65f2904 100644 --- a/frontend/src/HOC/AuthenticatedComponent.jsx +++ b/frontend/src/HOC/AuthenticatedComponent.jsx @@ -1,9 +1,5 @@ import React from 'react'; -//import { connect } from 'react-redux'; -//import { pushState } from 'redux-router'; - -//import { checkAuth } from 'reduxApp/modules/auth'; import { observer } from 'mobx-react'; export function requireAuthentication(Component) { @@ -48,10 +44,4 @@ export function requireAuthentication(Component) { return AuthenticatedComponent; - // return connect( - // (state) => ({ - // isAuthenticated: !!state.auth.user, - // }), - // { checkAuth } - // )(AuthenticatedComponent); }; diff --git a/frontend/src/App.jsx b/frontend/src/Stores/App.jsx similarity index 96% rename from frontend/src/App.jsx rename to frontend/src/Stores/App.jsx index 201478e..556d40a 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/Stores/App.jsx @@ -20,7 +20,7 @@ class App { } appStart = () => { - this.loadUsers(); + return this.loadUsers(); } loadUsers = () => { diff --git a/frontend/src/Auth.jsx b/frontend/src/Stores/Auth.jsx similarity index 100% rename from frontend/src/Auth.jsx rename to frontend/src/Stores/Auth.jsx diff --git a/frontend/src/Stores/index.jsx b/frontend/src/Stores/index.jsx new file mode 100644 index 0000000..07a392e --- /dev/null +++ b/frontend/src/Stores/index.jsx @@ -0,0 +1,22 @@ + +import App from 'Stores/App'; +import Auth from 'Stores/Auth'; +import TaskList from 'containers/TasksList/state'; +import TaskDetails from 'containers/TaskDetails/state'; +import TaskAdd from 'containers/TaskAdd/state'; +import ProjectsList from 'containers/ProjectsList/state'; + +const createStores = (initState) => { + const stores = { + app: new App(), + taskList: new TaskList(), + taskDetails: new TaskDetails(), + taskAdd: new TaskAdd(), + projectsList: new ProjectsList(), + auth: new Auth(), + }; + + return stores; +}; + +export default createStores; diff --git a/frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx b/frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx index c36fb68..e177784 100644 --- a/frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx +++ b/frontend/src/containers/Application/DashboardLayout/DashboardMenu.jsx @@ -27,11 +27,4 @@ const DashboardMenu = observer(['projectsList', 'auth'], ({ )); -import { connect } from 'react-redux'; - -import { logout } from 'reduxApp/modules/auth'; - -export default connect( - null, - { logout } -)(DashboardMenu); +export default DashboardMenu; diff --git a/frontend/src/containers/Application/TasksLayout/TasksMenu.jsx b/frontend/src/containers/Application/TasksLayout/TasksMenu.jsx index e8cc667..8ff4772 100644 --- a/frontend/src/containers/Application/TasksLayout/TasksMenu.jsx +++ b/frontend/src/containers/Application/TasksLayout/TasksMenu.jsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Link } from 'react-router'; +import { Link, withRouter } from 'react-router'; import { Navbar, NavBrand, Nav, NavItem } from 'react-bootstrap'; import { LinkContainer } from 'react-router-bootstrap'; @@ -10,35 +10,29 @@ import { observer } from 'mobx-react'; const TasksMenu = observer(['auth'], ({ projectId, auth: { logout }, -}) => ( - - - Task-tracker - - - - -)); +}) => { + return ( + + + Task-tracker + + + + + ) +}); -import { connect } from 'react-redux'; -import { logout } from 'reduxApp/modules/auth'; +export default TasksMenu;//withRouter(TasksMenu); -export default connect( - state => ({ - projectId: state.router.params.projectId, - router: state.router, - }), - { logout } -)(TasksMenu);; diff --git a/frontend/src/containers/Application/TasksLayout/index.jsx b/frontend/src/containers/Application/TasksLayout/index.jsx index 75ed7c6..a1cfd48 100644 --- a/frontend/src/containers/Application/TasksLayout/index.jsx +++ b/frontend/src/containers/Application/TasksLayout/index.jsx @@ -3,9 +3,9 @@ import React from 'react'; import TasksMenu from './TasksMenu'; import Main from 'components/Layouts/Main'; -const TasksLayout = ({ children }) => ( +const TasksLayout = ({ children, params: { projectId } }) => (
} + header={} > {children}
diff --git a/frontend/src/containers/ProjectsList/ProjectsList/index.jsx b/frontend/src/containers/ProjectsList/ProjectsList/index.jsx index 2086062..3d60d29 100644 --- a/frontend/src/containers/ProjectsList/ProjectsList/index.jsx +++ b/frontend/src/containers/ProjectsList/ProjectsList/index.jsx @@ -55,15 +55,3 @@ const ProjectsList = observer(['projectsList'], ({ export default ProjectsList; -// import { connect } from 'react-redux'; - -// import { -// removeProject, -// } from '../state'; - -// export default connect( -// state => ({ -// projects: state.projectsList.projects, -// }), -// { removeProject } -// )(ProjectsList); diff --git a/frontend/src/containers/ProjectsList/index.jsx b/frontend/src/containers/ProjectsList/index.jsx index 3045486..d499279 100644 --- a/frontend/src/containers/ProjectsList/index.jsx +++ b/frontend/src/containers/ProjectsList/index.jsx @@ -20,8 +20,4 @@ class ProjectsListPage extends Component { } } -// import loading from 'HOC/loading'; -// import { showPage } from './state'; - export default ProjectsListPage; -// loading([showPage])(ProjectsListPage); diff --git a/frontend/src/containers/UsersList/index.jsx b/frontend/src/containers/UsersList/index.jsx index 0463d9b..64146a5 100644 --- a/frontend/src/containers/UsersList/index.jsx +++ b/frontend/src/containers/UsersList/index.jsx @@ -9,5 +9,3 @@ const UsersList = () => ( ); export default UsersList; -// import loading from 'HOC/loading'; -// export default loading([])(UsersList); diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index cfa5067..f314add 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -1,69 +1,30 @@ - -import React, { PropTypes } from 'react'; +import React from 'react'; import { render } from 'react-dom'; -import { Provider } from 'react-redux'; -import { ReduxRouter } from 'redux-router'; -import createStore from './reduxApp/create.jsx'; -import { Provider as MOBXPRovider } from 'mobx-react'; +import { Provider } from 'mobx-react'; -import App from 'App'; -import Auth from 'Auth'; -import TaskList from 'containers/TasksList/state'; -import TaskDetails from 'containers/TaskDetails/state'; -import TaskAdd from 'containers/TaskAdd/state'; -import ProjectsList from 'containers/ProjectsList/state'; +import createStores from 'Stores'; +import { browserHistory, Router } from 'react-router'; const getRoutes = require('./routes'); -const Root = ({ store, stores }) => ( +const Root = ({ stores }) => (
- -
- -
- - - {getRoutes(store)} - - -
- -
+ + + {getRoutes()} +
); - -Root.propTypes = { - store: PropTypes.object.isRequired, -}; - -import { appStart } from 'reduxApp/modules/app'; - - window.onload = () => { const root = document.getElementById('app'); - //try { - const store = createStore(); - const app = new App(); - app.appStart(); - const stores = { - app, - taskList: new TaskList(), - taskDetails: new TaskDetails(), - taskAdd: new TaskAdd(), - projectsList: new ProjectsList(), - auth: new Auth(), - }; - render(( - - ), root); - store.dispatch(appStart()); - // } catch (e) { - // const RedBox = require('redbox-react').default; - // render(, root); - // } + const stores = createStores(); + stores.app.appStart(); + render(( + + ), root); }; diff --git a/frontend/src/reduxApp/create.jsx b/frontend/src/reduxApp/create.jsx deleted file mode 100644 index aac3ab6..0000000 --- a/frontend/src/reduxApp/create.jsx +++ /dev/null @@ -1,38 +0,0 @@ - -import { reduxReactRouter } from 'redux-router'; - -import useScroll from 'scroll-behavior'; -import { createHistory } from 'history'; - -import { applyMiddleware, compose, createStore as _createStore } from 'redux'; -import thunk from 'redux-thunk'; -import rootReducer from './modules/reducer'; - -import axios from 'axios'; -import axiosMiddleware from 'redux-axios-middleware'; - -const client = axios.create({ - responseType: 'json', -}); - -client.interceptors.response.use( - (response) => response.data, - (error) => Promise.reject(error.response.data), -); - -export default function createStore() { - const middleware = applyMiddleware(thunk, axiosMiddleware(client)); - - const createHistoryCustom = (options) => useScroll(createHistory({ - queryKey: false, ...options, - })); - - const createStoreWithMiddleware = compose( - middleware, - reduxReactRouter({ createHistory: createHistoryCustom }) - ); - - const _store = createStoreWithMiddleware(_createStore)(rootReducer); - - return _store; -} diff --git a/frontend/src/reduxApp/middleware/errors.jsx b/frontend/src/reduxApp/middleware/errors.jsx deleted file mode 100644 index 479a90a..0000000 --- a/frontend/src/reduxApp/middleware/errors.jsx +++ /dev/null @@ -1 +0,0 @@ -// TODO: add try catch and last log actions diff --git a/frontend/src/reduxApp/modules/app.jsx b/frontend/src/reduxApp/modules/app.jsx deleted file mode 100644 index 3bdfd48..0000000 --- a/frontend/src/reduxApp/modules/app.jsx +++ /dev/null @@ -1,40 +0,0 @@ - -const initState = { - users: [], - statuses: ['new', 'inprogress', 'testing', 'complited'], -}; - -export function reducer(state = initState, action) { - switch (action.type) { - case 'LOAD_USERS_SUCCESS': - return { ...state, users: action.payload }; - - default: - return state; - } -} - -import { push } from 'redux-router'; -//import { openPopup } from 'containers/ProjectsList/state'; - -const getAllUsers = () => ({ - type: 'LOAD_USERS', - payload: { - request: '/api/users', - }, -}); - -export const loadUsers = () => (dispatch) => dispatch(getAllUsers()); - -export const getStatusesOptions = (state) => - state.app.statuses.map(str => ({ value: str, label: str })); - -export const getUserOptions = (state) => - state.app.users.map(user => ({ value: user.id, label: user.name })); - -export const appStart = () => (dispatch) => dispatch(loadUsers()); - -export const showAddProject = () => (dispatch) => { - dispatch(push('/')); - // dispatch(openPopup()); -}; diff --git a/frontend/src/reduxApp/modules/auth.jsx b/frontend/src/reduxApp/modules/auth.jsx deleted file mode 100644 index fc77fae..0000000 --- a/frontend/src/reduxApp/modules/auth.jsx +++ /dev/null @@ -1,75 +0,0 @@ - -const initState = { - user: null, -}; - -export const LOGIN = 'LOGIN'; -import axios from 'axios'; - -export function reducer(state = initState, action) { - switch (action.type) { - case LOGIN: - return state; - - case 'START_SESSION': - return { ...state, user: action.payload }; - - default: - return state; - } -} - -import { push } from 'redux-router'; - - -export function startSession(payload) { - return { - type: 'START_SESSION', - payload, - }; -} - -export const logout = () => dispatch => { - axios.delete('/api/session') - .then(() => dispatch(push('/login'))); -} - -export const login = (form) => (dispatch) => - dispatch({ - types: ['START_SESSION', 'LOGIN_FAIL'], - payload: { - request: { - url: '/api/login', - method: 'post', - data: form, - }, - }, - }).then(() => dispatch(push('/'))); - - -export const registr = (form) => dispatch => - dispatch({ - type: 'REGISTR', - payload: { - request: { - url: '/api/users', - method: 'post', - data: form - }, - }, - }).then(data => dispatch(login(data.payload))); - - -export const checkAuth = () => (dispatch) => { - return axios.get('/api/session') - .then((response) => dispatch(startSession(response.data))) - .catch(() => dispatch(logout())); -}; - -// ({ -// types: ['START_SESSION', 'LOGOUT'], -// payload: { -// request: '/api/session', -// }, -// }); - diff --git a/frontend/src/reduxApp/modules/reducer.jsx b/frontend/src/reduxApp/modules/reducer.jsx deleted file mode 100644 index 033600a..0000000 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import { combineReducers } from 'redux'; -import { routerStateReducer } from 'redux-router'; -import { reducer as formReducer } from 'redux-form'; - -import { reducer as auth } from './auth'; -//import { reducer as app } from './app'; -//import { reducer as tasksAdd } from 'containers/TaskAdd/state'; -//import { reducer as tasksDetails } from 'containers/TaskDetails/state'; -//import { reducer as projectsList } from 'containers/ProjectsList/state'; - -export default combineReducers({ -// app, -// tasksDetails, -// projectsList, - //tasksAdd, - - auth, - router: routerStateReducer, - form: formReducer, -}); diff --git a/frontend/src/routes.jsx b/frontend/src/routes.jsx index a4407dc..38eb7f0 100644 --- a/frontend/src/routes.jsx +++ b/frontend/src/routes.jsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Router, Route, IndexRoute } from 'react-router'; +import { Route, IndexRoute } from 'react-router'; import Login from 'containers/Login/'; import ProjectsList from 'containers/ProjectsList/'; @@ -16,7 +16,7 @@ import TaskDetails from 'containers/TaskDetails'; import App from 'components/Layouts/App'; -module.exports = ({ dispatch, getState, app }) => ( +module.exports = () => (
From 26086376d6f76897be6cabf861648f6d516bcfee Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 17 Oct 2016 02:54:29 +0300 Subject: [PATCH 18/18] proof of concept --- frontend/src/containers/Test/index.jsx | 137 +++++++++++++++++++++++++ frontend/src/routes.jsx | 4 +- 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 frontend/src/containers/Test/index.jsx diff --git a/frontend/src/containers/Test/index.jsx b/frontend/src/containers/Test/index.jsx new file mode 100644 index 0000000..e45a974 --- /dev/null +++ b/frontend/src/containers/Test/index.jsx @@ -0,0 +1,137 @@ +import React, { Component } from 'react'; +import axios from 'axios'; + +function rest(propsName, url) { + return (WComponent) => { + class Wrapper extends Component { + constructor(props) { + super(props); + this.state = { + items: [], + loading: true, + item: null, + }; + } + + featchOne = (id) => { + const _url = url(this.props.params); + this.setState({ loading: true }); + return axios.get(`${_url}/${id}`) + .then(response => response.data) + .then(item => this.setState({ item, loading: false })); + } + + featch = () => { + return axios.get(url(this.props.params)) + .then(response => { + this.setState({ items: response.data }); + }); + } + add = (data) => { + return axios.post(url(this.props.params), data) + .then(response => response.data) + .then(item => this.push(item)); + } + + push = (item) => { + const items = this.state.items.concat([item]); + this.setState({ items }); + return item; + } + + removeLocal = (item) => { + const items = this.state.items.filter(_item => _item.id !== item.id); + this.setState({ items }); + } + + remove = (item) => { + return axios.delete(`${url(this.props.params)}/${item.id}`) + .then(() => this.removeLocal(item)); + } + + componentDidMount() { + } + render() { + const { + items, + item, + loading, + } = this.state; + + const props = { + [propsName]: { + items, + featch: this.featch, + add: this.add, + featchOne: this.featchOne, + remove: this.remove, + loading, + item, + }, + ...this.props, + }; + + return ( + + ); + } + } + + return Wrapper; + }; +} + + +@rest('comments', ({ id }) => `/api/tasks/${id}/comments`) +@rest('tasks', ({ id }) => '/api/tasks') +class Test extends Component { + componentDidMount() { + this.props.comments.featch(); + this.props.tasks.featchOne(this.props.params.id); + } + + addProject = (e) => { + const text = this.refs.input.value; + this.props.comments.add({ text, userName: 'vasa' }); + this.refs.input.value = ''; + } + + render() { + const { + items, + remove, + } = this.props.comments; + + const { + item, + loading, + } = this.props.tasks; + + const rows = items.map(comment => ( +
+ {comment.text} + +
+ )); + + return ( +
+ {!loading &&
+

{item.title}

+
} +
+ {rows} +
+
+ + +
+
+ ); + } +} + +export default Test; + diff --git a/frontend/src/routes.jsx b/frontend/src/routes.jsx index 38eb7f0..84c27f9 100644 --- a/frontend/src/routes.jsx +++ b/frontend/src/routes.jsx @@ -12,13 +12,15 @@ import DashboardLayout from 'containers/Application/DashboardLayout/'; import TasksList from 'containers/TasksList/'; import TaskAdd from 'containers/TaskAdd/'; import TaskDetails from 'containers/TaskDetails'; - +import Test from 'containers/Test/'; import App from 'components/Layouts/App'; module.exports = () => (
+ +