diff --git a/packages/component-faraday-selectors/src/index.js b/packages/component-faraday-selectors/src/index.js index e852fc3b043265b83ce87eabfd80fbd263b22a87..24418c030161f1921489d5bde7afb51ac31ef831 100644 --- a/packages/component-faraday-selectors/src/index.js +++ b/packages/component-faraday-selectors/src/index.js @@ -88,7 +88,7 @@ export const canOverrideTechnicalChecks = (state, collection) => { return canOverrideTechnicalChecksStatuses.includes(status) } -export const canSeeReviewersReports = (state, collectionId) => { +export const canViewReports = (state, collectionId) => { const isHE = isHEToManuscript(state, collectionId) const isEiC = currentUserIs(state, 'adminEiC') return isHE || isEiC diff --git a/packages/component-faraday-ui/src/EditorialReportCard.js b/packages/component-faraday-ui/src/EditorialReportCard.js new file mode 100644 index 0000000000000000000000000000000000000000..d23b06d23b4561eec995de328a4afd99b2175a09 --- /dev/null +++ b/packages/component-faraday-ui/src/EditorialReportCard.js @@ -0,0 +1,100 @@ +import React, { Fragment } from 'react' +import { get } from 'lodash' +import { withProps, withHandlers, compose } from 'recompose' +import styled from 'styled-components' +import { th } from '@pubsweet/ui-toolkit' +import { DateParser } from '@pubsweet/ui' + +import { Label, Item, Row, Text, Tag } from './' +import { getReportComments } from './helpers' + +const EditorialReportCard = ({ + journal, + publicReport, + privateReport, + recommendation, + reviewerName, + reviewerRole, + report: { submittedOn, reviewer }, +}) => ( + <Root> + <Row justify="space-between" mb={2}> + <Item vertical> + <Label mb={1 / 2}>Decision</Label> + <Text>{recommendation}</Text> + </Item> + + <Item justify="flex-end"> + {reviewer && ( + <Fragment> + <Text mr={1 / 2}>{reviewerName}</Text> + <Tag mr={2}>{reviewerRole}</Tag> + </Fragment> + )} + <DateParser timestamp={submittedOn}> + {date => <Text>{date}</Text>} + </DateParser> + </Item> + </Row> + + {publicReport && ( + <Row mb={2}> + <Item vertical> + <Label mb={1 / 2}>Message For Author</Label> + <Text>{publicReport}</Text> + </Item> + </Row> + )} + + {privateReport && ( + <Row mb={2}> + <Item vertical> + <Label mb={1 / 2}>Message For Editorial Team</Label> + <Text>{privateReport}</Text> + </Item> + </Row> + )} + </Root> +) + +export default compose( + withHandlers({ + getReviewerRole: ({ report }) => () => { + if (get(report, 'reviewer.handlingEditor')) { + return 'HE' + } + return get(report, 'reviewer.editorInChief') ? 'EiC' : '' + }, + getReviewerName: ({ report }) => () => + `${get(report, 'reviewer.firstName', '')} ${get( + report, + 'reviewer.lastName', + '', + )}`, + getRecommendationLabel: ({ + report, + journal: { recommendations = [] }, + }) => () => + get( + recommendations.find(r => r.value === report.recommendation), + 'label', + ), + }), + withProps( + ({ report, getReviewerRole, getReviewerName, getRecommendationLabel }) => ({ + recommendation: getRecommendationLabel(), + publicReport: getReportComments({ report, isPublic: true }), + privateReport: getReportComments({ report, isPublic: false }), + reviewerName: getReviewerName(), + reviewerRole: getReviewerRole(), + }), + ), +)(EditorialReportCard) + +// #region styles +const Root = styled.div` + box-shadow: ${th('boxShadow')}; + padding: calc(${th('gridUnit')} * 2); + margin: ${th('gridUnit')}; +` +// #endregion diff --git a/packages/component-faraday-ui/src/EditorialReportCard.md b/packages/component-faraday-ui/src/EditorialReportCard.md new file mode 100644 index 0000000000000000000000000000000000000000..26fd9dce55e7c917e5944b6baf617a3384c50407 --- /dev/null +++ b/packages/component-faraday-ui/src/EditorialReportCard.md @@ -0,0 +1,156 @@ +Editorial Report Card + +Card with message for the author + +```js +const report = { + id: '71effdc0-ccb1-4ea9-9422-dcc9f8713347', + userId: '9ac5b5b5-252c-4933-9e66-72ec7c644a5c', + comments: [ + { + files: [], + public: true, + content: 'Public report here. Message for the author.', + }, + ], + createdOn: 1538053564396, + updatedOn: 1538053600643, + submittedOn: 1538053600624, + recommendation: 'publish', + recommendationType: 'editorialRecommendation', + reviewer: { + id: '9ac5b5b5-252c-4933-9e66-72ec7c644a5c', + type: 'user', + lastName: 'John', + firstName: 'Doe', + editorInChief: true, + handlingEditor: true, + }, +} +const journal = { + recommendations: [ + { + value: 'publish', + label: 'Publish', + }, + { + value: 'major', + label: 'Major revision', + }, + { + value: 'minor', + label: 'Minor revision', + }, + { + value: 'reject', + label: 'Reject', + }, + ], +} +;<EditorialReportCard report={report} journal={journal} /> +``` + +Card with message for the editorial team + +```js +const report = { + id: '71effdc0-ccb1-4ea9-9422-dcc9f8713347', + userId: '9ac5b5b5-252c-4933-9e66-72ec7c644a5c', + comments: [ + { + files: [], + public: false, + content: 'Confidential note is here. Message for editorial team.', + }, + ], + createdOn: 1538053564396, + updatedOn: 1538053600643, + submittedOn: 1538053600624, + recommendation: 'publish', + recommendationType: 'editorialRecommendation', + reviewer: { + id: '9ac5b5b5-252c-4933-9e66-72ec7c644a5c', + type: 'user', + lastName: 'John', + firstName: 'Doe', + editorInChief: true, + handlingEditor: false, + }, +} +const journal = { + recommendations: [ + { + value: 'publish', + label: 'Publish', + }, + { + value: 'major', + label: 'Major revision', + }, + { + value: 'minor', + label: 'Minor revision', + }, + { + value: 'reject', + label: 'Reject', + }, + ], +} +;<EditorialReportCard report={report} journal={journal} /> +``` + +Card with message for the editorial team and for the author + +```js +const report = { + id: '71effdc0-ccb1-4ea9-9422-dcc9f8713347', + userId: '9ac5b5b5-252c-4933-9e66-72ec7c644a5c', + comments: [ + { + files: [], + public: false, + content: 'Confidential note is here. Message for editorial team.', + }, + { + files: [], + public: true, + content: 'Public report here. Message for the author.', + }, + ], + createdOn: 1538053564396, + updatedOn: 1538053600643, + submittedOn: 1538053600624, + recommendation: 'publish', + recommendationType: 'editorialRecommendation', + reviewer: { + id: '9ac5b5b5-252c-4933-9e66-72ec7c644a5c', + type: 'user', + lastName: 'John', + firstName: 'Doe', + editorInChief: false, + handlingEditor: true, + }, +} +const journal = { + recommendations: [ + { + value: 'publish', + label: 'Publish', + }, + { + value: 'major', + label: 'Major revision', + }, + { + value: 'minor', + label: 'Minor revision', + }, + { + value: 'reject', + label: 'Reject', + }, + ], +} +;<EditorialReportCard report={report} journal={journal} /> +``` diff --git a/packages/component-faraday-ui/src/ManuscriptCard.js b/packages/component-faraday-ui/src/ManuscriptCard.js index 4c71c77f4190a961d472b0c9f96e921933ff5fd2..16cace406648327a367dee185f54b1981d4d5b56 100644 --- a/packages/component-faraday-ui/src/ManuscriptCard.js +++ b/packages/component-faraday-ui/src/ManuscriptCard.js @@ -25,7 +25,7 @@ const ManuscriptCard = ({ onDelete, canDelete, onCardClick, - canSeeReviewersReports, + canViewReports, fragment = {}, manuscriptType = {}, collection: { visibleStatus = 'Draft', handlingEditor, customId, id: collId }, @@ -77,7 +77,7 @@ const ManuscriptCard = ({ <Text ml={1} mr={3} whiteSpace="nowrap"> {get(handlingEditor, 'name', 'Unassigned')} </Text> - {canSeeReviewersReports && ( + {canViewReports && ( <Fragment> <Label mr={1}>Reviewers Reports</Label> <ReviewerBreakdown fragment={fragment} label="Reviewer Reports" /> diff --git a/packages/component-faraday-ui/src/helpers/utils.js b/packages/component-faraday-ui/src/helpers/utils.js index f99bf88b7a695cb0e7891211f0471bcf93a52f8c..898fd3ebc62282763a1331fd359f9d8da85a732c 100644 --- a/packages/component-faraday-ui/src/helpers/utils.js +++ b/packages/component-faraday-ui/src/helpers/utils.js @@ -1,5 +1,12 @@ -import { get } from 'lodash' +import { get, chain } from 'lodash' export const handleError = fn => e => { fn(get(JSON.parse(e.response), 'error', 'Oops! Something went wrong!')) } + +export const getReportComments = ({ report, isPublic = false }) => + chain(report) + .get('comments', []) + .find(c => c.public === isPublic) + .get('content') + .value() diff --git a/packages/component-faraday-ui/src/index.js b/packages/component-faraday-ui/src/index.js index 88eb9e4be5129fb6851cb9f74ff01c2e35c24301..9c505e4d598f0482be74d0c8d9de774572327b8d 100644 --- a/packages/component-faraday-ui/src/index.js +++ b/packages/component-faraday-ui/src/index.js @@ -44,6 +44,7 @@ export { default as UserProfile } from './UserProfile' export { default as WizardAuthors } from './WizardAuthors' export { default as WizardFiles } from './WizardFiles' export { default as TextTooltip } from './TextTooltip' +export { default as EditorialReportCard } from './EditorialReportCard' export * from './OverrideAlert' export * from './manuscriptDetails' diff --git a/packages/component-manuscript/src/components/EditorialCommentCard.js b/packages/component-manuscript/src/components/EditorialCommentCard.js new file mode 100644 index 0000000000000000000000000000000000000000..aa9187971fa82db28b3a3896b7fafb78c4520622 --- /dev/null +++ b/packages/component-manuscript/src/components/EditorialCommentCard.js @@ -0,0 +1,17 @@ +import React from 'react' +import { + EditorialReportCard, + ContextualBox, + withFilePreview, + withFileDownload, +} from 'pubsweet-component-faraday-ui' + +const EditorialCommentCard = ({ journal, reports = [] }) => ( + <ContextualBox label="Editorial Comments" mb={2}> + {reports.map(report => ( + <EditorialReportCard journal={journal} key={report.id} report={report} /> + ))} + </ContextualBox> +) + +export default withFileDownload(withFilePreview(EditorialCommentCard)) diff --git a/packages/component-manuscript/src/components/ManuscriptLayout.js b/packages/component-manuscript/src/components/ManuscriptLayout.js index a13df8c3461d0c10f2571cb79d138772d45985c4..9694096258dcf514bfb8b5a8b148336b4d315346 100644 --- a/packages/component-manuscript/src/components/ManuscriptLayout.js +++ b/packages/component-manuscript/src/components/ManuscriptLayout.js @@ -3,6 +3,7 @@ import styled from 'styled-components' import { isEmpty, get, last } from 'lodash' import { Text, + paddingHelper, ReviewerDetails, ManuscriptHeader, ManuscriptAssignHE, @@ -10,11 +11,11 @@ import { ManuscriptDetailsTop, ManuscriptEicDecision, ResponseToInvitation, - paddingHelper, } from 'pubsweet-component-faraday-ui' import ReviewerReportCard from './ReviewReportCard' import ReviewerReportForm from './ReviewerReportForm' +import EditorialCommentCard from './EditorialCommentCard' const eicDecisions = [ { value: 'return-to-handling-editor', label: 'Return to Handling Editor' }, @@ -93,6 +94,14 @@ const ManuscriptLayout = ({ getSignedUrl={getSignedUrl} /> + {get(currentUser, 'permisions.canViewReports', true) && + !!editorialRecommendations.length && ( + <EditorialCommentCard + journal={journal} + reports={editorialRecommendations} + /> + )} + {submittedOwnRecommendation && ( <ReviewerReportCard getSignedUrl={getSignedUrl} @@ -150,7 +159,6 @@ const ManuscriptLayout = ({ {get(currentUser, 'permissions.canMakeDecision', false) && ( <ManuscriptEicDecision formValues={get(formValues, 'eicDecision')} - isFetching={isFetching.recommendationsFetching} messagesLabel={messagesLabel} mt={2} options={ diff --git a/packages/component-manuscript/src/components/ManuscriptPage.js b/packages/component-manuscript/src/components/ManuscriptPage.js index 7c2839e5966a45b40b6d210f8aef301e014403ea..a28b8024fce00361293d72b3dbcd0cacbc39d5ee 100644 --- a/packages/component-manuscript/src/components/ManuscriptPage.js +++ b/packages/component-manuscript/src/components/ManuscriptPage.js @@ -30,10 +30,15 @@ import { hasManuscriptFailure, clearCustomError, } from 'pubsweet-components-faraday/src/redux/errors' -import { selectEditorialRecommendations } from 'pubsweet-components-faraday/src/redux/recommendations' +import { + createRecommendation, + selectReviewRecommendations, + selectEditorialRecommendations, +} from 'pubsweet-components-faraday/src/redux/recommendations' import { getUserToken, currentUserIs, + canViewReports, canMakeRevision, canMakeDecision, canEditManuscript, @@ -61,11 +66,6 @@ import { selectHandlingEditors, handlingEditorDecision, } from '../redux/editors' -import { - createRecommendation, - recommendationsFetching, - selectReviewRecommendations, -} from '../redux/recommendations' export default compose( setDisplayName('ManuscriptPage'), @@ -144,6 +144,7 @@ export default compose( isInvitedToReview: !isUndefined(pendingReviewerInvitation), permissions: { canAssignHE: canAssignHE(state, match.params.project), + canViewReports: canViewReports(state, match.params.project), canInviteReviewers: canInviteReviewers(state, collection), canMakeRecommendation: !isUndefined(pendingOwnRecommendation), canMakeRevision: canMakeRevision(state, collection, fragment), @@ -154,7 +155,6 @@ export default compose( }, isFetching: { editorsFetching: selectFetching(state), - recommendationsFetching: recommendationsFetching(state), }, formValues: { eicDecision: getFormValues('eic-decision')(state), diff --git a/packages/component-manuscript/src/components/ReviewsAndReports.js b/packages/component-manuscript/src/components/ReviewsAndReports.js index 06eedd8921407c034aa768186a012ecf821d19dc..d2efda6e7b5bca2101fa9f7b0dfdbbb7e7a9c8c9 100644 --- a/packages/component-manuscript/src/components/ReviewsAndReports.js +++ b/packages/component-manuscript/src/components/ReviewsAndReports.js @@ -15,7 +15,7 @@ import { } from 'pubsweet-components-faraday/src/redux/reviewers' import { selectReviewRecommendations } from 'pubsweet-components-faraday/src/redux/recommendations' import { - canSeeReviewersReports, + canViewReports, currentUserIsAuthor, } from 'pubsweet-component-faraday-selectors' @@ -44,12 +44,12 @@ const ReviewsAndReports = ({ isReviewer, mappedReviewers, mappedRecommendations, - canSeeReviewersReports, + canViewReports, reviewerRecommendation, recommendations = [], }) => ( <Fragment> - {canSeeReviewersReports && ( + {canViewReports && ( <Root id="reviews-and-reports"> <Expandable label="Reviewers & Reports" @@ -108,7 +108,7 @@ export default compose( isReviewer: currentUserIsReviewer(state, version.id), isAuthor: currentUserIsAuthor(state, version.id), recommendations: selectReviewRecommendations(state, version.id), - canSeeReviewersReports: canSeeReviewersReports(state, project.id), + canViewReports: canViewReports(state, project.id), }), { getCollectionReviewers }, ), @@ -134,15 +134,15 @@ export default compose( })), lifecycle({ componentDidMount() { - const { getReviewers, canSeeReviewersReports } = this.props - canSeeReviewersReports && getReviewers() + const { getReviewers, canViewReports } = this.props + canViewReports && getReviewers() }, componentWillReceiveProps(nextProps) { - const { match, canSeeReviewersReports, getReviewers } = this.props + const { match, canViewReports, getReviewers } = this.props const version = get(match, 'params.version') const nextVersion = get(nextProps, 'match.params.version') if (version !== nextVersion) { - canSeeReviewersReports && getReviewers() + canViewReports && getReviewers() } }, }), diff --git a/packages/component-manuscript/src/redux/recommendations.js b/packages/component-manuscript/src/redux/recommendations.js index bb55354a57d734632a243ada5f91d7fcda7c0022..320a8bec80edccce574d6fcbb7b47cd6ca990272 100644 --- a/packages/component-manuscript/src/redux/recommendations.js +++ b/packages/component-manuscript/src/redux/recommendations.js @@ -18,8 +18,6 @@ export const selectReviewRecommendations = (state, fragmentId) => ), })) -export const recommendationsFetching = state => - get(state, 'recommendations', false) // #endregion // #region Actions diff --git a/packages/components-faraday/src/components/Dashboard/DashboardItems.js b/packages/components-faraday/src/components/Dashboard/DashboardItems.js index aad166bb291c64506465e89e1e1b306a578f427f..f40111b657f9d73f7da239ad2086c80daf5dd76d 100644 --- a/packages/components-faraday/src/components/Dashboard/DashboardItems.js +++ b/packages/components-faraday/src/components/Dashboard/DashboardItems.js @@ -7,26 +7,18 @@ import { th } from '@pubsweet/ui-toolkit' import { withRouter } from 'react-router-dom' import { compose, setDisplayName, withHandlers } from 'recompose' import { ManuscriptCard, Row } from 'pubsweet-component-faraday-ui' -import { canSeeReviewersReports } from 'pubsweet-component-faraday-selectors' +import { canViewReports } from 'pubsweet-component-faraday-selectors' import withVersion from './withVersion' const DashboardItem = compose( withVersion, connect((state, { collection }) => ({ - canSeeReviewersReports: canSeeReviewersReports( - state, - get(collection, 'id', ''), - ), + canViewReports: canViewReports(state, get(collection, 'id', '')), })), )(ManuscriptCard) -const DashboardItems = ({ - list, - onClick, - deleteProject, - canSeeReviewersReports, -}) => ( +const DashboardItems = ({ list, onClick, deleteProject, canViewReports }) => ( <Root> {!list.length ? ( <Row justify="center" mt={4}> diff --git a/packages/components-faraday/src/redux/recommendations.js b/packages/components-faraday/src/redux/recommendations.js index fb3d21fef4d517c04bfc5e811db9a13ef1965150..26e9ad321a8e2cf9434b9d23196ac25d5f2c69fc 100644 --- a/packages/components-faraday/src/redux/recommendations.js +++ b/packages/components-faraday/src/redux/recommendations.js @@ -5,13 +5,23 @@ import { create, update } from 'pubsweet-client/src/helpers/api' export const selectRecommendations = (state, fragmentId) => get(state, `fragments.${fragmentId}.recommendations`, []) export const selectEditorialRecommendations = (state, fragmentId) => - selectRecommendations(state, fragmentId).filter( - r => r.recommendationType === 'editorRecommendation' && r.comments, - ) + selectRecommendations(state, fragmentId) + .filter(r => r.recommendationType === 'editorRecommendation' && r.comments) + .map(r => ({ + ...r, + reviewer: get(state, 'users.users', []).find( + user => user.id === r.userId, + ), + })) export const selectReviewRecommendations = (state, fragmentId) => - selectRecommendations(state, fragmentId).filter( - r => r.recommendationType === 'review', - ) + selectRecommendations(state, fragmentId) + .filter(r => r.recommendationType === 'review') + .map(r => ({ + ...r, + reviewer: get(state, 'users.users', []).find( + user => user.id === r.userId, + ), + })) // #endregion // #region Actions