From f08ab06d9ab45b8c2b17540778d9e7d96fe046f8 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 1 Oct 2016 04:15:14 +0300 Subject: [PATCH 1/3] 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 4c360c47c089e768f81d6e833ad2f8ba4aec62fb Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 2 Oct 2016 23:58:37 +0300 Subject: [PATCH 2/3] 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 56007349c0f10ce3b75daf3bd77af033299707d5 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 20 Oct 2016 01:46:07 +0300 Subject: [PATCH 3/3] try multireducer --- frontend/package.json | 1 + .../ProjectsList/AddProjectModal/index.jsx | 2 +- .../src/containers/ProjectsList/state.jsx | 21 +++- .../TaskDetails/CommentsList/index.jsx | 2 +- .../containers/TaskDetails/TaskInfo/index.jsx | 2 +- frontend/src/containers/TaskDetails/state.jsx | 57 ++++------- .../containers/TasksList/TaskPager/index.jsx | 16 +-- .../containers/TasksList/TaskTable/index.jsx | 2 +- frontend/src/containers/TasksList/state.jsx | 35 ++----- frontend/src/reduxApp/modules/popup.jsx | 21 ++++ frontend/src/reduxApp/modules/reducer.jsx | 13 +++ frontend/src/reduxApp/modules/rest.jsx | 97 +++++++++++++++++++ 12 files changed, 189 insertions(+), 80 deletions(-) create mode 100644 frontend/src/reduxApp/modules/popup.jsx create mode 100644 frontend/src/reduxApp/modules/rest.jsx diff --git a/frontend/package.json b/frontend/package.json index 1641887..d37520d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -36,6 +36,7 @@ "mobx-react": "^3.0.4", "mobx-react-devtools": "^4.0.2", "moment": "^2.10.6", + "multireducer": "^3.0.1", "react": "0.14.3", "react-addons-linked-state-mixin": "15.3.0", "react-bootstrap": "0.27.0", diff --git a/frontend/src/containers/ProjectsList/AddProjectModal/index.jsx b/frontend/src/containers/ProjectsList/AddProjectModal/index.jsx index ad2154a..37d8e6e 100644 --- a/frontend/src/containers/ProjectsList/AddProjectModal/index.jsx +++ b/frontend/src/containers/ProjectsList/AddProjectModal/index.jsx @@ -72,7 +72,7 @@ import { export default connect( state => ({ - show: state.projectsList.popupOpen, + show: state.popups.addProject.show, users: state.app.users, }), { onHide: closePopup, addProject } diff --git a/frontend/src/containers/ProjectsList/state.jsx b/frontend/src/containers/ProjectsList/state.jsx index 475754a..a092efe 100644 --- a/frontend/src/containers/ProjectsList/state.jsx +++ b/frontend/src/containers/ProjectsList/state.jsx @@ -33,9 +33,26 @@ export const showPage = () => (dispatch) => dispatch(loadProjects()); export const removeProject = ({ id }) => (dispatch) => http.del(`/api/projects/${id}`).then(() => dispatch(loadProjects())); +import * as popup from 'reduxApp/modules/popup'; -export const openPopup = () => ({ type: 'PROJECTS_OPEN_POPUP' }); -export const closePopup = () => ({ type: 'PROJECTS_CLOSE_POPUP' }); +import { wrapDispatch as wd } from 'multireducer'; + +function getActions(actions, name) { + const wdActions = {}; + for (const key in actions) { + if (actions[key]) { + wdActions[key] = (...arg) => + (dispatch) => wd(dispatch, 'addProject')(actions[key](arg)); + } + } + + return wdActions; +} + +const _addProject = getActions(popup, 'addProject'); + +export const openPopup = _addProject.open; +export const closePopup = _addProject.close; export const addProject = (title, userIds) => (dispatch) => http.post('/api/projects', { title }) diff --git a/frontend/src/containers/TaskDetails/CommentsList/index.jsx b/frontend/src/containers/TaskDetails/CommentsList/index.jsx index 266643a..2939f10 100644 --- a/frontend/src/containers/TaskDetails/CommentsList/index.jsx +++ b/frontend/src/containers/TaskDetails/CommentsList/index.jsx @@ -33,7 +33,7 @@ import { deleteComment } from '../state'; export default connect( state => ({ - comments: state.tasksDetails.comments, + comments: state.rest.comments.items, }), { deleteComment } )(CommentsList); diff --git a/frontend/src/containers/TaskDetails/TaskInfo/index.jsx b/frontend/src/containers/TaskDetails/TaskInfo/index.jsx index 90cf29a..77ffcaa 100644 --- a/frontend/src/containers/TaskDetails/TaskInfo/index.jsx +++ b/frontend/src/containers/TaskDetails/TaskInfo/index.jsx @@ -25,7 +25,7 @@ import { deleteTask } from '../state'; export default connect( state => ({ - task: state.tasksDetails.task, + task: state.rest.tasks.item, }), { deleteTask } )(TaskInfo); diff --git a/frontend/src/containers/TaskDetails/state.jsx b/frontend/src/containers/TaskDetails/state.jsx index e650e87..65fcbf1 100644 --- a/frontend/src/containers/TaskDetails/state.jsx +++ b/frontend/src/containers/TaskDetails/state.jsx @@ -1,58 +1,35 @@ -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))); +import { getActions } from 'reduxApp/modules/rest'; +const comments = getActions('/api/tasks/:id/comments', 'comments'); +const tasks = getActions('/api/tasks', 'tasks'); // 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(tasks.remove({ 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))); + return dispatch(comments.create({ text, userName: name })) + .then(() => dispatch(comments.loadAll())); }; -export const showPage = ({ id }) => (dispatch) => - Promise.all([ - dispatch(loadComments(id)), - dispatch(loadTask(id)), +export const deleteComment = (comment) => (dispatch, getState) => + dispatch(comments.remove(comment)) + .then(() => dispatch(comments.loadAll())); + +export const showPage = ({ id }) => (dispatch) => { + comments.setUrl(`/api/tasks/${id}/comments`); + return Promise.all([ + dispatch(comments.loadAll()), + dispatch(tasks.loadOne(id)), ]); +}; diff --git a/frontend/src/containers/TasksList/TaskPager/index.jsx b/frontend/src/containers/TasksList/TaskPager/index.jsx index bdbf647..4b4972f 100644 --- a/frontend/src/containers/TasksList/TaskPager/index.jsx +++ b/frontend/src/containers/TasksList/TaskPager/index.jsx @@ -18,14 +18,18 @@ TaskPager.propTypes = { onSelect: PropTypes.func, }; -import { changePage } from '../state'; +//import { changePage } from '../state'; +import { bindActionCreators } from 'multireducer'; + +import { getActions } from 'reduxApp/modules/rest'; + +const tasks = getActions('/api/tasks'); +//const mapDispatchToProps = (dispatch) => bindActionCreators({ changePage }, dispatch, 'tasks') export default connect( state => ({ - items: state.tasksList.items, - activePage: state.tasksList.page, + items: state.rest.tasks.totalPages, + activePage: state.rest.tasks.page, }), - { - onSelect: changePage, - } + (dispatch) => bindActionCreators({ onSelect: tasks.changePage }, dispatch, 'tasks') )(TaskPager); diff --git a/frontend/src/containers/TasksList/TaskTable/index.jsx b/frontend/src/containers/TasksList/TaskTable/index.jsx index 0b06b8d..f288ace 100644 --- a/frontend/src/containers/TasksList/TaskTable/index.jsx +++ b/frontend/src/containers/TasksList/TaskTable/index.jsx @@ -41,7 +41,7 @@ TaskTable.propTypes = { import { connect } from 'react-redux'; export default connect( state => ({ - tasks: state.tasksList.tasks, + tasks: state.rest.tasks.items, projectId: state.router.params.projectId, }) )(TaskTable); diff --git a/frontend/src/containers/TasksList/state.jsx b/frontend/src/containers/TasksList/state.jsx index 373ddf6..cb8e876 100644 --- a/frontend/src/containers/TasksList/state.jsx +++ b/frontend/src/containers/TasksList/state.jsx @@ -2,10 +2,6 @@ const initState = { userId: null, status: 'new', - - tasks: [], - page: 1, - items: 0, }; export function reducer(state = initState, action) { @@ -14,52 +10,35 @@ export function reducer(state = initState, action) { 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 }); +import { getActions } from 'reduxApp/modules/rest'; + +const tasks = getActions('/api/tasks', 'tasks'); const loadTasks = () => (dispatch, getState) => { const { tasksList: { userId, status, - page, }, -// router: { params: { projectId } }, } = getState(); - const query = toParams({ + const params = { assignee: userId, status, - }); - return http.get(`/api/tasks/page/${page}/5?${query}`) - .then(payload => dispatch(setTasks(payload))); + }; + return dispatch(tasks.getPage(params)); }; -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()); +export const showPage = () => (dispatch, getState) => dispatch(loadTasks()); diff --git a/frontend/src/reduxApp/modules/popup.jsx b/frontend/src/reduxApp/modules/popup.jsx new file mode 100644 index 0000000..eb4caa1 --- /dev/null +++ b/frontend/src/reduxApp/modules/popup.jsx @@ -0,0 +1,21 @@ + +const initState = { + show: false, +}; + +export function reducer(state = initState, action) { + switch (action.type) { + case 'OPEN_POPUP': + return { ...state, show: true }; + + case 'CLOSE_POPUP': + return { ...state, show: false }; + + default: + return state; + } +} + +export const open = () => ({ type: 'OPEN_POPUP' }); +export const close = () => ({ type: 'CLOSE_POPUP' }); + diff --git a/frontend/src/reduxApp/modules/reducer.jsx b/frontend/src/reduxApp/modules/reducer.jsx index f9265d3..46584fd 100644 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ b/frontend/src/reduxApp/modules/reducer.jsx @@ -9,6 +9,10 @@ import { reducer as tasksList } from 'containers/TasksList/state'; import { reducer as tasksDetails } from 'containers/TaskDetails/state'; import { reducer as projectsList } from 'containers/ProjectsList/state'; +import multireducer from 'multireducer'; +import { reducer as REST } from './rest'; +import { reducer as popup } from './popup'; + export default combineReducers({ app, tasksDetails, @@ -16,6 +20,15 @@ export default combineReducers({ projectsList, tasksAdd, + rest: multireducer({ + tasks: REST, + comments: REST, + }), + + popups: multireducer({ + addProject: popup, + }), + auth, router: routerStateReducer, form: formReducer, diff --git a/frontend/src/reduxApp/modules/rest.jsx b/frontend/src/reduxApp/modules/rest.jsx new file mode 100644 index 0000000..f46f3ff --- /dev/null +++ b/frontend/src/reduxApp/modules/rest.jsx @@ -0,0 +1,97 @@ + +const initState = { + items: [], + item: null, + + page: 1, + totalPages: 1, +}; + +export function reducer(state = initState, action) { + switch (action.type) { + case 'ITEMS_LOADED': { + return { ...state, items: action.payload }; + } + + case 'SET_ITEMS_PAGE': { + const { count, items } = action.payload; + return { ...state, items, totalPages: Math.ceil(count / 10) }; + } + + case 'SET_ITEM': { + return { ...state, item: action.payload }; + } + + case 'SET_PAGE': + return { ...state, page: action.payload }; + + default: + return state; + } +} + +import http, { toParams } from 'utils/http'; + +function _getPage(url, params = {}) { + return (dispatch, getState) => { + const page = getState().rest.tasks.page; + const query = toParams(params); + return http.get(`/api/tasks/page/${page}/5?${query}`) + .then(payload => { + dispatch({ type: 'SET_ITEMS_PAGE', payload }); + }); + }; +} + +const setItems = (items) => ({ type: 'ITEMS_LOADED', payload: items }); + +function _loadAll(url) { + return (dispatch) => { + http.get(url).then(data => { + dispatch(setItems(data)); + }); + }; +} + +function _loadOne(url, id) { + return (dispatch) => { + return http.get(`${url}/${id}`).then(data => { + dispatch({ type: 'SET_ITEM', payload: data }); + }); + }; +} + +// http.del(`/api/tasks/${id}/comments/${comment.id}`) + +function _create(url, data) { + return () => http.post(url, data); +} + +function _remove(url, item) { + return () => http.del(`${url}/${item.id}`); +} + +const setPage = (page) => ({ type: 'SET_PAGE', payload: page }); + +import { wrapDispatch as wd } from 'multireducer'; + +export function getActions(url, name) { + let _url = url; + return { + setUrl: (newUrl) => { + _url = newUrl; + return _url; + }, + changePage: (page) => (dispatch) => { + dispatch(setPage(page)); + return dispatch(_getPage(_url)); + }, + getPage: (params) => dispatch => wd(dispatch, name)(_getPage(_url, params)), + loadAll: (params) => dispatch => wd(dispatch, name)(_loadAll(_url, params)), + loadOne: (id) => dispatch => wd(dispatch, name)(_loadOne(_url, id)), + create: (data) => dispatch => wd(dispatch, name)(_create(_url, data)), + remove: (item) => dispatch => wd(dispatch, name)(_remove(_url, item)), + }; +} + +