From 3538f3c06b406c4307a7be290eedc1d9ab6589b4 Mon Sep 17 00:00:00 2001 From: Alexandru Munteanu <alexandru.munt@gmail.com> Date: Fri, 23 Mar 2018 14:42:38 +0200 Subject: [PATCH] feat(assign-he-modal): assign handling editor flow --- packages/component-invite/config/default.js | 2 +- .../component-modal/src/components/Modal.js | 2 +- .../src/components/withModal.js | 6 +- .../src/components/Admin/AddUserForm.js | 2 +- .../src/components/Dashboard/AssignEditor.js | 20 +++ .../src/components/Dashboard/AssignHEModal.js | 164 ++++++++++++++++++ .../src/components/Dashboard/Dashboard.js | 5 - .../src/components/Dashboard/DashboardCard.js | 55 +++--- .../src/components/Dashboard/DashboardPage.js | 3 +- packages/components-faraday/src/index.js | 1 + .../components-faraday/src/redux/editors.js | 31 ++++ .../components-faraday/src/redux/index.js | 1 + 12 files changed, 253 insertions(+), 39 deletions(-) create mode 100644 packages/components-faraday/src/components/Dashboard/AssignEditor.js create mode 100644 packages/components-faraday/src/components/Dashboard/AssignHEModal.js create mode 100644 packages/components-faraday/src/redux/editors.js diff --git a/packages/component-invite/config/default.js b/packages/component-invite/config/default.js index 6da7762b0..350b9de77 100644 --- a/packages/component-invite/config/default.js +++ b/packages/component-invite/config/default.js @@ -8,7 +8,7 @@ module.exports = { 'http://localhost:3000/invite', }, roles: { - global: ['admin', 'editorInChief', 'author'], + global: ['admin', 'editorInChief', 'author', 'handlingEditor'], collection: ['handlingEditor', 'reviewer'], inviteRights: { admin: ['admin', 'editorInChief', 'author'], diff --git a/packages/component-modal/src/components/Modal.js b/packages/component-modal/src/components/Modal.js index b16774202..4015782fe 100644 --- a/packages/component-modal/src/components/Modal.js +++ b/packages/component-modal/src/components/Modal.js @@ -40,5 +40,5 @@ const ModalRoot = styled.div` justify-content: center; background-color: ${({ overlayColor }) => overlayColor || 'rgba(0, 0, 0, 0.8)'}; - z-index: ${({ theme }) => theme.modalIndex}; + /* z-index: ${({ theme }) => theme.modalIndex}; */ ` diff --git a/packages/component-modal/src/components/withModal.js b/packages/component-modal/src/components/withModal.js index 1fb9a2261..0ee82669d 100644 --- a/packages/component-modal/src/components/withModal.js +++ b/packages/component-modal/src/components/withModal.js @@ -10,7 +10,7 @@ const mapState = state => ({ }) const mapDispatch = dispatch => ({ - hideModal: () => dispatch(setModalVisibility(false)), + hideModal: e => dispatch(setModalVisibility(false)), showModal: (modalProps = {}) => dispatch(setModalVisibility(true, modalProps)), }) @@ -21,7 +21,7 @@ const withModal = ({ }) => WrappedComponent => connect(mapState, mapDispatch)( ({ modalVisible, modalProps, hideModal, ...rest }) => ( - <div> + <React.Fragment> {modalVisible && ( <Modal {...modalProps} @@ -31,7 +31,7 @@ const withModal = ({ /> )} <WrappedComponent hideModal={hideModal} {...rest} /> - </div> + </React.Fragment> ), ) diff --git a/packages/components-faraday/src/components/Admin/AddUserForm.js b/packages/components-faraday/src/components/Admin/AddUserForm.js index df8e96e05..ba6dd5a17 100644 --- a/packages/components-faraday/src/components/Admin/AddUserForm.js +++ b/packages/components-faraday/src/components/Admin/AddUserForm.js @@ -13,7 +13,7 @@ const emailValidator = value => const AddUserForm = ({ roles, journal, error }) => { const roleOptions = roles.filter(r => - ['editorInChief', 'author', 'admin'].includes(r.value), + ['editorInChief', 'author', 'admin', 'handlingEditor'].includes(r.value), ) return ( <div> diff --git a/packages/components-faraday/src/components/Dashboard/AssignEditor.js b/packages/components-faraday/src/components/Dashboard/AssignEditor.js new file mode 100644 index 000000000..1e05fedce --- /dev/null +++ b/packages/components-faraday/src/components/Dashboard/AssignEditor.js @@ -0,0 +1,20 @@ +import React from 'react' +import { compose, withHandlers } from 'recompose' +import { withModal } from 'pubsweet-component-modal/src/components' + +import HEModal from './AssignHEModal' + +const AssignEditor = ({ assign }) => <button onClick={assign}>ASSIGN</button> + +export default compose( + withModal({ + modalComponent: HEModal, + }), + withHandlers({ + assign: ({ showModal, collectionId }) => () => { + showModal({ + collectionId, + }) + }, + }), +)(AssignEditor) diff --git a/packages/components-faraday/src/components/Dashboard/AssignHEModal.js b/packages/components-faraday/src/components/Dashboard/AssignHEModal.js new file mode 100644 index 000000000..f6694a7e6 --- /dev/null +++ b/packages/components-faraday/src/components/Dashboard/AssignHEModal.js @@ -0,0 +1,164 @@ +/* eslint react/prefer-stateless-function: 0 */ + +import React from 'react' +import { th } from '@pubsweet/ui' +import { compose } from 'recompose' +import { connect } from 'react-redux' +import styled from 'styled-components' + +import { handlingEditors, assignHandlingEditor } from '../../redux/editors' + +class AssignHEModal extends React.Component { + state = { + searchInput: '', + } + + changeInput = e => { + this.setState({ searchInput: e.target.value }) + } + + filterEditors = editors => { + const { searchInput } = this.state + return editors.filter(({ firstName, lastName, email }) => + `${firstName} ${lastName} ${email}` + .toLowerCase() + .includes(searchInput.trim()), + ) + } + + assignEditor = email => () => { + const { assignHandlingEditor, collectionId, hideModal } = this.props + assignHandlingEditor(email, collectionId).then(hideModal) + } + + render() { + const { searchInput } = this.state + const { editors } = this.props + const filteredEditors = this.filterEditors(editors) + return ( + <RootModal> + <ModalTitle>Assign Handling Editor</ModalTitle> + <ModalHeader> + <span>HANDLING EDITORS</span> + <SearchInput + onChange={this.changeInput} + type="text" + value={searchInput} + /> + </ModalHeader> + <ScrollContainer> + <ModalContent> + {filteredEditors.map(({ firstName, lastName, email }, index) => ( + <SuggestedEditor + isLast={index === filteredEditors.length - 1} + key={email} + > + <EditorDetails> + <span>{`${firstName} ${lastName}`}</span> + <span>{email}</span> + </EditorDetails> + <AssignButton onClick={this.assignEditor(email)}> + ASSIGN + </AssignButton> + </SuggestedEditor> + ))} + </ModalContent> + </ScrollContainer> + </RootModal> + ) + } +} + +export default compose( + connect( + state => ({ + editors: handlingEditors(state), + }), + { assignHandlingEditor }, + ), +)(AssignHEModal) + +// #region styled-components +const EditorDetails = styled.div` + display: flex; + flex-direction: column; +` +const SuggestedEditor = styled.div` + align-items: center; + border: ${th('borderDefault')}; + border-bottom: ${({ isLast }) => (isLast ? th('borderDefault') : 'none')}; + display: flex; + justify-content: space-between; + padding: ${th('subGridUnit')}; + height: calc(${th('gridUnit')} * 2); + + &:hover { + background-color: ${th('colorSecondary')}; + } +` + +const AssignButton = styled.button` + align-items: center; + color: ${th('colorTextReverse')}; + background-color: ${th('colorPrimary')}; + display: flex; + justify-content: center; + height: ${th('gridUnit')}; + width: calc(${th('gridUnit')} * 4); + opacity: 0; + + ${SuggestedEditor}:hover & { + opacity: 1; + } +` + +const RootModal = styled.div` + align-items: center; + background-color: ${th('colorBackgroundHue')}; + display: flex; + flex-direction: column; + justify-content: flex-start; + height: calc(${th('gridUnit')} * 18); + padding: calc(${th('subGridUnit')} * 8) calc(${th('subGridUnit')} * 6); + width: calc(${th('gridUnit')} * 24); +` + +const ModalTitle = styled.span` + color: ${th('colorPrimary')}; + font-size: ${th('fontSizeHeading4')}; + font-family: ${th('fontHeading')}; + margin-bottom: calc(${th('subGridUnit')} * 5); +` + +const ModalHeader = styled.div` + align-self: stretch; + display: flex; + flex-direction: column; + + & span { + color: ${th('colorPrimary')}; + font-size: ${th('fontSizeBase')}; + margin-bottom: ${th('subGridUnit')}; + } +` + +const SearchInput = styled.input` + border: 4px solid gray; + height: calc(${th('subGridUnit')} * 5); + padding: ${th('subGridUnit')}; +` + +const ScrollContainer = styled.div` + align-self: stretch; + flex: 1; + overflow: auto; + border: ${th('borderDefault')}; +` + +const ModalContent = styled.div` + display: flex; + flex-direction: column; + overflow: auto; + padding: calc(${th('subGridUnit')} * 2) calc(${th('subGridUnit')} * 3); +` +// #endregion diff --git a/packages/components-faraday/src/components/Dashboard/Dashboard.js b/packages/components-faraday/src/components/Dashboard/Dashboard.js index d609c746c..bae7748ed 100644 --- a/packages/components-faraday/src/components/Dashboard/Dashboard.js +++ b/packages/components-faraday/src/components/Dashboard/Dashboard.js @@ -3,9 +3,7 @@ import { get } from 'lodash' import { Button } from '@pubsweet/ui' import styled from 'styled-components' import { compose, withHandlers } from 'recompose' -import { withModal } from 'pubsweet-component-modal/src/components' -import AbstractModal from './AbstractModal' import DashboardItems from './DashboardItems' import DashboardFilters from './DashboardFilters' @@ -49,9 +47,6 @@ const Dashboard = ({ ) export default compose( - withModal({ - modalComponent: AbstractModal, - }), withHandlers({ showAbstractModal: ({ showModal }) => metadata => () => { showModal({ diff --git a/packages/components-faraday/src/components/Dashboard/DashboardCard.js b/packages/components-faraday/src/components/Dashboard/DashboardCard.js index af8cd34a4..548e2283d 100644 --- a/packages/components-faraday/src/components/Dashboard/DashboardCard.js +++ b/packages/components-faraday/src/components/Dashboard/DashboardCard.js @@ -3,15 +3,12 @@ import { get } from 'lodash' import PropTypes from 'prop-types' import { Button, Icon, th } from '@pubsweet/ui' import styled, { css } from 'styled-components' -import { compose, getContext, withHandlers } from 'recompose' -import { - withModal, - ConfirmationModal, -} from 'pubsweet-component-modal/src/components' +import { compose, getContext } from 'recompose' import { parseVersion, parseJournalIssue } from './utils' import ZipFiles from './ZipFiles' +import AssignEditor from './AssignEditor' const DashboardCard = ({ deleteProject, @@ -130,6 +127,10 @@ const DashboardCard = ({ )} </AuthorList> </Top> + <div> + Handling editor + <AssignEditor collectionId={project.id} /> + </div> </DetailsView> )} </Card> @@ -138,28 +139,28 @@ const DashboardCard = ({ export default compose( getContext({ journal: PropTypes.object }), - withModal({ - modalComponent: ConfirmationModal, - }), - withHandlers({ - cancelSubmission: ({ - showModal, - deleteProject, - project, - hideModal, - }) => () => { - const modalConfig = { - onConfirm: () => { - deleteProject(project) - hideModal() - }, - dismissable: false, - title: 'Are you sure you want to delete the manuscript?', - subtitle: 'This operation cannot be undone!', - } - showModal(modalConfig) - }, - }), + // withModal({ + // modalComponent: ConfirmationModal, + // }), + // withHandlers({ + // cancelSubmission: ({ + // showModal, + // deleteProject, + // project, + // hideModal, + // }) => () => { + // const modalConfig = { + // onConfirm: () => { + // deleteProject(project) + // hideModal() + // }, + // dismissable: false, + // title: 'Are you sure you want to delete the manuscript?', + // subtitle: 'This operation cannot be undone!', + // } + // showModal(modalConfig) + // }, + // }), )(DashboardCard) // #region styled-components diff --git a/packages/components-faraday/src/components/Dashboard/DashboardPage.js b/packages/components-faraday/src/components/Dashboard/DashboardPage.js index f183c434e..1e59c3c66 100644 --- a/packages/components-faraday/src/components/Dashboard/DashboardPage.js +++ b/packages/components-faraday/src/components/Dashboard/DashboardPage.js @@ -10,7 +10,7 @@ import { newestFirst, selectCurrentUser } from 'xpub-selectors' import { createDraftSubmission } from 'pubsweet-component-wizard/src/redux/conversion' import Dashboard from './Dashboard' - +import { getHandlingEditors } from '../../redux/editors' import withFilters from './withFilters' export default compose( @@ -18,6 +18,7 @@ export default compose( actions.getCollections(), actions.getTeams(), actions.getUsers(), + getHandlingEditors(), ]), connect( state => { diff --git a/packages/components-faraday/src/index.js b/packages/components-faraday/src/index.js index 10dad0847..1565988f7 100644 --- a/packages/components-faraday/src/index.js +++ b/packages/components-faraday/src/index.js @@ -5,6 +5,7 @@ module.exports = { authors: () => require('./redux/authors').default, files: () => require('./redux/files').default, modal: () => require('./redux/modal').default, + editors: () => require('./redux/editors').default, }, }, } diff --git a/packages/components-faraday/src/redux/editors.js b/packages/components-faraday/src/redux/editors.js new file mode 100644 index 000000000..e026c91b8 --- /dev/null +++ b/packages/components-faraday/src/redux/editors.js @@ -0,0 +1,31 @@ +import { get, create } from 'pubsweet-client/src/helpers/api' + +const SET_HANDLING_EDITORS = 'SET_HANDLING_EDITORS' + +const setHandlingEditors = editors => ({ + type: SET_HANDLING_EDITORS, + editors, +}) + +export const handlingEditors = state => state.editors + +export const getHandlingEditors = () => dispatch => + get(`/users?handlingEditor=true`).then(res => { + dispatch(setHandlingEditors(res.users)) + }) + +export const assignHandlingEditor = (email, collectionId) => dispatch => + create(`/users/invite/${collectionId}`, { + email, + role: 'handlingEditor', + }) + +const initialState = [] +export default (state = initialState, action = {}) => { + switch (action.type) { + case SET_HANDLING_EDITORS: + return action.editors + default: + return state + } +} diff --git a/packages/components-faraday/src/redux/index.js b/packages/components-faraday/src/redux/index.js index bc48f2ff5..8e6ed7c70 100644 --- a/packages/components-faraday/src/redux/index.js +++ b/packages/components-faraday/src/redux/index.js @@ -1,2 +1,3 @@ export { default as modal } from './modal' export { default as authors } from './authors' +export { default as editors } from './editors' -- GitLab