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