Skip to content
Snippets Groups Projects
Commit bccf5cd0 authored by Jure's avatar Jure
Browse files

Merge branch 'fix-reviewers-assignment' into 'master'

Fix reviewers assignment

Closes xpub/xpub#235

See merge request pubsweet/pubsweet!488
parents 81bd0351 18faca60
No related branches found
No related tags found
No related merge requests found
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
"react-moment": "^0.6.1", "react-moment": "^0.6.1",
"react-select": "^1.0.0-rc.10", "react-select": "^1.0.0-rc.10",
"recompose": "^0.26.0", "recompose": "^0.26.0",
"redux-form": "^7.0.3", "formik": "^1.4.2",
"striptags": "^3.1.0", "striptags": "^3.1.0",
"styled-components": "^4.1.1", "styled-components": "^4.1.1",
"uuid": "^3.1.0", "uuid": "^3.1.0",
......
...@@ -322,11 +322,20 @@ export default compose( ...@@ -322,11 +322,20 @@ export default compose(
}), }),
), ),
withFormik({ withFormik({
enableReinitialize: true,
mapPropsToValues: props =>
props.manuscript.reviews.find(review => !review.isDecision) || {
id: null,
comments: [],
recommendation: null,
},
isInitialValid: ({ review }) => { isInitialValid: ({ review }) => {
const isRecommendation = review.recommendation !== '' if (!review.id) return false
const isCommented = getCommentContent(review, 'note') !== '' const hasRecommendation = review.recommendation !== null
const comment = getCommentContent(review, 'note')
const isCommented = comment !== null && comment !== ''
return isCommented && isRecommendation return isCommented && hasRecommendation
}, },
displayName: 'review', displayName: 'review',
handleSubmit: (props, { props: { completeReview, history } }) => handleSubmit: (props, { props: { completeReview, history } }) =>
......
import { compose, withProps, withHandlers } from 'recompose' import { compose, withProps } from 'recompose'
import { withFormik } from 'formik' import { withFormik } from 'formik'
import { graphql } from 'react-apollo' import { graphql } from 'react-apollo'
import { gql } from 'apollo-client-preset' import { gql } from 'apollo-client-preset'
...@@ -177,12 +177,6 @@ const handleSubmit = ( ...@@ -177,12 +177,6 @@ const handleSubmit = (
} }
} }
const loadOptions = props => input => {
const options = props.reviewerUsers
return Promise.resolve({ options })
}
export default compose( export default compose(
graphql(query, { graphql(query, {
options: ({ match }) => ({ options: ({ match }) => ({
...@@ -228,9 +222,9 @@ export default compose( ...@@ -228,9 +222,9 @@ export default compose(
} }
}, },
), ),
withHandlers({ // withHandlers({
loadOptions: props => loadOptions(props), // loadOptions: props => props.reviewerUsers, // loadOptions(props),
}), // }),
withFormik({ withFormik({
mapPropsToValues: () => ({ user: '' }), mapPropsToValues: () => ({ user: '' }),
displayName: 'reviewers', displayName: 'reviewers',
......
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { cloneDeep, set } from 'lodash' import { cloneDeep, set } from 'lodash'
import { Field, FieldArray } from 'formik' import { Field } from 'formik'
import { NoteEditor } from 'xpub-edit' import { NoteEditor } from 'xpub-edit'
import { import {
Button, Button,
...@@ -12,20 +12,15 @@ import { ...@@ -12,20 +12,15 @@ import {
} from '@pubsweet/ui' } from '@pubsweet/ui'
import { withJournal } from 'xpub-journal' import { withJournal } from 'xpub-journal'
import { import { getCommentFiles, stripHtml, createComments } from './util'
getCommentFiles,
getCommentContent,
stripHtml,
createComments,
} from './util'
import AdminSection from '../atoms/AdminSection' import AdminSection from '../atoms/AdminSection'
const AttachmentsInput = type => ({ const AttachmentsInput = ({
field, field,
form: { values }, form: { values },
updateReview, updateReview,
uploadFile, uploadFile,
review, type,
}) => [ }) => [
<UploadButton <UploadButton
buttonText="↑ Upload files" buttonText="↑ Upload files"
...@@ -36,12 +31,12 @@ const AttachmentsInput = type => ({ ...@@ -36,12 +31,12 @@ const AttachmentsInput = type => ({
file.type = type file.type = type
const { updateIndex, comment } = createComments( const { updateIndex, comment } = createComments(
review, values,
{ files: [file] }, { files: [file] },
type, type,
) )
const data = cloneDeep(review) const data = cloneDeep(values)
set(data, `comments.${updateIndex}`, comment) set(data, `comments.${updateIndex}`, comment)
updateReview(data).then(({ data: { updateReview } }) => { updateReview(data).then(({ data: { updateReview } }) => {
...@@ -50,7 +45,7 @@ const AttachmentsInput = type => ({ ...@@ -50,7 +45,7 @@ const AttachmentsInput = type => ({
}} }}
/>, />,
<Flexbox> <Flexbox>
{getCommentFiles(review, type).map(val => { {getCommentFiles(values, type).map(val => {
const file = cloneDeep(val) const file = cloneDeep(val)
file.name = file.filename file.name = file.filename
return <UploadingFile file={file} key={file.name} uploaded /> return <UploadingFile file={file} key={file.name} uploaded />
...@@ -58,13 +53,18 @@ const AttachmentsInput = type => ({ ...@@ -58,13 +53,18 @@ const AttachmentsInput = type => ({
</Flexbox>, </Flexbox>,
] ]
const NoteInput = ({ field, form: { values }, review, updateReview }) => ( const NoteInput = ({
field,
form: { values, setFieldValue },
updateReview,
}) => (
<NoteEditor <NoteEditor
key="note-comment"
placeholder="Enter your review…" placeholder="Enter your review…"
title="Comments to the Author" title="Comments to the Author"
{...field} {...field}
onBlur={value => { onChange={value => {
const { updateIndex, comment } = createComments( const { comment } = createComments(
values, values,
{ {
type: 'note', type: 'note',
...@@ -73,82 +73,90 @@ const NoteInput = ({ field, form: { values }, review, updateReview }) => ( ...@@ -73,82 +73,90 @@ const NoteInput = ({ field, form: { values }, review, updateReview }) => (
'note', 'note',
) )
const data = cloneDeep(review) setFieldValue(`comments.0`, comment)
set(data, `comments.${updateIndex}`, comment) const data = cloneDeep(values)
set(data, `comments.0`, comment)
updateReview(data) updateReview(data)
}} }}
value={getCommentContent(review, 'note')} value={field.value || ''}
/> />
) )
const ConfidentialInput = ({ field, review, updateReview }) => ( const ConfidentialInput = ({
field,
form: { values, setFieldValue },
updateReview,
}) => (
<NoteEditor <NoteEditor
key="confidential-comment"
placeholder="Enter a confidential note to the editor (optional)…" placeholder="Enter a confidential note to the editor (optional)…"
title="Confidential Comments to Editor (Optional)" title="Confidential Comments to Editor (Optional)"
{...field} {...field}
onBlur={value => { onChange={value => {
const { updateIndex, comment } = createComments( const { comment } = createComments(
review, values,
{ {
type: 'confidential', type: 'confidential',
content: stripHtml(value), content: stripHtml(value),
}, },
'confidential', '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) updateReview(data)
}} }}
value={getCommentContent(review, 'confidential')} value={field.value || ''}
/> />
) )
const RecommendationInput = journal => ({ field, updateReview, review }) => ( const RecommendationInput = ({
field,
form: { values },
updateReview,
journal,
}) => (
<RadioGroup <RadioGroup
inline inline
{...field}
onChange={val => { onChange={val => {
const data = cloneDeep(review) const data = cloneDeep(values)
set(data, 'recommendation', val) set(data, 'recommendation', val)
updateReview(data) updateReview(data)
}} }}
options={journal.recommendations} options={journal.recommendations}
value={review.recommendation}
/> />
) )
const ReviewComment = (updateReview, uploadFile, review) => props => [ const ReviewComment = props => [
<AdminSection> <AdminSection>
<div name="note"> <div name="note">
<Field <Field
component={NoteInput} component={extraProps => <NoteInput {...props} {...extraProps} />}
review={review} key="noteField"
updateReview={updateReview} name="comments.0.content"
{...props}
/> />
<Field <Field
component={AttachmentsInput('note')} component={extraProps => (
review={review} <AttachmentsInput type="note" {...props} {...extraProps} />
updateReview={updateReview} )}
uploadFile={uploadFile}
{...props}
/> />
</div> </div>
</AdminSection>, </AdminSection>,
<AdminSection> <AdminSection>
<div name="confidential"> <div name="confidential">
<Field <Field
component={ConfidentialInput} component={extraProps => (
review={review} <ConfidentialInput {...props} {...extraProps} />
updateReview={updateReview} )}
{...props} key="confidentialField"
name="comments.1.content"
/> />
<Field <Field
component={AttachmentsInput('confidential')} component={extraProps => (
review={review} <AttachmentsInput type="confidential" {...props} {...extraProps} />
updateReview={updateReview} )}
uploadFile={uploadFile}
{...props}
/> />
</div> </div>
</AdminSection>, </AdminSection>,
...@@ -165,17 +173,19 @@ const ReviewForm = ({ ...@@ -165,17 +173,19 @@ const ReviewForm = ({
review, review,
}) => ( }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<FieldArray <ReviewComment updateReview={updateReview} uploadFile={uploadFile} />
component={ReviewComment(updateReview, uploadFile, review)}
name="comments"
/>
<AdminSection> <AdminSection>
<div name="Recommendation"> <div name="Recommendation">
<Title>Recommendation</Title> <Title>Recommendation</Title>
<Field <Field
component={RecommendationInput(journal)} component={props => (
<RecommendationInput
journal={journal}
updateReview={updateReview}
{...props}
/>
)}
name="recommendation" name="recommendation"
review={review}
updateReview={updateReview} updateReview={updateReview}
/> />
</div> </div>
......
...@@ -6,19 +6,19 @@ export const stripHtml = htmlString => { ...@@ -6,19 +6,19 @@ export const stripHtml = htmlString => {
export const getCommentFiles = (review = {}, type) => { export const getCommentFiles = (review = {}, type) => {
const comments = const comments =
(review.comments || []).find(comment => comment.type === type) || {} (review.comments || []).find(comment => (comment || {}).type === type) || {}
return comments.files || [] return comments.files || []
} }
export const getCommentContent = (review = {}, type) => { export const getCommentContent = (review = {}, type) => {
const comments = const comments =
(review.comments || []).find(comment => comment.type === type) || {} (review.comments || []).find(comment => (comment || {}).type === type) || {}
return comments.content || '' return comments.content || ''
} }
export const createComments = (values, val, type) => { export const createComments = (values, val, type) => {
let updateIndex = (values.comments || []).findIndex( let updateIndex = (values.comments || []).findIndex(
comment => comment.type === type, comment => (comment || {}).type === type,
) )
updateIndex = updateIndex =
(values.comments || []).length > 0 && updateIndex < 0 ? 1 : updateIndex (values.comments || []).length > 0 && updateIndex < 0 ? 1 : updateIndex
......
import React from 'react' import React from 'react'
import Select from 'react-select' import Select from 'react-select'
import { Field, FieldArray } from 'formik' import { Field } from 'formik'
import { Button } from '@pubsweet/ui' import { Button } from '@pubsweet/ui'
import { required } from 'xpub-validators' import { required } from 'xpub-validators'
import 'react-select/dist/react-select.css' import 'react-select/dist/react-select.css'
...@@ -12,46 +12,40 @@ const OptionRenderer = option => ( ...@@ -12,46 +12,40 @@ const OptionRenderer = option => (
</div> </div>
) )
const ReviewerInput = loadOptions => ({ const ReviewerInput = ({
field, field,
form: { values, setFieldValue }, form: { values, setFieldValue },
push, push,
replace, replace,
reviewerUsers,
}) => ( }) => (
<Select.AsyncCreatable <Select.Creatable
{...field} {...field}
filterOption={() => true}
labelKey="username" labelKey="username"
loadOptions={loadOptions}
onChange={user => { onChange={user => {
setFieldValue('user', user) setFieldValue('user', user)
}} }}
optionRenderer={OptionRenderer} optionRenderer={OptionRenderer}
options={reviewerUsers}
promptTextCreator={label => `Add ${label}?`} promptTextCreator={label => `Add ${label}?`}
value={values.user.id}
valueKey="id" valueKey="id"
/> />
) )
const componentFields = loadOptions => props => (
<Field
component={ReviewerInput(loadOptions)}
name="user"
validate={required}
{...props}
/>
)
const ReviewerForm = ({ const ReviewerForm = ({
reset, reset,
isValid, isValid,
handleSubmit, handleSubmit,
onSubmit, onSubmit,
loadOptions, reviewerUsers,
}) => ( }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<FieldArray component={componentFields(loadOptions)} /> <Field
component={ReviewerInput}
name="user"
reviewerUsers={reviewerUsers}
validate={required}
/>
<Button disabled={!isValid} primary type="submit"> <Button disabled={!isValid} primary type="submit">
Invite reviewer Invite reviewer
</Button> </Button>
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment