Commit 05bedc5d authored by Yannis Barlas's avatar Yannis Barlas

feat(*): add curator role

parent 949f96cc
......@@ -69,6 +69,7 @@ const Dashboard = props => {
authorArticles,
client,
createManuscript,
curatorArticles,
currentUser,
deleteArticle,
editorArticles,
......@@ -88,6 +89,7 @@ const Dashboard = props => {
const isEditor = currentUser.auth.isGlobalEditor
const isScienceOfficer = currentUser.auth.isGlobalScienceOfficer
const isSectionEditor = currentUser.auth.isGlobalSectionEditor
const isCurator = currentUser.auth.isGlobalCurator
const options = [
{
......@@ -271,6 +273,14 @@ const Dashboard = props => {
label="Science Officer Section"
/>
)}
{isCurator && (
<Section
itemComponent={EditorSectionItem}
items={curatorArticles}
label="Curator Section"
/>
)}
</DashboardWrapper>
<ArticlePreviewModal
......
......@@ -207,6 +207,7 @@ const mapProps = args => {
})
const authorArticles = makeArticleData('author')
const curatorArticles = makeArticleData('curator')
const reviewerArticles = makeArticleData('reviewer')
const editorArticles = makeArticleData('editor')
const scienceOfficerArticles = makeArticleData('scienceOfficer')
......@@ -241,6 +242,7 @@ const mapProps = args => {
allEditors,
allScienceOfficers,
authorArticles,
curatorArticles,
createManuscript: args.createManuscript.createManuscript,
deleteArticle: args.deleteArticle.deleteArticle,
editorArticles,
......
......@@ -263,6 +263,23 @@ const DASHBOARD_MANUSCRIPTS = gql`
}
}
}
curator: manuscripts(role: "curator") {
...ManuscriptFields
teams {
...TeamFields
}
versions {
...VersionFields
reviews {
status {
submitted
}
}
teams {
...TeamFields
}
}
}
}
`
......
......@@ -17,7 +17,7 @@ const GET_ARTICLE_FOR_EDITOR = gql`
displayName
}
}
reviewerId
userId
}
currentlyWith
doi
......
......@@ -8,7 +8,7 @@ const GET_USER_REVIEWS_FOR_ARTICLE = gql`
query GetUserReviewsForArticle($id: ID!) {
manuscript(id: $id) {
id
chatThreads(currentReviewerOnly: true, type: reviewer) {
chatThreads(currentUserOnly: true, type: reviewer) {
id
messages {
id
......
......@@ -110,7 +110,7 @@ const ArticlePreview = props => {
</DiscreetButton>
<ChatModal
headerText="Chat with the editors"
headerText={`Chat with the ${isEditor ? 'authors' : 'editors'}`}
isOpen={showChatModal}
messages={authorChatMessages}
onRequestClose={() => setShowChatModal(false)}
......
......@@ -46,7 +46,7 @@ const ReviewerInfo = props => {
const reviewWithChat = clone(review)
reviewWithChat.chatThread = reviewerChatThreads.find(
thread => thread.reviewerId === review.reviewer.id,
thread => thread.userId === review.reviewer.id,
)
return reviewWithChat
......
......@@ -125,187 +125,7 @@ const SCIENCE_OFFICER_PREVIEW = gql`
}
`
// const GET_MANUSCRIPT = gql`
// query manuscript($id: ID!) {
// manuscript(id: $id) {
// id
// chatThreads(type: author) {
// id
// chatType
// messages {
// content
// timestamp
// user {
// displayName
// }
// }
// }
// dataType
// isDataTypeSelected
// isInitiallySubmitted
// versions {
// id
// created
// active
// submitted
// isApprovedByScienceOfficer
// acknowledgements
// authors {
// affiliations
// credit
// email
// firstName
// lastName
// submittingAuthor
// correspondingAuthor
// equalContribution
// WBId
// orcid
// }
// comments
// decision
// disclaimer
// funding
// image {
// name
// url
// }
// imageCaption
// laboratory {
// name
// WBId
// }
// methods
// reagents
// patternDescription
// references {
// reference
// pubmedId
// doi
// }
// suggestedReviewer {
// name
// WBId
// }
// title
// geneExpression {
// antibodyUsed
// backboneVector {
// name
// WBId
// }
// coinjected
// constructComments
// constructionDetails
// detectionMethod
// dnaSequence {
// name
// WBId
// }
// expressionPattern {
// name
// WBId
// }
// fusionType {
// name
// WBId
// }
// genotype
// injectionConcentration
// inSituDetails
// integratedBy {
// name
// WBId
// }
// observeExpression {
// certainly {
// certainly {
// name
// WBId
// }
// during {
// name
// WBId
// }
// id
// subcellularLocalization {
// name
// WBId
// }
// }
// partially {
// partially {
// name
// WBId
// }
// during {
// name
// WBId
// }
// id
// subcellularLocalization {
// name
// WBId
// }
// }
// possibly {
// possibly {
// name
// WBId
// }
// during {
// name
// WBId
// }
// id
// subcellularLocalization {
// name
// WBId
// }
// }
// not {
// not {
// name
// WBId
// }
// during {
// name
// WBId
// }
// id
// subcellularLocalization {
// name
// WBId
// }
// }
// }
// reporter {
// name
// WBId
// }
// species {
// name
// WBId
// }
// strain
// transgeneName
// transgeneUsed {
// name
// WBId
// }
// utr {
// name
// WBId
// }
// variation {
// name
// WBId
// }
// }
// }
// }
// }
// `
const CURATOR_PREVIEW = SCIENCE_OFFICER_PREVIEW
const MANUSCRIPT_STATUS = gql`
query ManuscriptStatus($id: ID!) {
......@@ -512,7 +332,7 @@ const EDITOR_PANEL = gql`
displayName
}
}
reviewerId
userId
}
# currentlyWith
dataType
......@@ -543,6 +363,17 @@ const EDITOR_PANEL = gql`
lastName
submittingAuthor
}
curatorReviews {
id
content
curator {
id
displayName
}
recommendation
openAcknowledgement
submitted
}
decision
decisionLetter
reviews {
......@@ -658,7 +489,7 @@ const REVIEWER_PANEL = gql`
query ReviewerPanel($id: ID!) {
manuscript(id: $id) {
id
chatThreads(currentReviewerOnly: true, type: reviewer) {
chatThreads(currentUserOnly: true, type: reviewer) {
id
messages {
id
......@@ -690,12 +521,67 @@ const REVIEWER_PANEL = gql`
}
`
const CURATOR_PANEL = gql`
query ReviewerPanel($id: ID!) {
manuscript(id: $id) {
id
chatThreads(currentUserOnly: true, type: curator) {
id
messages {
id
content
timestamp
user {
id
displayName
}
}
}
versions {
id
created
decision
curatorReviews(currentUserOnly: true) {
id
content
recommendation
openAcknowledgement
submitted
}
}
}
}
`
const REINVITE_REVIEWER = gql`
mutation ReinviteReviewer($input: ReinviteReviewerInput!) {
reinviteReviewer(input: $input)
}
`
const SAVE_CURATOR_REVIEW = gql`
mutation SaveCuratorReview($id: ID!, $input: CuratorReviewInput!) {
saveCuratorReview(id: $id, input: $input) {
id
content
recommendation
openAcknowledgement
}
}
`
const SUBMIT_CURATOR_REVIEW = gql`
mutation SubmitCuratorReview($id: ID!, $input: CuratorReviewInput!) {
submitCuratorReview(id: $id, input: $input) {
id
content
recommendation
openAcknowledgement
submitted
}
}
`
const SAVE_FORM = gql`
mutation saveSubmissionForm($input: SubmissionFormInput!) {
saveSubmissionForm(input: $input)
......@@ -762,6 +648,8 @@ const UPLOAD_FILE = gql`
`
export {
CURATOR_PANEL,
CURATOR_PREVIEW,
EDITOR_PANEL,
MANUSCRIPT_SUBMISSION_FORM,
MANUSCRIPT_STATUS,
......@@ -770,12 +658,14 @@ export {
REVIEWER_PREVIEW,
FULL_PREVIEW,
REINVITE_REVIEWER,
SAVE_CURATOR_REVIEW,
SAVE_FORM,
SAVE_REVIEW,
SCIENCE_OFFICER_PANEL,
SCIENCE_OFFICER_PREVIEW,
SEND_CHAT,
SET_DATA_TYPE,
SUBMIT_CURATOR_REVIEW,
SUBMIT_DECISION,
SUBMIT_MANUSCRIPT,
SUBMIT_REVIEW,
......
......@@ -7,6 +7,7 @@ import { MANUSCRIPT_STATUS } from '../graphql'
import CurrentUserContext from '../userContext'
import {
CuratorView,
EditorView,
Preview,
ReviewerView,
......@@ -36,6 +37,7 @@ const Article = props => {
const isScienceOfficer = currentUser.auth.isAssignedScienceOfficer.includes(
manuscriptId,
)
const isCurator = currentUser.auth.isAssignedCurator.includes(manuscriptId)
// const showSplitScreen = isEditor
......@@ -152,6 +154,10 @@ const Article = props => {
return <ScienceOfficerView manuscriptId={manuscriptId} />
}
if (isCurator) {
return <CuratorView manuscriptId={manuscriptId} />
}
return null
}
......
import { get } from 'lodash'
const getInvitedReviewersTeam = versionTeams => {
const reviewerTeam = versionTeams.find(t => t.role === 'reviewer')
const invitedReviewers = reviewerTeam.members.filter(member =>
......@@ -23,7 +25,11 @@ const getManuscriptTeam = manuscriptTeams => {
const manuscriptSOs = membersOfTeam(manuscriptTeams, 'scienceOfficer')
const manuscriptSO = manuscriptSOs && manuscriptSOs[0]
const manuscriptCurators = membersOfTeam(manuscriptTeams, 'curator')
const manuscriptCurator = manuscriptCurators && manuscriptCurators[0]
return {
curator: manuscriptCurator,
editor: manuscriptEditor,
sectionEditor: manuscriptSectionEditor,
scienceOfficer: manuscriptSO,
......@@ -79,6 +85,15 @@ const transformChatMessages = messages =>
displayName: message.user.displayName,
}))
const transformCuratorReviews = reviews =>
reviews.map(review => ({
content: review.content,
curatorId: get(review, 'curator.id'),
openAcknowledgement: review.openAcknowledgement,
recommendation: review.recommendation,
submitted: review.submitted,
}))
const transformReviews = reviews =>
reviews.map(review => ({
askedToSeeRevision: review.askedToSeeRevision,
......@@ -98,5 +113,6 @@ export {
getSubmittingAuthor,
membersOfTeam,
transformChatMessages,
transformCuratorReviews,
transformReviews,
}
......@@ -5,6 +5,7 @@ import {
getSubmittingAuthor,
membersOfTeam,
transformChatMessages,
transformCuratorReviews,
transformReviews,
} from './common'
......@@ -78,7 +79,7 @@ export default (data, mutations) => {
* Manuscript-level teams
*/
// const manuscriptCuratorTeam = manuscript.teams.find(t => t.role === 'curator')
const manuscriptCuratorTeam = manuscript.teams.find(t => t.role === 'curator')
const manuscriptEditorTeam = manuscript.teams.find(t => t.role === 'editor')
const manuscriptSectionEditorTeam = manuscript.teams.find(
t => t.role === 'sectionEditor',
......@@ -88,6 +89,7 @@ export default (data, mutations) => {
)
const {
curator: assignedCurator,
editor: assignedEditor,
sectionEditor: assignedSectionEditor,
scienceOfficer: assignedScienceOfficer,
......@@ -100,7 +102,7 @@ export default (data, mutations) => {
const chatData = chatThreads.map(thread => ({
id: thread.id,
chatType: thread.chatType,
reviewerId: thread.reviewerId,
reviewerId: thread.userId,
messages: transformChatMessages(thread.messages),
}))
......@@ -118,6 +120,7 @@ export default (data, mutations) => {
const {
authors,
created,
curatorReviews,
decision,
decisionLetter,
id,
......@@ -132,6 +135,11 @@ export default (data, mutations) => {
const invitedReviewers = getInvitedReviewersTeam(teams)
const reviewerCounts = getReviewerCounts(teams)
const versionReviews = transformReviews(reviews)
const curatorReview =
assignedCurator &&
transformCuratorReviews(curatorReviews).find(
item => item.curatorId === assignedCurator.id,
)
const previousReviewers = []
if (latest && filteredVersions.length > 1) {
......@@ -178,6 +186,7 @@ export default (data, mutations) => {
acceptedReviewersCount: reviewerCounts.accepted,
rejectedReviewersCount: reviewerCounts.rejected,
curatorReview,
reviews: versionReviews,
previousReviewers,
submitted,
......@@ -225,9 +234,9 @@ export default (data, mutations) => {
})
const updateManuscriptTeams = values => {
// const curatorMembers = []
// if (values.curator && values.curator.id)
// curatorMembers.push(values.curator.id)
const curatorMembers = []
if (values.curator && values.curator.id)
curatorMembers.push(values.curator.id)
const editorMembers = []
if (values.editor && values.editor.id) {
......@@ -244,18 +253,14 @@ export default (data, mutations) => {
scienceOfficerMembers.push(values.scienceOfficer.id)
}
// console.log('ct', curatorTeamId, curatorMembers)
// console.log('et', editorTeamId, editorMembers)
// console.log('st', scienceOfficerTeamId, scienceOfficerMembers)
return updateManuscriptTeamsMutation({
variables: {
input: {
teams: [
// {
// teamId: curatorTeamId,
// members: curatorMembers,
// },
{
teamId: manuscriptCuratorTeam.id,
members: curatorMembers,
},
{
teamId: manuscriptEditorTeam.id,
members: editorMembers,
......@@ -290,6 +295,7 @@ export default (data, mutations) => {
scienceOfficers,
curators,
assignedCurator,
assignedEditor,
assignedSectionEditor,
assignedScienceOfficer,
......
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { useQuery, useMutation } from '@apollo/react-hooks'
import {
CURATOR_PREVIEW,
CURATOR_PANEL,
SAVE_CURATOR_REVIEW,
SEND_CHAT,
SUBMIT_CURATOR_REVIEW,
} from '../../graphql'
import {
ChatModal,
DateParser,
ReviewerPanel,
ReviewSubmissionConfirmation,
SyncedTabs,
} from '../../../ui'
import { ArticlePreview } from '../../components/ui'
import { transformChatMessages } from '../_helpers/common'
/* eslint-disable-next-line react/prop-types */
const Label = ({ created, index }) => (
<DateParser dateFormat="MM.DD.YY HH:mm" timestamp={new Date(Number(created))}>
{timestamp => (
<span>
{index === 0
? `Original: ${timestamp}`
: `Revision ${index}: ${timestamp}`}
</span>
)}
</DateParser>
)
const CuratorView = props => {
const { manuscriptId } = props
/**
* Handle modals with state
*/
const [showChatModal, setShowChatModal] = useState(false)
const [showConfirmSubmission, setShowConfirmSubmission] = useState(false)
const [submissionData, setSubmissionData] = useState(null)
/**
* Queries & Mutations
*/
const { data: previewData, loading: previewLoading } = useQuery(
CURATOR_PREVIEW,
{
variables: {
id: manuscriptId,
},
},
)
const { data: panelData, loading: panelLoading } = useQuery(CURATOR_PANEL, {
variables: {
id: manuscriptId,
},
})
const [saveMutation] = useMutation(SAVE_CURATOR_REVIEW)
const [submitMutation] = useMutation(SUBMIT_CURATOR_REVIEW)
const [sendChatMutation] = useMutation(SEND_CHAT, {
refetchQueries: [
{
query: CURATOR_PANEL,
variables: { id: manuscriptId },
},
],
})
/**
* Left side: preview
*/
const leftSections =
previewData &&
previewData.manuscript &&
previewData.manuscript.versions.map((version, index) => ({
key: version.id,
label: <Label created={version.created} index={index} />,
content: (
<ArticlePreview
article={version}
manuscriptId={manuscriptId}
previousVersion={previewData.manuscript.versions[index - 1]}
showAdditionalData
showHeader={false}
/>
),
}))
/**
* Right side: Curator panel
*/
let chatMessages, sendChatMessage
let rightSections
let submitReview
if (panelData && panelData.manuscript && panelData.manuscript.versions) {
const { manuscript } = panelData
const { chatThreads, versions } = manuscript
// there can only be one chat per curator per manuscript
const chatThread = chatThreads[0]
chatMessages = transformChatMessages(chatThread.messages)
sendChatMessage = content =>
sendChatMutation({
variables: {
input: {
chatThreadId: chatThread.id,
content,
},
},
})
rightSections = versions.map((version, index) => {
const { decision, curatorReviews } = version
const latest = index === versions.length - 1
// there can only be one review per curator per version
const thisReview = curatorReviews[0]
const { submitted } = thisReview
const review = {
content: thisReview.content,