diff --git a/packages/component-faraday-selectors/src/index.js b/packages/component-faraday-selectors/src/index.js index 3dcdaa7a4cea7472a0d05a947f1a318fc9aee9a3..868a670bd4a2db39005389eb4a5319fd7d06e828 100644 --- a/packages/component-faraday-selectors/src/index.js +++ b/packages/component-faraday-selectors/src/index.js @@ -89,8 +89,9 @@ export const canViewReviewersDetails = (state, collection = {}) => { return canViewReports(state, get(collection, 'id', '')) } -const authorCanViewReportsDetailsStatuses = [ +const authorAndReviewersCanViewReportsDetailsStatuses = [ 'revisionRequested', + 'underReview', 'pendingApproval', 'rejected', 'accepted', @@ -106,13 +107,29 @@ export const authorCanViewReportsDetails = ( const isAuthor = currentUserIsAuthor(state, fragmentId) return ( isAuthor && - (authorCanViewReportsDetailsStatuses.includes( + (authorAndReviewersCanViewReportsDetailsStatuses.includes( get(collection, 'status', 'draft'), ) || canViewContextualBoxOnOldVersion(collection, fragmentId)) ) } +export const reviewersCanViewReviewerReports = ( + state, + collection = {}, + fragmentId, +) => { + const isReviewer = currentUserIsReviewer(state, fragmentId) + const reviewerReports = getFragmentReviewerRecommendations(state, fragmentId) + return ( + isReviewer && + authorAndReviewersCanViewReportsDetailsStatuses.includes( + get(collection, 'status', 'draft'), + ) && + reviewerReports.length > 0 + ) +} + const canHeViewEditorialCommentsStatuses = [ 'revisionRequested', 'rejected', diff --git a/packages/component-faraday-ui/src/ReviewerReport.js b/packages/component-faraday-ui/src/ReviewerReport.js index feaa1d39389a4bde4a863b4d9fb63c004073d71b..fc2c942c5bb4fdee93bfcbd64cc2bb05d768d3f2 100644 --- a/packages/component-faraday-ui/src/ReviewerReport.js +++ b/packages/component-faraday-ui/src/ReviewerReport.js @@ -11,6 +11,7 @@ const ReviewerReport = ({ onPreview, onDownload, reportFile, + currentUser, publicReport, privateReport, reviewerName, @@ -27,14 +28,12 @@ const ReviewerReport = ({ </Item> <Item justify="flex-end"> - {showOwner && ( - <Fragment> - <Text>{reviewerName}</Text> - <Text customId ml={1} mr={1}> - {`Reviewer ${reviewerNumber}`} - </Text> - </Fragment> - )} + <Fragment> + {showOwner && <Text>{reviewerName}</Text>} + <Text customId ml={1} mr={1}> + {`Reviewer ${reviewerNumber}`} + </Text> + </Fragment> <DateParser timestamp={submittedOn}> {date => <Text>{date}</Text>} </DateParser> @@ -76,20 +75,22 @@ const ReviewerReport = ({ </Root> ) -export default withProps(({ report, journal: { recommendations = [] } }) => ({ - recommendation: get( - recommendations.find(r => r.value === report.recommendation), - 'label', - ), - reportFile: get(report, 'comments.0.files.0'), - publicReport: get(report, 'comments.0.content'), - privateReport: get(report, 'comments.1.content'), - reviewerName: `${get(report, 'reviewer.firstName', '')} ${get( - report, - 'reviewer.lastName', - '', - )}`, -}))(ReviewerReport) +export default withProps( + ({ report, currentUser, journal: { recommendations = [] } }) => ({ + recommendation: get( + recommendations.find(r => r.value === report.recommendation), + 'label', + ), + reportFile: get(report, 'comments.0.files.0'), + publicReport: get(report, 'comments.0.content'), + privateReport: get(report, 'comments.1.content'), + reviewerName: `${get(currentUser, 'firstName', '')} ${get( + currentUser, + 'lastName', + '', + )}`, + }), +)(ReviewerReport) // #region styles const Root = styled.div` diff --git a/packages/component-manuscript-manager/src/tests/collections/get.test.js b/packages/component-manuscript-manager/src/tests/collections/get.test.js index aa4a1cfec4a3e9de92809e07e056a81b2aaede1f..b721805c99c9b00833fcb953cc16f5a529840164 100644 --- a/packages/component-manuscript-manager/src/tests/collections/get.test.js +++ b/packages/component-manuscript-manager/src/tests/collections/get.test.js @@ -58,7 +58,6 @@ describe('Get collections route handler', () => { expect(res.statusCode).toBe(200) const data = JSON.parse(res._getData()) - expect(data).toHaveLength(2) expect(data[0].type).toEqual('collection') expect(data[0].currentVersion.recommendations).toHaveLength(6) diff --git a/packages/component-manuscript/src/components/ManuscriptLayout.js b/packages/component-manuscript/src/components/ManuscriptLayout.js index 0d1abdeb62cf16434d58430900d4b5a0f0bb5d2d..5f870ecfa43fe24b4886780c6a78feaf2f0d675b 100644 --- a/packages/component-manuscript/src/components/ManuscriptLayout.js +++ b/packages/component-manuscript/src/components/ManuscriptLayout.js @@ -17,9 +17,9 @@ import { ResponseToRevisionRequest, } from 'pubsweet-component-faraday-ui' -import ReviewerReportCard from './ReviewReportCard' import ReviewerReportForm from './ReviewerReportForm' import EditorialCommentCard from './EditorialCommentCard' +import ReviewerReports from './ReviewerReports' const messagesLabel = { 'return-to-handling-editor': 'Comments for Handling Editor', @@ -129,11 +129,18 @@ const ManuscriptLayout = ({ /> )} - {submittedOwnRecommendation && ( - <ReviewerReportCard + {get( + currentUser, + 'permissions.reviewersCanViewReviewerReports', + false, + ) && ( + <ReviewerReports + currentUser={currentUser} getSignedUrl={getSignedUrl} + invitations={invitationsWithReviewers} + isLatestVersion={isLatestVersion} journal={journal} - report={submittedOwnRecommendation} + reviewerReports={reviewerRecommendations} token={get(currentUser, 'token')} /> )} diff --git a/packages/component-manuscript/src/components/ManuscriptPage.js b/packages/component-manuscript/src/components/ManuscriptPage.js index 49431eac63aad495e794a5c5a5b6e749323191df..9ecbb8297bdce18ebcfd1b00c0b26e2b624af545 100644 --- a/packages/component-manuscript/src/components/ManuscriptPage.js +++ b/packages/component-manuscript/src/components/ManuscriptPage.js @@ -57,6 +57,7 @@ import { getOwnPendingRecommendation, getOwnSubmittedRecommendation, canAuthorViewEditorialComments, + reviewersCanViewReviewerReports, canHEMakeRecommendationToPublish, getFragmentReviewerRecommendations, getInvitationsWithReviewersForFragment, @@ -199,6 +200,11 @@ export default compose( collection, get(fragment, 'id', ''), ), + reviewersCanViewReviewerReports: reviewersCanViewReviewerReports( + state, + collection, + get(fragment, 'id', ''), + ), canOverrideTechChecks: canOverrideTechnicalChecks(state, collection), canAuthorViewEditorialComments: canAuthorViewEditorialComments( state, diff --git a/packages/component-manuscript/src/components/ReviewerReports.js b/packages/component-manuscript/src/components/ReviewerReports.js new file mode 100644 index 0000000000000000000000000000000000000000..659d3f2330753045f4cb1100a190e93d5b6307d9 --- /dev/null +++ b/packages/component-manuscript/src/components/ReviewerReports.js @@ -0,0 +1,80 @@ +import React from 'react' +import { compose, withProps } from 'recompose' +import { get } from 'lodash' +import { + ReviewerReport, + ContextualBox, + withFilePreview, + withFileDownload, + Text, + Row, + indexReviewers, +} from 'pubsweet-component-faraday-ui' + +const SubmittedReports = ({ reports }) => ( + <Row fitContent justify="flex-end"> + <Text customId mr={1 / 2}> + {reports} + </Text> + <Text mr={1 / 2} pr={1 / 2} secondary> + {' '} + submitted + </Text> + </Row> +) + +const ReviewerReports = ({ + journal, + reports, + previewFile, + downloadFile, + isLatestVersion, + currentUser, + token, + invitations, + reviwerReports, +}) => ( + <ContextualBox + label={isLatestVersion ? 'Your Report' : 'Reviewer Reports'} + mb={2} + rightChildren={<SubmittedReports reports={reports.length} />} + startExpanded + > + {reports.map(report => ( + <ReviewerReport + currentUser={currentUser} + journal={journal} + key={report.id} + onDownload={downloadFile} + onPreview={previewFile} + report={report} + reviewerNumber={report.reviewerNumber} + showOwner={report.userId === currentUser.id} + /> + ))} + </ContextualBox> +) + +export default compose( + withFileDownload, + withFilePreview, + withProps( + ({ + invitations = [], + publonReviewers = [], + reviewerReports = [], + currentUser, + }) => ({ + token: get(currentUser, 'token', ''), + publonReviewers, + invitations: invitations.map(i => ({ + ...i, + review: reviewerReports.find(r => r.userId === i.userId), + })), + reports: indexReviewers( + reviewerReports.filter(r => r.submittedOn), + invitations, + ), + }), + ), +)(ReviewerReports) diff --git a/packages/xpub-faraday/config/authsome-helpers.js b/packages/xpub-faraday/config/authsome-helpers.js index 08e583c721fa6103de2f4729e0f2e64a348567a7..af24148066d5dc53764e440da68fb2c965436f2d 100644 --- a/packages/xpub-faraday/config/authsome-helpers.js +++ b/packages/xpub-faraday/config/authsome-helpers.js @@ -72,6 +72,34 @@ const filterAuthorRecommendations = (recommendations, status, isLast) => { return [] } +const filterRecommendationsFromLastVersion = (recommendations, user) => + recommendations + .filter( + r => + r.userId === user.id || r.recommendationType === 'editorRecommendation', + ) + .map(r => ({ + ...r, + comments: r.comments.filter(c => c.public === true), + })) + +const filterRecommendationsFromOlderVersions = (recommendations, user) => { + const ownRecommendation = recommendations.find(r => r.userId === user.id) + if (ownRecommendation) { + return recommendations + .filter( + r => r.submittedOn || r.recommendationType === 'editorRecommendation', + ) + .map( + r => + r.userId !== ownRecommendation.userId + ? { ...r, comments: r.comments.filter(c => c.public === true) } + : { ...r }, + ) + } + return [] +} + const stripeCollectionByRole = ({ collection = {}, role = '' }) => { if (role === 'author') { const { handlingEditor } = collection @@ -96,7 +124,8 @@ const stripeFragmentByRole = ({ user = {}, isLast = false, }) => { - const { recommendations, files, authors } = fragment + const { files, authors, recommendations = [] } = fragment + let recommendationsFromFragment = [] switch (role) { case 'author': return { @@ -106,22 +135,22 @@ const stripeFragmentByRole = ({ : [], } case 'reviewer': + if (isLast) { + recommendationsFromFragment = filterRecommendationsFromLastVersion( + recommendations, + user, + ) + } else { + recommendationsFromFragment = filterRecommendationsFromOlderVersions( + recommendations, + user, + ) + } return { ...fragment, files: omit(files, ['coverLetter']), authors: authors.map(a => omit(a, ['email'])), - recommendations: recommendations - ? recommendations - .filter( - r => - r.userId === user.id || - r.recommendationType === 'editorRecommendation', - ) - .map(r => ({ - ...r, - comments: r.comments.filter(c => c.public === true), - })) - : [], + recommendations: recommendationsFromFragment, } case 'handlingEditor': return { @@ -175,10 +204,13 @@ const getCollections = async ({ user, models }) => { } else { fragment = await models.Fragment.find(userPermission.objectId) collection = await models.Collection.find(fragment.collectionId) + const latestFragmentId = + collection.fragments[collection.fragments.length - 1] collection.currentVersion = stripeFragmentByRole({ fragment, role: userPermission.role, user, + isLast: latestFragmentId === fragment.id, }) } } catch (e) { diff --git a/packages/xpub-faraday/tests/config/authsome-helpers.test.js b/packages/xpub-faraday/tests/config/authsome-helpers.test.js index c5634168fdf6ab29134874c7c3fa9ee96183b2d0..de16758af67242c6ab707a10a6f0f31f2cb24876 100644 --- a/packages/xpub-faraday/tests/config/authsome-helpers.test.js +++ b/packages/xpub-faraday/tests/config/authsome-helpers.test.js @@ -116,7 +116,7 @@ describe('Authsome Helpers', () => { const { files = {} } = result expect(files.coverLetter).toBeFalsy() }) - it('reviewer should not see private comments', () => { + it('reviewer should not see private comments on the last version of the manuscript', () => { const { fragment } = testFixtures.fragments fragment.recommendations = [ { @@ -132,13 +132,59 @@ describe('Authsome Helpers', () => { ], }, ] - const result = ah.stripeFragmentByRole({ fragment, role: 'reviewer' }) + const result = ah.stripeFragmentByRole({ + fragment, + role: 'reviewer', + isLast: true, + }) const { recommendations } = result expect(recommendations).toHaveLength(1) expect(recommendations[0].comments).toHaveLength(1) expect(recommendations[0].comments[0].public).toEqual(true) }) + it('reviewer should see other reviewers recommendations on previous version if he submitted a review on that fragment', () => { + const { fragment } = testFixtures.fragments + const { answerReviewer } = testFixtures.users + + const result = ah.stripeFragmentByRole({ + fragment, + role: 'reviewer', + isLast: false, + user: answerReviewer, + }) + const { recommendations } = result + expect(recommendations).toHaveLength(7) + }) + + it('reviewer should not see other reviewers recommendations on latest fragment', () => { + const { fragment } = testFixtures.fragments + const { answerReviewer } = testFixtures.users + + const result = ah.stripeFragmentByRole({ + fragment, + role: 'reviewer', + isLast: true, + user: answerReviewer, + }) + const { recommendations } = result + expect(recommendations).toHaveLength(6) + }) + + it('reviewer should not see any reviewer recommendation on previous version if he did not submit a review on that fragment', () => { + const { fragment } = testFixtures.fragments + const { inactiveReviewer } = testFixtures.users + + const result = ah.stripeFragmentByRole({ + fragment, + role: 'reviewer', + isLast: false, + user: inactiveReviewer, + }) + const { recommendations } = result + expect(recommendations).toHaveLength(0) + }) + it('author should not see recommendations if a decision has not been made', () => { const { fragment } = testFixtures.fragments fragment.recommendations = [