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

refactor: move the xpub-review component

parent 89935d7c
No related branches found
No related tags found
No related merge requests found
Showing
with 648 additions and 26 deletions
import React from 'react'
import moment from 'moment'
import { Tabs } from '@pubsweet/ui'
import { Formik } from 'formik'
import gql from 'graphql-tag'
import { useMutation, useQuery } from '@apollo/react-hooks'
import DecisionForm from './decision/DecisionForm'
import DecisionReviews from './decision/DecisionReviews'
import AssignEditorsReviewers from './assignEditors/AssignEditorsReviewers'
import AssignEditor from './assignEditors/AssignEditor'
import ReviewMetadata from './metadata/ReviewMetadata'
import Decision from './decision/Decision'
// import EditorSection from './EditorSection'
import { AdminSection, Columns, Manuscript, Chat, TabsContainer } from './style'
// const addEditor = (manuscript, label) => ({
// content: <EditorSection manuscript={manuscript} />,
// key: manuscript.id,
// label,
// })
import { Spinner } from '../../../shared'
import { getCommentContent } from './review/util'
import MessageContainer from '../../../component-chat/src'
const reviewFields = `
id
created
updated
comments {
type
content
files {
id
created
label
filename
fileType
mimeType
size
url
}
}
isDecision
recommendation
user {
id
username
}
`
const fragmentFields = `
id
created
files {
id
created
label
filename
fileType
mimeType
size
url
}
reviews {
${reviewFields}
}
decision
teams {
id
name
role
object {
objectId
objectType
}
members {
id
user {
id
username
}
status
}
}
status
meta {
title
source
abstract
declarations {
openData
openPeerReview
preregistered
previouslySubmitted
researchNexus
streamlinedReview
}
articleSections
articleType
history {
type
date
}
notes {
notesType
content
}
keywords
}
submission
suggestions {
reviewers {
opposed
suggested
}
editors {
opposed
suggested
}
}
`
const query = gql`
query($id: ID!) {
currentUser {
id
username
admin
}
manuscript(id: $id) {
${fragmentFields}
manuscriptVersions {
${fragmentFields}
}
channels {
id
type
topic
}
}
}
`
const updateReviewMutationQuery = gql`
mutation($id: ID, $input: ReviewInput) {
updateReview(id: $id, input: $input) {
${reviewFields}
}
}
`
const uploadReviewFilesMutation = gql`
mutation($file: Upload!) {
upload(file: $file) {
url
}
}
`
const createFileMutation = gql`
mutation($file: Upload!) {
createFile(file: $file) {
id
created
label
filename
fileType
mimeType
size
url
}
}
`
const submitMutation = gql`
mutation($id: ID!, $input: String) {
submitManuscript(id: $id, input: $input) {
id
${fragmentFields}
}
}
`
const updateCacheForFileCreation = (proxy, { data: { createFile } }) => {
const data = proxy.readQuery({
query,
variables: {
id: match.params.version,
},
})
data.manuscript.reviews.map(review => {
if (review.id === file.objectId) {
review.comments.map(comment => {
if (comment.type === createFile.fileType) {
comment.files = [createFile]
}
return comment
})
}
return review
})
proxy.writeQuery({ query, data })
}
// const createFile = file => {
// mutate({
// variables: {
// file,
// },
// update:
// },
//
const dateLabel = date => moment(date).format('YYYY-MM-DD')
const decisionSections = ({
manuscript,
handleSubmit,
isValid,
updateReview,
uploadFile,
}) => {
const decisionSections = []
const manuscriptVersions = manuscript.manuscriptVersions || []
manuscriptVersions.forEach(manuscript => {
decisionSections.push({
content: (
<>
<ReviewMetadata manuscript={manuscript} />
<DecisionReviews manuscript={manuscript} />
<Decision
review={manuscript.reviews.find(review => review.isDecision)}
/>
</>
),
key: manuscript.id,
label: dateLabel(manuscript.updated),
})
}, [])
if (manuscript.status !== 'revising') {
decisionSections.push({
content: (
<>
<AdminSection key="assign-editors">
<AssignEditorsReviewers
AssignEditor={AssignEditor}
manuscript={manuscript}
/>
</AdminSection>
<AdminSection key="review-metadata">
<ReviewMetadata manuscript={manuscript} />
</AdminSection>
<AdminSection key="decision-review">
<DecisionReviews manuscript={manuscript} />
</AdminSection>
<AdminSection key="decision-form">
<DecisionForm
handleSubmit={handleSubmit}
isValid={isValid}
updateReview={updateReview}
uploadFile={uploadFile}
/>
</AdminSection>
</>
),
key: manuscript.id,
label: dateLabel(),
})
}
return decisionSections
}
const editorSections = ({ manuscript }) => {
const editorSections = []
const manuscriptVersions = manuscript.manuscriptVersions || []
manuscriptVersions.forEach(manuscript => {
editorSections.push(addEditor(manuscript, dateLabel(manuscript.updated)))
}, [])
if (manuscript.status !== 'revising') {
editorSections.push(addEditor(manuscript, dateLabel()))
}
}
const DecisionPage = ({ match }) => {
// Hooks from the old world
const [completeDecision] = useMutation(submitMutation, {
refetchQueries: [query],
})
const [updateReviewMutation] = useMutation(updateReviewMutationQuery)
// File upload
const [uploadReviewFiles] = useMutation(uploadReviewFilesMutation)
const { loading, error, data } = useQuery(query, {
variables: {
id: match.params.version,
},
})
if (loading) return <Spinner />
if (error) return `Error! ${error.message}`
const manuscript = data.manuscript
const channelId = manuscript.channels.find(c => c.type === 'editorial').id
const uploadFile = (file, updateReview, type) =>
uploadReviewFiles({
variables: {
file,
},
}).then(({ data }) => {
const newFile = {
url: data.upload.url,
filename: file.name,
size: file.size,
object: 'Review',
objectId: updateReview.id,
fileType: type,
}
createFile(newFile)
})
const updateReview = (data, file) => {
const reviewData = {
isDecision: true,
manuscriptId: manuscript.id,
}
if (data.comment) {
reviewData.comments = [data.comment]
}
if (data.recommendation) {
reviewData.recommendation = data.recommendation
}
const review = manuscript.reviews.find(review => review.isDecision) || {}
return updateReviewMutation({
variables: {
id: review.id || undefined,
input: reviewData,
},
update: (proxy, { data: { updateReview } }) => {
const data = proxy.readQuery({
query,
variables: {
id: manuscript.id,
},
})
const reviewIndex = data.manuscript.reviews.findIndex(
review => review.id === updateReview.id,
)
if (reviewIndex < 0) {
data.manuscript.reviews.push(updateReview)
} else {
data.manuscript.reviews[reviewIndex] = updateReview
}
proxy.writeQuery({ query, data })
},
})
}
// },
// }).then(() => {
// history.push('/dashboard')
// })
// }
//
// console.log(props)
const initialValues = (manuscript.reviews &&
manuscript.reviews.find(review => review.isDecision)) || {
comments: [],
recommendation: null,
}
return (
<Columns>
<Manuscript>
<Formik
displayName="decision"
handleSubmit={(
props,
{ props: { completeDecision, history, manuscript } },
) => {
completeDecision({
variables: {
id: manuscript.id,
input: JSON.stringify({
decision: manuscript.reviews.find(review => review.isDecision)
.recommendation,
}),
},
})
}}
// isInitialValid={({ manuscript }) => {
// const rv =
// manuscript.reviews.find(review => review.isDecision) || {}
// const isRecommendation = rv.recommendation != null
// const isCommented = getCommentContent(rv, 'note') !== ''
// return isCommented && isRecommendation
// }}
initialValues={initialValues}
validate={(values, props) => {
const errors = {}
if (getCommentContent(values, 'note') === '') {
errors.comments = 'Required'
}
if (values.recommendation === null) {
errors.recommendation = 'Required'
}
return errors
}}
>
{props => (
// Temp
/* <Tabs
activeKey={editorSections[editorSections.length - 1].key}
sections={editorSections}
title="Versions"
/> */
<Tabs
activeKey={
decisionSections({
manuscript,
handleSubmit: props.handleSubmit,
isValid: props.isValid,
updateReview,
uploadFile,
})[decisionSections.length - 1].key
}
sections={decisionSections({
manuscript,
handleSubmit: props.handleSubmit,
isValid: props.isValid,
updateReview,
uploadFile,
})}
title="Versions"
/>
)}
</Formik>
</Manuscript>
<Chat>
<MessageContainer channelId={channelId} />
</Chat>
</Columns>
)
}
export default DecisionPage
import React from 'react'
import styled from 'styled-components'
import { Action } from '@pubsweet/ui'
import {
Roles,
Container,
SectionHeader,
SectionRowGrid,
Title,
} from '../style'
const AssignEditorsReviewers = ({ manuscript, AssignEditor }) => (
<Container>
<SectionHeader>
<Title>Assign Editors</Title>
</SectionHeader>
<SectionRowGrid>
<AssignEditor manuscript={manuscript} teamRole="seniorEditor" />
<AssignEditor manuscript={manuscript} teamRole="handlingEditor" />
<Action to={`/journal/versions/${manuscript.id}/reviewers`}>
Assign Reviewers
</Action>
</SectionRowGrid>
</Container>
)
export default AssignEditorsReviewers
......@@ -19,10 +19,18 @@ import {
createComments,
} from '../review/util'
import AdminSection from '../atoms/AdminSection'
import {
AdminSection,
Container,
Title,
SectionHeader,
SectionRowGrid,
SectionRow,
SectionAction,
} from '../style'
const NoteDecision = (updateReview, uploadFile) => props => (
<AdminSection>
<>
<Field
component={NoteInput}
name="comments"
......@@ -34,7 +42,7 @@ const NoteDecision = (updateReview, uploadFile) => props => (
updateReview={updateReview}
uploadFile={uploadFile}
/>
</AdminSection>
</>
)
const NoteInput = ({
......@@ -46,6 +54,7 @@ const NoteInput = ({
key="note-input"
onBlur={() => {}}
onChange={value => {
console.log(values)
const { updateIndex, comment } = createComments(
values,
{
......@@ -61,7 +70,6 @@ const NoteInput = ({
)
}}
placeholder="Write/paste your decision letter here, or upload it using the upload button on the right."
title="Decision"
value={getCommentContent({ comments: field.value }, 'note')}
/>
)
......@@ -126,32 +134,34 @@ const RecommendationInput = ({
}
const DecisionForm = ({ handleSubmit, uploadFile, updateReview, isValid }) => (
<form onSubmit={handleSubmit}>
<AdminSection key="note">
<div name="note">
<FieldArray
component={NoteDecision(updateReview, uploadFile)}
key="comments-array"
name="comments"
/>
</div>
</AdminSection>
<AdminSection key="recommendation">
<Container>
{/* <form onSubmit={handleSubmit}> */}
<SectionHeader>
<Title>Decision</Title>
</SectionHeader>
<SectionRow key="note">
<FieldArray
component={NoteDecision(updateReview, uploadFile)}
key="comments-array"
name="comments"
/>
</SectionRow>
<SectionRowGrid>
<Field
component={RecommendationInput}
name="recommendation"
updateReview={updateReview}
validate={required}
/>
</AdminSection>
<AdminSection key="submit">
<Button disabled={!isValid} primary type="submit">
Submit
</Button>
</AdminSection>
</form>
<SectionAction key="submit">
<Button disabled={!isValid} primary type="submit">
Submit
</Button>
</SectionAction>
</SectionRowGrid>
{/* </form> */}
</Container>
)
export default DecisionForm
......@@ -7,10 +7,13 @@ import DecisionReview from './DecisionReview'
// member => member.user.id === currentUser.id,
// )[0] || {}
// return status
const getCompletedReviews = (manuscript, currentUser) => {
const team =
manuscript.teams.find(team => team.role === 'reviewerEditor') || {}
if (!team.members) {
return null
}
const currentMember = team.members.find(m => m.user.id === currentUser.id)
return currentMember && currentMember.status
}
......
......@@ -3,8 +3,7 @@ import { Wax /*, CreateSchema */ } from 'wax-prosemirror-core'
// import { XpubSchema } from 'wax-prosemirror-schema'
// import 'wax-prosemirror-themes/themes/default-theme.css'
import { EditorWrapper } from '../molecules/EditorWrapper'
import { Info } from '../molecules/Info'
import { EditorWrapper, Info } from '../style'
const options = {
// schema: new CreateSchema(XpubSchema),
......
import React from 'react'
import styled from 'styled-components'
import { get } from 'lodash'
import { Attachment } from '@pubsweet/ui'
// import { grid } from '@pubsweet/ui-toolkit'
const Root = styled.div``
import form from '../../../../../storage/forms/submit.json'
const Title = styled.div``
import {
Container,
Title,
SectionHeader,
SectionRow,
SectionRowGrid,
} from '../style'
const Heading = styled.span`
font-weight: inherit;
padding: 0 1em 0 0;
white-space: nowrap;
text-align: right;
flex-grow: 0;
flex-shrink: 0;
flex-basis: 50%;
`
const Metadata = styled.div`
div {
display: flex;
flex-direction: row;
justify-content: flex-start;
}
`
// const MetadataRow = styled.div`
// padding: ${grid(2)} ${grid(3)};
// `
// const Metadata = styled.div`
// div {
// display: flex;
// flex-direction: row;
// // justify-content: flex-start;
// }
// `
const Cell = styled.span`
padding: 0;
flex-grow: 0;
flex-shrink: 0;
flex-basis: 50%;
grid-column: span 2 / span 2;
`
const getNote = (notes, type) =>
......@@ -40,6 +46,10 @@ const getDeclarations = (manuscript, field) =>
const getSupplementaryFiles = supplementary =>
(supplementary || []).filter(file => file.fileType === 'supplementary') || []
const showFieldData = (manuscript, fieldName) => {
const data = get(manuscript, fieldName)
return Array.isArray(data) ? data.join(', ') : data
}
// Due to migration to new Data Model
// Attachement component needs different data structure to work
// needs to change the pubsweet ui Attachement to support the new Data Model
......@@ -48,76 +58,38 @@ const filesToAttachment = file => ({
url: file.url,
})
const ReviewMetadata = ({ manuscript }) => (
<Root>
<Title>Metadata</Title>
const ReviewMetadata = ({ manuscript: rawManuscript }) => {
// Parse submission metadata JSON for display purposes
const manuscript = {
...rawManuscript,
submission: JSON.parse(rawManuscript.submission),
}
<Metadata>
<div>
<Heading>Open Peer Review :</Heading>
<Cell>
{getDeclarations(manuscript, 'openPeerReview') === 'yes'
? 'Yes'
: 'No'}
</Cell>
</div>
<div>
<Heading>Streamlined Review :</Heading>
<Cell>
{getDeclarations(manuscript, 'streamlinedReview') === 'yes'
? 'Please view supplementary uploaded files'
: 'No'}
</Cell>
</div>
<div>
<Heading>Part of Research Nexus :</Heading>
<Cell>
{getDeclarations(manuscript, 'researchNexus') === 'yes'
? 'Yes'
: 'No'}
</Cell>
</div>
<div>
<Heading>Pre-registered :</Heading>
<Cell>
{getDeclarations(manuscript, 'preregistered') === 'yes'
? 'Yes'
: 'No'}
</Cell>
</div>
<div>
<Heading>Suggested Reviewers :</Heading>
<Cell>
{((manuscript.suggestions || {}).reviewers || {}).suggested || 'None'}
</Cell>
</div>
<div>
<Heading>Opposed Reviewers :</Heading>
<Cell>
{((manuscript.suggestions || {}).reviewers || {}).opposed || 'None'}
</Cell>
</div>
<div>
<Heading>Suggested Editors :</Heading>
<Cell>
{((manuscript.suggestions || {}).editors || {}).suggested || 'None'}
</Cell>
</div>
<div>
<Heading>Opposed Editors :</Heading>
<Cell>
{((manuscript.suggestions || {}).editors || {}).opposed || 'None'}
</Cell>
</div>
<div>
<Heading>Special Instructions :</Heading>
const sortedFormElements = form.children.sort((a, b) =>
parseInt(a.order || '0') > parseInt(b.order | '0') ? 1 : -1,
)
return (
<Container>
<SectionHeader>
<Title>Metadata</Title>
</SectionHeader>
{sortedFormElements.map(element => (
<SectionRowGrid key={element.id}>
<Heading>{element.shortDescription || element.title}</Heading>
<Cell>{showFieldData(manuscript, element.name)}</Cell>
</SectionRowGrid>
))}
<SectionRowGrid>
<Heading>Special Instructions</Heading>
<Cell>
{getNote(manuscript.meta.notes || [], 'specialInstructions')
.content || 'None'}
</Cell>
</div>
</SectionRowGrid>
{getSupplementaryFiles(manuscript.files).length > 0 && (
<div>
<SectionRowGrid>
<Heading>
{getSupplementaryFiles(manuscript.files).length} supplementary{' '}
{getSupplementaryFiles(manuscript.files).length === 1
......@@ -136,13 +108,10 @@ const ReviewMetadata = ({ manuscript }) => (
))}
</Cell>
)}
</div>
</SectionRowGrid>
)}
<Title>Additional metadata</Title>
{// TODO
JSON.stringify(JSON.parse(manuscript.submission), null, 2)}
</Metadata>
</Root>
)
</Container>
)
}
export default ReviewMetadata
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