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/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/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/components/TaskPager/index.jsx b/frontend/src/components/TaskPager/index.jsx deleted file mode 100644 index acc64d7..0000000 --- a/frontend/src/components/TaskPager/index.jsx +++ /dev/null @@ -1,20 +0,0 @@ -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/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.users.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..37d8e6e 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.popups.addProject.show, + users: state.app.users, + }), + { onHide: closePopup, addProject } +)(AddProjectModal); 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..8a3fd30 100644 --- a/frontend/src/containers/ProjectsList/index.jsx +++ b/frontend/src/containers/ProjectsList/index.jsx @@ -1,22 +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 ProjectsList from './ProjectsList/'; +import AddProjectModal from './AddProjectModal/'; const ProjectsListPage = () => ( -
} - > +
-
+
); -import loading from 'containers/Application/loading'; -import { loadProducts } from 'reduxApp/modules/projects'; -import { fetchUsers } from 'reduxApp/modules/users'; +import loading from 'HOC/loading'; +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..a092efe --- /dev/null +++ b/frontend/src/containers/ProjectsList/state.jsx @@ -0,0 +1,65 @@ + +const initState = { + projects: [], +}; + +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 }; + + 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())); + +import * as popup from 'reduxApp/modules/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 }) + .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/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..2939f10 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.rest.comments.items, + }), + { 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..77ffcaa --- /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.rest.tasks.item, + }), + { deleteTask } +)(TaskInfo); + + diff --git a/frontend/src/containers/TaskDetails/index.jsx b/frontend/src/containers/TaskDetails/index.jsx index 31cb176..aa90264 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 = () => (
@@ -12,8 +12,7 @@ const TaskDetails = () => (
); -import loading from 'containers/Application/loading'; -import { loadTask } from 'reduxApp/modules/tasks'; -import { loadComments } from 'reduxApp/modules/comments'; +import loading from 'HOC/loading'; +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..65fcbf1 --- /dev/null +++ b/frontend/src/containers/TaskDetails/state.jsx @@ -0,0 +1,35 @@ + +import { push } from 'redux-router'; + + +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 dispatch(tasks.remove({ id })) + .then(() => dispatch(push(`/projects/${projectId}/tasks`))); +}; + +export const addComment = (text) => (dispatch, getState) => { + const { + auth: { user: { name } }, + } = getState(); + + return dispatch(comments.create({ text, userName: name })) + .then(() => dispatch(comments.loadAll())); +}; + +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/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/containers/TasksList/TaskPager/index.jsx b/frontend/src/containers/TasksList/TaskPager/index.jsx new file mode 100644 index 0000000..4b4972f --- /dev/null +++ b/frontend/src/containers/TasksList/TaskPager/index.jsx @@ -0,0 +1,35 @@ +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)} + />} +
+); + +TaskPager.propTypes = { + items: PropTypes.number, + activePage: PropTypes.number, + onSelect: PropTypes.func, +}; + +//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.rest.tasks.totalPages, + activePage: state.rest.tasks.page, + }), + (dispatch) => bindActionCreators({ onSelect: tasks.changePage }, dispatch, 'tasks') +)(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..f288ace 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.rest.tasks.items, + projectId: state.router.params.projectId, + }) +)(TaskTable); diff --git a/frontend/src/containers/TasksList/index.jsx b/frontend/src/containers/TasksList/index.jsx index 0930a4a..786543f 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 = () => (
@@ -14,8 +14,8 @@ const TaskList = () => (
); -import loading from 'containers/Application/loading'; -import { loadTasks } from 'reduxApp/modules/tasks'; -import { fetchUsers } from 'reduxApp/modules/users'; +import loading from 'HOC/loading'; -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..cb8e876 --- /dev/null +++ b/frontend/src/containers/TasksList/state.jsx @@ -0,0 +1,44 @@ + +const initState = { + userId: null, + status: 'new', +}; + +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 }; + + default: + return state; + } +} + + +import { getActions } from 'reduxApp/modules/rest'; + +const tasks = getActions('/api/tasks', 'tasks'); + +const loadTasks = () => (dispatch, getState) => { + const { + tasksList: { + userId, + status, + }, + } = getState(); + const params = { + assignee: userId, + status, + }; + return dispatch(tasks.getPage(params)); +}; + + +export const changeUserId = (id) => ({ type: 'CHANGE_USER_ID', payload: id }); +export const changeStatus = (status) => ({ type: 'CHANGE_STATUS', payload: status }); + +export const makeSearch = () => (dispatch) => dispatch(loadTasks()); +export const showPage = () => (dispatch, getState) => 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/components/UsersTable/index.jsx b/frontend/src/containers/UsersList/UsersTable/index.jsx similarity index 82% rename from frontend/src/components/UsersTable/index.jsx rename to frontend/src/containers/UsersList/UsersTable/index.jsx index 4ba23b6..6accd6d 100644 --- a/frontend/src/components/UsersTable/index.jsx +++ b/frontend/src/containers/UsersList/UsersTable/index.jsx @@ -39,4 +39,14 @@ const UsersTable = ({ ); -export default UsersTable; +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..a6c6c89 100644 --- a/frontend/src/containers/UsersList/index.jsx +++ b/frontend/src/containers/UsersList/index.jsx @@ -1,15 +1,12 @@ import React from 'react'; -import UsersTable from './UsersTable'; -import Main from 'components/Layouts/Main'; -import DashboardMenu from 'containers/Application/DashboardMenu'; +import UsersTable from './UsersTable/'; const UsersList = () => ( -
}> +
-
+
); -import loading from 'containers/Application/loading'; -import { fetchUsers } from 'reduxApp/modules/users'; -export default loading(fetchUsers)(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 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..ced8ce7 --- /dev/null +++ b/frontend/src/reduxApp/modules/app.jsx @@ -0,0 +1,38 @@ + +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'; +import { push } from 'redux-router'; +import { openPopup } from 'containers/ProjectsList/state'; + +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()); + +export const showAddProject = () => (dispatch) => { + dispatch(push('/')); + dispatch(openPopup()); +}; 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/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/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 abad1e8..46584fd 100644 --- a/frontend/src/reduxApp/modules/reducer.jsx +++ b/frontend/src/reduxApp/modules/reducer.jsx @@ -3,18 +3,32 @@ 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'; + +import multireducer from 'multireducer'; +import { reducer as REST } from './rest'; +import { reducer as popup } from './popup'; export default combineReducers({ - taskFilter, - comments, - users, - projects, - tasks, + app, + tasksDetails, + tasksList, + 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)), + }; +} + + 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/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 }) => ( - - - + + + + + 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, }