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

feat: review component improvements

parent ddb6d53e
No related branches found
No related tags found
No related merge requests found
Showing
with 179 additions and 280 deletions
......@@ -176,9 +176,9 @@ const createFileMutation = gql`
}
`
const submitMutation = gql`
mutation($id: ID!, $input: String) {
submitManuscript(id: $id, input: $input) {
const makeDecisionMutation = gql`
mutation($id: ID!, $decision: String) {
makeDecision(id: $id, decision: $decision) {
id
${fragmentFields}
}
......@@ -294,7 +294,7 @@ const editorSections = ({ manuscript }) => {
const DecisionPage = ({ match }) => {
// Hooks from the old world
const [completeDecision] = useMutation(submitMutation, {
const [makeDecision] = useMutation(makeDecisionMutation, {
refetchQueries: [query],
})
const [updateReviewMutation] = useMutation(updateReviewMutationQuery)
......@@ -306,13 +306,19 @@ const DecisionPage = ({ match }) => {
variables: {
id: match.params.version,
},
fetchPolicy: 'network-only',
})
if (loading) return <Spinner />
if (error) return `Error! ${error.message}`
const manuscript = data.manuscript
const channelId = manuscript.channels.find(c => c.type === 'editorial').id
// Protect if channels don't exist for whatever reason
let channelId
if (Array.isArray(manuscript.channels) && manuscript.channels.length) {
channelId = manuscript.channels.find(c => c.type === 'editorial').id
}
const uploadFile = (file, updateReview, type) =>
uploadReviewFiles({
......@@ -391,20 +397,7 @@ const DecisionPage = ({ match }) => {
<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,
}),
},
})
}}
initialValues={initialValues}
// isInitialValid={({ manuscript }) => {
// const rv =
// manuscript.reviews.find(review => review.isDecision) || {}
......@@ -413,7 +406,15 @@ const DecisionPage = ({ match }) => {
// return isCommented && isRecommendation
// }}
initialValues={initialValues}
onSubmit={() => {
makeDecision({
variables: {
id: manuscript.id,
decision: manuscript.reviews.find(review => review.isDecision)
.recommendation,
},
})
}}
validate={(values, props) => {
const errors = {}
if (getCommentContent(values, 'note') === '') {
......@@ -456,9 +457,7 @@ const DecisionPage = ({ match }) => {
</Formik>
</Manuscript>
<Chat>
<MessageContainer channelId={channelId} />
</Chat>
<Chat>{channelId && <MessageContainer channelId={channelId} />}</Chat>
</Columns>
)
}
......
......@@ -229,6 +229,7 @@ export default ({ match, ...props }) => {
variables: {
id: match.params.version,
},
fetchPolicy: 'network-only',
})
if (loading) return <Spinner />
......@@ -327,23 +328,12 @@ export default ({ match, ...props }) => {
},
},
}).then(() => {
history.push('/dashboard')
history.push('/journal/home')
})
}
return (
<Formik
handleSubmit={(props, { props: { completeReview, history } }) =>
completeReview(history)
}
// isInitialValid: ({ review }) => {
// if (!review.id) return false
// const hasRecommendation = review.recommendation !== null
// const comment = getCommentContent(review, 'note')
// const isCommented = comment !== null && comment !== ''
// return isCommented && hasRecommendation
// },
initialValues={
(manuscript.reviews &&
manuscript.reviews.find(
......@@ -354,16 +344,28 @@ export default ({ match, ...props }) => {
recommendation: null,
}
}
onSubmit={values => completeReview(props.history)}
validateOnMount={review => {
if (!review.id) return false
const hasRecommendation = review.recommendation !== null
const comment = getCommentContent(review, 'note')
const isCommented = comment !== null && comment !== ''
return isCommented && hasRecommendation
}}
>
<ReviewLayout
currentUser={currentUser}
manuscript={manuscript}
review={review}
status={status}
updateReview={updateReview}
uploadFile={uploadFile}
channelId={channelId}
/>
{formikProps => (
<ReviewLayout
channelId={channelId}
currentUser={currentUser}
manuscript={manuscript}
review={review}
status={status}
updateReview={updateReview}
uploadFile={uploadFile}
{...formikProps}
/>
)}
</Formik>
)
}
......
......@@ -21,6 +21,8 @@ const teamFields = `
user {
id
username
profilePicture
online
}
status
}
......@@ -93,6 +95,8 @@ const query = gql`
users {
id
username
profilePicture
online
admin
}
......
......@@ -17,10 +17,6 @@ const AssignEditorsReviewers = ({ manuscript, AssignEditor }) => (
<SectionRowGrid>
<AssignEditor manuscript={manuscript} teamRole="seniorEditor" />
<AssignEditor manuscript={manuscript} teamRole="handlingEditor" />
<Action to={`/journal/versions/${manuscript.id}/reviewers`}>
Assign Reviewers
</Action>
</SectionRowGrid>
</Container>
)
......
A decision on a version of a project.
```js
const decision = {
id: faker.random.uuid(),
comments: [{ type: 'note', content: 'this needs review' }],
created: 'Thu Oct 11 2018',
open: false,
status: '<p>This is a decision</p>',
user: { identities: [] },
}
;<Decision decision={decision} />
```
......@@ -54,7 +54,6 @@ const NoteInput = ({
key="note-input"
onBlur={() => {}}
onChange={value => {
console.log(values)
const { updateIndex, comment } = createComments(
values,
{
......@@ -135,32 +134,32 @@ const RecommendationInput = ({
const DecisionForm = ({ handleSubmit, uploadFile, updateReview, isValid }) => (
<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}
/>
<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}
/>
<SectionAction key="submit">
<Button disabled={!isValid} primary type="submit">
Submit
</Button>
</SectionAction>
</SectionRowGrid>
{/* </form> */}
<SectionAction key="submit">
<Button disabled={!isValid} primary type="submit">
Submit
</Button>
</SectionAction>
</SectionRowGrid>
</form>
</Container>
)
......
Reviews of a version of a project, as shown when making a decision.
```js
const review = {
comments: [{ content: 'this needs review' }],
created: 'Thu Oct 11 2018',
open: false,
recommendation: 'revise',
user: { id: 1, username: 'test user' },
}
const reviewer = {
ordinal: faker.random.number({ min: 1, max: 5 }),
name: faker.name.findName(),
}
;<DecisionReview open review={review} reviewer={{ name: 'test user' }} />
```
The review is hidden by default, but can be toggled to display the review.
```js
const review = {
comments: [{ content: 'this needs review' }],
created: 'Thu Oct 11 2018',
open: false,
recommendation: 'revise',
user: { id: 1, username: 'test user' },
}
const reviewer = {
ordinal: faker.random.number({ min: 1, max: 5 }),
name: faker.name.findName(),
}
;<DecisionReview review={review} reviewer={{ name: 'test user' }} />
```
import React from 'react'
import DecisionReview from './DecisionReview'
import { Container, SectionHeader, SectionRow, Title } from '../style'
import { H1, Action } from '@pubsweet/ui'
// TODO: read reviewer ordinal and name from project reviewer
// const { status } =
......@@ -19,8 +21,11 @@ const getCompletedReviews = (manuscript, currentUser) => {
}
const DecisionReviews = ({ manuscript }) => (
<div>
{manuscript.reviews &&
<Container>
<SectionHeader>
<Title>Reviews</Title>
</SectionHeader>
{manuscript.reviews && manuscript.reviews.length ? (
manuscript.reviews
.filter(
review =>
......@@ -28,7 +33,7 @@ const DecisionReviews = ({ manuscript }) => (
review.isDecision === false,
)
.map((review, index) => (
<div key={review.id}>
<SectionRow key={review.id}>
<DecisionReview
open
review={review}
......@@ -37,9 +42,16 @@ const DecisionReviews = ({ manuscript }) => (
ordinal: index + 1,
}}
/>
</div>
))}
</div>
</SectionRow>
))
) : (
<SectionRow>
<Action to={`/journal/versions/${manuscript.id}/reviewers`}>
Assign Reviewers
</Action>
</SectionRow>
)}
</Container>
)
export default DecisionReviews
Reviews of a version of a project, as shown when making a decision.
```js
const { JournalProvider } = require('xpub-journal')
const journal = require('@pubsweet/styleguide/config/journal')
const manuscriptTemplate = () => ({
id: faker.random.uuid(),
teams: [
{
id: faker.random.uuid(),
role: 'reviewerEditor',
name: 'Reviewer',
object: {
id: faker.random.uuid(),
__typename: 'Manuscript',
},
objectType: 'manuscript',
members: [
{
user: {
id: 1,
username: 'test user',
},
status: 'accepted',
},
],
},
],
meta: {
title: faker.lorem.sentence(25),
abstract: faker.lorem.sentence(100),
articleType: 'original-research',
declarations: {
openData: 'yes',
openPeerReview: 'no',
preregistered: 'yes',
previouslySubmitted: 'yes',
researchNexus: 'no',
streamlinedReview: 'no',
},
},
decision: {
id: faker.random.uuid(),
comments: [{ type: 'note', content: 'this needs review' }],
created: 'Thu Oct 11 2018',
open: false,
status: '<p>This is a decision</p>',
user: { id: 1 },
},
reviews: [
{
comments: [{ content: 'this needs review' }],
created: 'Thu Oct 11 2018',
open: false,
recommendation: 'revise',
user: { id: 1, username: 'test user' },
},
],
})
const manuscript = Object.assign({}, manuscriptTemplate(), {
manuscriptVersions: [manuscriptTemplate()],
})
;<JournalProvider journal={journal}>
<DecisionReviews manuscript={manuscript} />
</JournalProvider>
```
......@@ -17,6 +17,8 @@ import {
const Heading = styled.span`
font-weight: inherit;
padding: 0 1em 0 0;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
`
......
......@@ -13,7 +13,15 @@ import {
import { JournalContext } from 'xpub-journal'
import { getCommentFiles, stripHtml, createComments } from './util'
import { AdminSection } from '../style'
import {
AdminSection,
Container,
Title,
SectionHeader,
SectionRowGrid,
SectionRow,
SectionAction,
} from '../style'
const AttachmentsInput = ({
field,
......@@ -158,43 +166,50 @@ const ReviewComment = props => (
</>
)
const Title = styled.div``
const ReviewForm = ({
journal,
isValid,
isSubmitting,
handleSubmit,
updateReview,
uploadFile,
review,
}) => (
<form onSubmit={handleSubmit}>
<ReviewComment
review={review}
updateReview={updateReview}
uploadFile={uploadFile}
/>
<AdminSection>
<div name="Recommendation">
<Title>Recommendation</Title>
<Field name="recommendation" updateReview={updateReview}>
{props => (
<RecommendationInput
journal={journal}
updateReview={updateReview}
{...props}
/>
)}
</Field>
</div>
</AdminSection>
<AdminSection>
<Button disabled={!isValid} primary type="submit">
Submit
</Button>
</AdminSection>
</form>
<Container>
<form onSubmit={handleSubmit}>
<AdminSection>
<SectionHeader>
<Title>Review</Title>
</SectionHeader>
<SectionRow key="note">
<ReviewComment
review={review}
updateReview={updateReview}
uploadFile={uploadFile}
/>
</SectionRow>
<SectionHeader>
<Title>Recommendation</Title>
</SectionHeader>
<SectionRowGrid>
<Field name="recommendation" updateReview={updateReview}>
{props => (
<RecommendationInput
journal={journal}
updateReview={updateReview}
{...props}
/>
)}
</Field>
<SectionAction key="submit">
<Button disabled={!isValid || isSubmitting} primary type="submit">
Submit
</Button>
</SectionAction>
</SectionRowGrid>
</AdminSection>
</form>
</Container>
)
export default ReviewForm
......@@ -7,7 +7,7 @@ import ReviewForm from './ReviewForm'
import ReviewMetadata from '../metadata/ReviewMetadata'
import Review from './Review'
import EditorSection from '../decision/EditorSection'
import { Columns, Manuscript, Chat } from '../style'
import { Columns, Manuscript, Chat, AdminSection } from '../style'
import MessageContainer from '../../../../component-chat/src'
const addEditor = (manuscript, label) => ({
......
......@@ -3,11 +3,11 @@ import styled from 'styled-components'
import { Link } from '@pubsweet/ui'
import { th } from '@pubsweet/ui-toolkit'
import ReviewerForm from './ReviewerForm'
import { Container, PaddedContent } from '../../../../shared'
// TODO: Make this a proper shared component?
import { UserAvatar } from '../../../../component-avatar/src'
const Root = styled.div`
display: flex;
margin-top: calc(${th('gridUnit')} * 3);
`
const Form = styled.div``
const ReviewersList = styled.div`
display: flex;
......@@ -26,30 +26,34 @@ const Reviewers = ({
handleSubmit,
teams,
}) => (
<Root>
<Form>
<ReviewerForm
handleSubmit={handleSubmit}
isValid={isValid}
journal={journal}
loadOptions={loadOptions}
reviewerUsers={reviewerUsers}
/>
<Link
to={`/journal/versions/${manuscript.id}/decisions/${manuscript.id}`}
>
Back to Control Panel
</Link>
</Form>
{reviewers && (
<ReviewersList>
{reviewers.map(reviewer => (
<Reviewer journal={journal} key={reviewer.id} reviewer={reviewer} />
))}
</ReviewersList>
)}
</Root>
<Container>
<PaddedContent>
<Form>
<ReviewerForm
handleSubmit={handleSubmit}
isValid={isValid}
journal={journal}
loadOptions={loadOptions}
reviewerUsers={reviewerUsers}
/>
<Link
to={`/journal/versions/${manuscript.id}/decisions/${manuscript.id}`}
>
Back to Control Panel
</Link>
</Form>
</PaddedContent>
<PaddedContent>
{reviewers && (
<ReviewersList>
{reviewers.map(reviewer => (
<UserAvatar key={reviewer.id} user={reviewer.user} />
// <Reviewer journal={journal} key={reviewer.id} username={reviewer.user.username} />
))}
</ReviewersList>
)}
</PaddedContent>
</Container>
)
export default Reviewers
......@@ -77,28 +77,10 @@ export const Container = styled.div`
// padding: ${grid(2)} ${grid(3)};
`
export const Title = styled.h2`
font-size: ${th('fontSizeHeading5')};
font-weight: 500;
`
export const SectionHeader = styled.div`
padding: ${grid(2)} ${grid(3)};
border-bottom: 1px solid ${th('colorFurniture')};
`
export const SectionRow = styled.div`
border-bottom: 1px solid ${th('colorFurniture')};
padding: ${grid(2)} ${grid(3)};
`
export const SectionRowGrid = styled(SectionRow)`
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-gap: ${grid(2)};
`
export const SectionAction = styled.div`
grid-column: 3;
justify-self: end;
`
export {
Title,
SectionHeader,
SectionRow,
SectionRowGrid,
SectionAction,
} from '../../../shared'
File moved
File moved
File moved
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