diff --git a/packages/components/xpub-review/package.json b/packages/components/xpub-review/package.json index b61c4eeae8f649e3ddb137a184d67e50b3feed80..0bf979551e42677c3307d3ab68c2965a89e02050 100644 --- a/packages/components/xpub-review/package.json +++ b/packages/components/xpub-review/package.json @@ -18,7 +18,7 @@ "react-moment": "^0.6.1", "react-select": "^1.0.0-rc.10", "recompose": "^0.26.0", - "redux-form": "^7.0.3", + "formik": "^1.4.2", "striptags": "^3.1.0", "styled-components": "^4.1.1", "uuid": "^3.1.0", diff --git a/packages/components/xpub-review/src/components/ReviewPage.js b/packages/components/xpub-review/src/components/ReviewPage.js index 9b95a98603db87f4b4fe230fbd60de4ba31cbe78..2581ab4e2901eaeb45d77a0779c1d586d52b9f41 100644 --- a/packages/components/xpub-review/src/components/ReviewPage.js +++ b/packages/components/xpub-review/src/components/ReviewPage.js @@ -322,11 +322,20 @@ export default compose( }), ), withFormik({ + enableReinitialize: true, + mapPropsToValues: props => + props.manuscript.reviews.find(review => !review.isDecision) || { + id: null, + comments: [], + recommendation: null, + }, isInitialValid: ({ review }) => { - const isRecommendation = review.recommendation !== '' - const isCommented = getCommentContent(review, 'note') !== '' + if (!review.id) return false + const hasRecommendation = review.recommendation !== null + const comment = getCommentContent(review, 'note') + const isCommented = comment !== null && comment !== '' - return isCommented && isRecommendation + return isCommented && hasRecommendation }, displayName: 'review', handleSubmit: (props, { props: { completeReview, history } }) => diff --git a/packages/components/xpub-review/src/components/ReviewersPage.js b/packages/components/xpub-review/src/components/ReviewersPage.js index 873ec9d69a0a2cff85031514563ca7c470428792..883719e26244744052704aed545f7ddccc4a1f10 100644 --- a/packages/components/xpub-review/src/components/ReviewersPage.js +++ b/packages/components/xpub-review/src/components/ReviewersPage.js @@ -1,4 +1,4 @@ -import { compose, withProps, withHandlers } from 'recompose' +import { compose, withProps } from 'recompose' import { withFormik } from 'formik' import { graphql } from 'react-apollo' import { gql } from 'apollo-client-preset' @@ -177,12 +177,6 @@ const handleSubmit = ( } } -const loadOptions = props => input => { - const options = props.reviewerUsers - - return Promise.resolve({ options }) -} - export default compose( graphql(query, { options: ({ match }) => ({ @@ -228,9 +222,9 @@ export default compose( } }, ), - withHandlers({ - loadOptions: props => loadOptions(props), - }), + // withHandlers({ + // loadOptions: props => props.reviewerUsers, // loadOptions(props), + // }), withFormik({ mapPropsToValues: () => ({ user: '' }), displayName: 'reviewers', diff --git a/packages/components/xpub-review/src/components/review/ReviewForm.js b/packages/components/xpub-review/src/components/review/ReviewForm.js index 9902e1f898f4eabaf74a355b0c7951c1b1dc2cb3..e6cee89afe25060d9a61dc42e5677f8d2c2f23b1 100644 --- a/packages/components/xpub-review/src/components/review/ReviewForm.js +++ b/packages/components/xpub-review/src/components/review/ReviewForm.js @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components' import { cloneDeep, set } from 'lodash' -import { Field, FieldArray } from 'formik' +import { Field } from 'formik' import { NoteEditor } from 'xpub-edit' import { Button, @@ -12,20 +12,15 @@ import { } from '@pubsweet/ui' import { withJournal } from 'xpub-journal' -import { - getCommentFiles, - getCommentContent, - stripHtml, - createComments, -} from './util' +import { getCommentFiles, stripHtml, createComments } from './util' import AdminSection from '../atoms/AdminSection' -const AttachmentsInput = type => ({ +const AttachmentsInput = ({ field, form: { values }, updateReview, uploadFile, - review, + type, }) => [ <UploadButton buttonText="↑ Upload files" @@ -36,12 +31,12 @@ const AttachmentsInput = type => ({ file.type = type const { updateIndex, comment } = createComments( - review, + values, { files: [file] }, type, ) - const data = cloneDeep(review) + const data = cloneDeep(values) set(data, `comments.${updateIndex}`, comment) updateReview(data).then(({ data: { updateReview } }) => { @@ -50,7 +45,7 @@ const AttachmentsInput = type => ({ }} />, <Flexbox> - {getCommentFiles(review, type).map(val => { + {getCommentFiles(values, type).map(val => { const file = cloneDeep(val) file.name = file.filename return <UploadingFile file={file} key={file.name} uploaded /> @@ -58,13 +53,18 @@ const AttachmentsInput = type => ({ </Flexbox>, ] -const NoteInput = ({ field, form: { values }, review, updateReview }) => ( +const NoteInput = ({ + field, + form: { values, setFieldValue }, + updateReview, +}) => ( <NoteEditor + key="note-comment" placeholder="Enter your review…" title="Comments to the Author" {...field} - onBlur={value => { - const { updateIndex, comment } = createComments( + onChange={value => { + const { comment } = createComments( values, { type: 'note', @@ -73,82 +73,90 @@ const NoteInput = ({ field, form: { values }, review, updateReview }) => ( 'note', ) - const data = cloneDeep(review) - set(data, `comments.${updateIndex}`, comment) - + setFieldValue(`comments.0`, comment) + const data = cloneDeep(values) + set(data, `comments.0`, comment) updateReview(data) }} - value={getCommentContent(review, 'note')} + value={field.value || ''} /> ) -const ConfidentialInput = ({ field, review, updateReview }) => ( +const ConfidentialInput = ({ + field, + form: { values, setFieldValue }, + updateReview, +}) => ( <NoteEditor + key="confidential-comment" placeholder="Enter a confidential note to the editor (optional)…" title="Confidential Comments to Editor (Optional)" {...field} - onBlur={value => { - const { updateIndex, comment } = createComments( - review, + onChange={value => { + const { comment } = createComments( + values, { type: 'confidential', content: stripHtml(value), }, 'confidential', ) - const data = cloneDeep(review) - set(data, `comments.${updateIndex}`, comment) + + setFieldValue(`comments.1`, comment) + const data = cloneDeep(values) + set(data, `comments.1`, comment) updateReview(data) }} - value={getCommentContent(review, 'confidential')} + value={field.value || ''} /> ) -const RecommendationInput = journal => ({ field, updateReview, review }) => ( +const RecommendationInput = ({ + field, + form: { values }, + updateReview, + journal, +}) => ( <RadioGroup inline + {...field} onChange={val => { - const data = cloneDeep(review) + const data = cloneDeep(values) set(data, 'recommendation', val) updateReview(data) }} options={journal.recommendations} - value={review.recommendation} /> ) -const ReviewComment = (updateReview, uploadFile, review) => props => [ +const ReviewComment = props => [ <AdminSection> <div name="note"> <Field - component={NoteInput} - review={review} - updateReview={updateReview} - {...props} + component={extraProps => <NoteInput {...props} {...extraProps} />} + key="noteField" + name="comments.0.content" /> <Field - component={AttachmentsInput('note')} - review={review} - updateReview={updateReview} - uploadFile={uploadFile} - {...props} + component={extraProps => ( + <AttachmentsInput type="note" {...props} {...extraProps} /> + )} /> </div> </AdminSection>, <AdminSection> <div name="confidential"> <Field - component={ConfidentialInput} - review={review} - updateReview={updateReview} - {...props} + component={extraProps => ( + <ConfidentialInput {...props} {...extraProps} /> + )} + key="confidentialField" + name="comments.1.content" /> <Field - component={AttachmentsInput('confidential')} - review={review} - updateReview={updateReview} - uploadFile={uploadFile} - {...props} + component={extraProps => ( + <AttachmentsInput type="confidential" {...props} {...extraProps} /> + )} /> </div> </AdminSection>, @@ -165,17 +173,19 @@ const ReviewForm = ({ review, }) => ( <form onSubmit={handleSubmit}> - <FieldArray - component={ReviewComment(updateReview, uploadFile, review)} - name="comments" - /> + <ReviewComment updateReview={updateReview} uploadFile={uploadFile} /> <AdminSection> <div name="Recommendation"> <Title>Recommendation</Title> <Field - component={RecommendationInput(journal)} + component={props => ( + <RecommendationInput + journal={journal} + updateReview={updateReview} + {...props} + /> + )} name="recommendation" - review={review} updateReview={updateReview} /> </div> diff --git a/packages/components/xpub-review/src/components/review/util.js b/packages/components/xpub-review/src/components/review/util.js index 91551dc7e458bdadcb7e7f98554ec0aec72c09b4..a760b1ba6da04d9e430ba7cbd223e6fe334d8364 100644 --- a/packages/components/xpub-review/src/components/review/util.js +++ b/packages/components/xpub-review/src/components/review/util.js @@ -6,19 +6,19 @@ export const stripHtml = htmlString => { export const getCommentFiles = (review = {}, type) => { const comments = - (review.comments || []).find(comment => comment.type === type) || {} + (review.comments || []).find(comment => (comment || {}).type === type) || {} return comments.files || [] } export const getCommentContent = (review = {}, type) => { const comments = - (review.comments || []).find(comment => comment.type === type) || {} + (review.comments || []).find(comment => (comment || {}).type === type) || {} return comments.content || '' } export const createComments = (values, val, type) => { let updateIndex = (values.comments || []).findIndex( - comment => comment.type === type, + comment => (comment || {}).type === type, ) updateIndex = (values.comments || []).length > 0 && updateIndex < 0 ? 1 : updateIndex diff --git a/packages/components/xpub-review/src/components/reviewers/ReviewerForm.js b/packages/components/xpub-review/src/components/reviewers/ReviewerForm.js index 7770e2c95c1c15ddc3e0a56d88e898eb2bd577f6..db27927ff3931edc6e27c825e6c9af772f9f1b35 100644 --- a/packages/components/xpub-review/src/components/reviewers/ReviewerForm.js +++ b/packages/components/xpub-review/src/components/reviewers/ReviewerForm.js @@ -1,6 +1,6 @@ import React from 'react' import Select from 'react-select' -import { Field, FieldArray } from 'formik' +import { Field } from 'formik' import { Button } from '@pubsweet/ui' import { required } from 'xpub-validators' import 'react-select/dist/react-select.css' @@ -12,46 +12,40 @@ const OptionRenderer = option => ( </div> ) -const ReviewerInput = loadOptions => ({ +const ReviewerInput = ({ field, form: { values, setFieldValue }, push, replace, + reviewerUsers, }) => ( - <Select.AsyncCreatable + <Select.Creatable {...field} - filterOption={() => true} labelKey="username" - loadOptions={loadOptions} onChange={user => { setFieldValue('user', user) }} optionRenderer={OptionRenderer} + options={reviewerUsers} promptTextCreator={label => `Add ${label}?`} - value={values.user.id} valueKey="id" /> ) -const componentFields = loadOptions => props => ( - <Field - component={ReviewerInput(loadOptions)} - name="user" - validate={required} - {...props} - /> -) - const ReviewerForm = ({ reset, isValid, handleSubmit, onSubmit, - loadOptions, + reviewerUsers, }) => ( <form onSubmit={handleSubmit}> - <FieldArray component={componentFields(loadOptions)} /> - + <Field + component={ReviewerInput} + name="user" + reviewerUsers={reviewerUsers} + validate={required} + /> <Button disabled={!isValid} primary type="submit"> Invite reviewer </Button>