From 217e57f4d33b21d3117394fe67c7ffe14f2a22a3 Mon Sep 17 00:00:00 2001 From: Alexandru Munteanu <alexandru.munt@gmail.com> Date: Wed, 30 May 2018 15:46:59 +0300 Subject: [PATCH] feat(eic-decision): various improvements on the eic form --- .../src/components/MakeDecision.js | 145 ------------------ .../src/components/ManuscriptLayout.js | 2 + .../src/components/ManuscriptPage.js | 8 +- .../src/components/SideBarActions.js | 13 +- .../src/components/index.js | 1 - .../src/components/MakeDecision/Decision.js | 3 +- .../components/MakeDecision/DecisionForm.js | 103 +++++++------ .../src/components/MakeDecision/utils.js | 38 +++++ .../MakeRecommendation/RecommendWizard.js | 1 + 9 files changed, 116 insertions(+), 198 deletions(-) delete mode 100644 packages/component-manuscript/src/components/MakeDecision.js create mode 100644 packages/components-faraday/src/components/MakeDecision/utils.js diff --git a/packages/component-manuscript/src/components/MakeDecision.js b/packages/component-manuscript/src/components/MakeDecision.js deleted file mode 100644 index e9662b768..000000000 --- a/packages/component-manuscript/src/components/MakeDecision.js +++ /dev/null @@ -1,145 +0,0 @@ -import React from 'react' -import { get } from 'lodash' -import { connect } from 'react-redux' -import { AbstractEditor } from 'xpub-edit' -import { required } from 'xpub-validators' -import { compose, withHandlers } from 'recompose' -import { withModal } from 'pubsweet-component-modal/src/components' -import { - Icon, - Button, - Spinner, - ErrorText, - RadioGroup, - ValidatedField, -} from '@pubsweet/ui' -import { - reduxForm, - isSubmitting, - getFormValues, - change as changeForm, -} from 'redux-form' - -import { - Row, - ModalRoot, - CloseIcon, - ModalTitle, - ActionButton, - CustomRadioGroup, - ButtonsContainer, - SpinnerContainer, -} from '../atoms' - -const options = [ - { - value: 'publish', - label: 'Confirm recommendation to Publish', - }, - { - value: 'reject', - label: 'Confirm recommendation to Reject', - }, - { - value: 'return', - label: 'Return manuscript with comments', - }, -] - -const MakeDecision = ({ showDecisionModal }) => ( - <ActionButton onClick={showDecisionModal}>Make Decision</ActionButton> -) - -// To be removed -const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) - -const DecisionModal = compose( - connect( - state => ({ - isSubmitting: isSubmitting('makeDecision')(state), - decision: get(getFormValues('makeDecision')(state), 'decision'), - }), - { changeForm, getFormValues }, - ), - reduxForm({ - form: 'makeDecision', - onSubmit: (values, dispatch, { isSubmitting }) => - sleep(1000).then(() => { - // TODO: link to backend - window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`) - }), - }), - withHandlers({ - selectOption: ({ changeForm, formValues }) => value => { - changeForm('makeDecision', 'decision', value) - }, - }), -)( - ({ - error, - decision, - hideModal, - handleSubmit, - selectOption, - isSubmitting, - }) => ( - <ModalRoot> - <CloseIcon data-test="icon-modal-hide" onClick={hideModal}> - <Icon primary>x</Icon> - </CloseIcon> - - <ModalTitle>Manuscript Decision</ModalTitle> - <CustomRadioGroup> - <RadioGroup - isRequired - name="decision" - onChange={selectOption} - options={options} - /> - </CustomRadioGroup> - {decision === 'return' && ( - <Row> - <ValidatedField - component={AbstractEditor} - name="comment" - placeholder="Return with comment (required)" - title="Return with comment (required)" - validate={[required]} - /> - </Row> - )} - {error && <ErrorText>{error}</ErrorText>} - <ButtonsContainer> - <Button data-test="button-modal-hide" onClick={hideModal}> - Cancel - </Button> - {isSubmitting ? ( - <SpinnerContainer> - <Spinner size={4} /> - </SpinnerContainer> - ) : ( - <Button - data-test="button-modal-confirm" - onClick={handleSubmit} - primary - > - SEND REQUEST - </Button> - )} - </ButtonsContainer> - </ModalRoot> - ), -) - -export default compose( - connect(null, null), - withModal({ - modalKey: 'EiCMakeDecision', - modalComponent: DecisionModal, - }), - withHandlers({ - showDecisionModal: ({ showModal }) => () => { - showModal() - }, - }), -)(MakeDecision) diff --git a/packages/component-manuscript/src/components/ManuscriptLayout.js b/packages/component-manuscript/src/components/ManuscriptLayout.js index 4b2ced19b..3e7348b21 100644 --- a/packages/component-manuscript/src/components/ManuscriptLayout.js +++ b/packages/component-manuscript/src/components/ManuscriptLayout.js @@ -28,6 +28,7 @@ const ManuscriptLayout = ({ currentUserIs, editorInChief, updateManuscript, + heRecommendation, canSeeEditorialComments, editorialRecommendations, project = {}, @@ -71,6 +72,7 @@ const ManuscriptLayout = ({ <SideBar flex={1}> <SideBarActions currentUserIs={currentUserIs} + heRecommendation={heRecommendation} project={project} version={version} /> diff --git a/packages/component-manuscript/src/components/ManuscriptPage.js b/packages/component-manuscript/src/components/ManuscriptPage.js index 528eb7afe..a75bf7281 100644 --- a/packages/component-manuscript/src/components/ManuscriptPage.js +++ b/packages/component-manuscript/src/components/ManuscriptPage.js @@ -121,9 +121,15 @@ export default compose( ) }, }), - withProps(({ project }) => ({ + withProps(({ version: { recommendations = [] }, project }) => ({ canSeeEditorialComments: ['revisionRequested', 'pendingApproval'].includes( get(project, 'status'), ), + heRecommendation: + recommendations.find( + r => + r.recommendationType === 'editorRecommendation' && + r.userId === get(project, 'handlingEditor.id'), + ) || {}, })), )(ManuscriptLayout) diff --git a/packages/component-manuscript/src/components/SideBarActions.js b/packages/component-manuscript/src/components/SideBarActions.js index a8cc8acf2..f3fecb756 100644 --- a/packages/component-manuscript/src/components/SideBarActions.js +++ b/packages/component-manuscript/src/components/SideBarActions.js @@ -4,19 +4,28 @@ import { connect } from 'react-redux' import styled from 'styled-components' import { th, Icon } from '@pubsweet/ui' import ZipFiles from 'pubsweet-components-faraday/src/components/Files/ZipFiles' +import { Decision } from 'pubsweet-components-faraday/src/components/MakeDecision' import { Recommendation } from 'pubsweet-components-faraday/src/components/MakeRecommendation' -import { MakeDecision } from './' import { canMakeRecommendation } from '../../../component-faraday-selectors/src' const SideBarActions = ({ project, version, currentUserIs, + heRecommendation, canMakeRecommendation, }) => ( <Root> - {currentUserIs('adminEiC') && <MakeDecision />} + {currentUserIs('adminEiC') && ( + <Decision + collectionId={project.id} + fragmentId={version.id} + heRecommendation={heRecommendation} + modalKey={`decide-${version.id}`} + /> + )} + {canMakeRecommendation && ( <Recommendation collectionId={project.id} diff --git a/packages/component-manuscript/src/components/index.js b/packages/component-manuscript/src/components/index.js index e1d35c49c..3509c1104 100644 --- a/packages/component-manuscript/src/components/index.js +++ b/packages/component-manuscript/src/components/index.js @@ -1,7 +1,6 @@ export { default as Files } from './Files' export { default as Authors } from './Authors' export { default as ShowMore } from './ShowMore' -export { default as MakeDecision } from './MakeDecision' export { default as SideBarRoles } from './SideBarRoles' export { default as ManuscriptPage } from './ManuscriptPage' export { default as SideBarActions } from './SideBarActions' diff --git a/packages/components-faraday/src/components/MakeDecision/Decision.js b/packages/components-faraday/src/components/MakeDecision/Decision.js index 1dfa521c4..feb44fff5 100644 --- a/packages/components-faraday/src/components/MakeDecision/Decision.js +++ b/packages/components-faraday/src/components/MakeDecision/Decision.js @@ -1,7 +1,7 @@ import React from 'react' import { th } from '@pubsweet/ui' import styled from 'styled-components' -import { compose, withHandlers } from 'recompose' +import { compose, withHandlers, setDisplayName } from 'recompose' import { ConfirmationModal, @@ -24,6 +24,7 @@ const ModalComponent = ({ type, ...rest }) => { } export default compose( + setDisplayName('EICDecisionRoot'), withModal2(() => ({ modalComponent: ModalComponent, })), diff --git a/packages/components-faraday/src/components/MakeDecision/DecisionForm.js b/packages/components-faraday/src/components/MakeDecision/DecisionForm.js index c3b6c6d6f..01253c226 100644 --- a/packages/components-faraday/src/components/MakeDecision/DecisionForm.js +++ b/packages/components-faraday/src/components/MakeDecision/DecisionForm.js @@ -2,15 +2,29 @@ import React from 'react' import { get } from 'lodash' import { connect } from 'react-redux' import { actions } from 'pubsweet-client' +import { required } from 'xpub-validators' import styled, { css } from 'styled-components' import { reduxForm, formValueSelector } from 'redux-form' import { compose, setDisplayName, withProps } from 'recompose' -import { th, Icon, Button, RadioGroup, ValidatedField } from '@pubsweet/ui' +import { + th, + Icon, + Button, + Spinner, + RadioGroup, + ValidatedField, +} from '@pubsweet/ui' import { FormItems } from '../UIComponents' -import { createRecommendation } from '../../redux/recommendations' +import { + selectError, + selectFetching, + createRecommendation, +} from '../../redux/recommendations' +import { subtitleParser, decisions, parseFormValues } from './utils' const { + Err, Row, Title, Label, @@ -22,16 +36,12 @@ const { } = FormItems const Form = RootContainer.withComponent(FormContainer) -const decisionOptions = [ - { label: 'Publish', value: 'publish' }, - { label: 'Reject', value: 'reject' }, - { label: 'Return to Handling Editor', value: 'return-to-handling-editor' }, -] - const DecisionForm = ({ decision, hideModal, + isFetching, handleSubmit, + recommendationError, heRecommendation: { reason, message = '' }, }) => ( <Form onSubmit={handleSubmit}> @@ -39,24 +49,30 @@ const DecisionForm = ({ <Icon primary>x</Icon> </IconButton> <Title>Make decision</Title> - <CustomSubtitle> - Recommended to<BoldSubtitle>{reason}</BoldSubtitle> - </CustomSubtitle> - <Row> - <RowItem vertical> - <Label>Message from Handling Editor</Label> - <span>{message}</span> - </RowItem> - </Row> + {!!reason && ( + <CustomSubtitle> + Recommended to<BoldSubtitle>{reason}</BoldSubtitle> + </CustomSubtitle> + )} + {!!message && ( + <Row> + <RowItem vertical> + <Label>Message from Handling Editor</Label> + <span>{message}</span> + </RowItem> + </Row> + )} <Row> <RowItem vertical> <Label>Your Decision</Label> <ValidatedField component={input => ( - <CustomRadioGroup> + <CustomRadioGroup + justify={reason ? 'space-between' : 'space-around'} + > <RadioGroup name="decision" - options={decisionOptions} + options={reason ? decisions : decisions.slice(0, 2)} {...input} /> </CustomRadioGroup> @@ -72,43 +88,43 @@ const DecisionForm = ({ <ValidatedField component={input => <Textarea {...input} height={70} />} name="messageToHE" + validate={[required]} /> </RowItem> </Row> )} + {recommendationError && ( + <Row> + <RowItem centered> + <Err>{recommendationError}</Err> + </RowItem> + </Row> + )} <Row> <RowItem centered> <Button onClick={hideModal}>Cancel</Button> </RowItem> <RowItem centered> - <Button primary type="submit"> - Submit - </Button> + {isFetching ? ( + <Spinner size={3} /> + ) : ( + <Button primary type="submit"> + Submit + </Button> + )} </RowItem> </Row> </Form> ) -const subtitleParser = t => { - switch (t) { - case 'major': - return 'Revise(major)' - case 'minor': - return 'Revise(minor)' - case 'reject': - return 'Reject' - case 'publish': - default: - return 'Publish' - } -} - const selector = formValueSelector('eicDecision') export default compose( setDisplayName('DecisionForm'), connect( state => ({ + isFetching: selectFetching(state), decision: selector(state, 'decision'), + recommendationError: selectError(state), }), { createRecommendation, getCollections: actions.getCollections }, ), @@ -121,7 +137,7 @@ export default compose( reduxForm({ form: 'eicDecision', onSubmit: ( - { decision, messageToHE }, + values, dispatch, { showModal, @@ -131,16 +147,7 @@ export default compose( createRecommendation, }, ) => { - const recommendation = { - recommendation: decision, - recommendationType: 'editorRecommendation', - comments: [ - { - public: false, - content: messageToHE, - }, - ], - } + const recommendation = parseFormValues(values) createRecommendation(collectionId, fragmentId, recommendation).then(r => { getCollections() showModal({ @@ -179,7 +186,7 @@ const BoldSubtitle = Subtitle.extend` const CustomRadioGroup = styled.div` div { flex-direction: row; - justify-content: space-between; + justify-content: ${({ justify }) => justify || 'space-between'}; label { span:last-child { font-style: normal; diff --git a/packages/components-faraday/src/components/MakeDecision/utils.js b/packages/components-faraday/src/components/MakeDecision/utils.js new file mode 100644 index 000000000..1ca6f60c9 --- /dev/null +++ b/packages/components-faraday/src/components/MakeDecision/utils.js @@ -0,0 +1,38 @@ +export const decisions = [ + { label: 'Publish', value: 'publish' }, + { label: 'Reject', value: 'reject' }, + { label: 'Return to Handling Editor', value: 'return-to-handling-editor' }, +] + +export const subtitleParser = t => { + switch (t) { + case 'major': + return 'Revise(major)' + case 'minor': + return 'Revise(minor)' + case 'reject': + return 'Reject' + case 'publish': + return 'Publish' + default: + return '' + } +} + +export const parseFormValues = ({ decision, messageToHE }) => { + const recommendation = { + recommendation: decision, + recommendationType: 'editorRecommendation', + } + return messageToHE + ? { + ...recommendation, + comments: [ + { + public: false, + content: messageToHE, + }, + ], + } + : recommendation +} diff --git a/packages/components-faraday/src/components/MakeRecommendation/RecommendWizard.js b/packages/components-faraday/src/components/MakeRecommendation/RecommendWizard.js index a4e85febb..1b06f395f 100644 --- a/packages/components-faraday/src/components/MakeRecommendation/RecommendWizard.js +++ b/packages/components-faraday/src/components/MakeRecommendation/RecommendWizard.js @@ -86,6 +86,7 @@ export default compose( getCollections() showModal({ title: 'Recommendation sent', + cancelText: 'OK', }) }) }, -- GitLab