From 5581cfced5e1b0cd5d5b4388ba02021264c2d6d7 Mon Sep 17 00:00:00 2001 From: Alexandru Munteanu <alexandru.munt@gmail.com> Date: Tue, 29 May 2018 17:06:58 +0300 Subject: [PATCH] feat(manuscript-details): add editorial comments section --- .../src/components/EditorialComment.js | 120 ++++++++++++++++++ .../src/components/EditorialComments.js | 33 +++++ .../src/components/ManuscriptDetails.js | 1 - .../src/components/ManuscriptLayout.js | 10 ++ .../src/components/ManuscriptPage.js | 17 ++- .../src/components/ReviewsAndReports.js | 1 - .../src/components/index.js | 2 + .../src/molecules/Expandable.js | 9 +- .../src/redux/recommendations.js | 17 ++- 9 files changed, 201 insertions(+), 9 deletions(-) create mode 100644 packages/component-manuscript/src/components/EditorialComment.js create mode 100644 packages/component-manuscript/src/components/EditorialComments.js diff --git a/packages/component-manuscript/src/components/EditorialComment.js b/packages/component-manuscript/src/components/EditorialComment.js new file mode 100644 index 000000000..5376566d4 --- /dev/null +++ b/packages/component-manuscript/src/components/EditorialComment.js @@ -0,0 +1,120 @@ +import React from 'react' +import { sortBy } from 'lodash' +import { th } from '@pubsweet/ui' +import styled, { css } from 'styled-components' +import { setDisplayName, withProps, compose } from 'recompose' +import { DateParser } from 'pubsweet-components-faraday/src/components' + +const parseRecommendationType = t => { + switch (t) { + case 'reject': + return 'Rejection request' + case 'publish': + return 'Publish request' + default: + return 'Revision request' + } +} + +const parseRecommendationComments = (comments = []) => + sortBy(comments, c => !c.public).map(c => ({ + title: c.public ? 'Reason & Details' : 'Internal note', + content: c.content, + })) + +const EditorialComment = ({ + reason, + comments, + updatedOn, + handlingEditor: { name }, +}) => ( + <Root> + <Row> + <HeaderContainer> + <ReasonText>{reason}</ReasonText> + <CommentAuthor>{name}</CommentAuthor> + <RoleBadge>he</RoleBadge> + </HeaderContainer> + <DateParser timestamp={updatedOn}> + {t => <DefaultText>{t}</DefaultText>} + </DateParser> + </Row> + {comments.map(({ title, content }) => ( + <EditorContainer key={title}> + <ReasonText>{title}</ReasonText> + <DefaultText>{content}</DefaultText> + </EditorContainer> + ))} + </Root> +) + +export default compose( + setDisplayName('EditorialComment'), + withProps(({ recommendationType, comments = [] }) => ({ + reason: parseRecommendationType(recommendationType), + comments: parseRecommendationComments(comments), + })), +)(EditorialComment) + +// #region styled-components +const defaultText = css` + color: ${th('colorPrimary')}; + font-size: ${th('fontSizeBaseSmall')}; + font-family: ${th('fontReading')}; +` + +const DefaultText = styled.span` + ${defaultText}; +` + +const CommentAuthor = DefaultText.extend` + margin-left: calc(${th('subGridUnit')} * 2); + text-decoration: underline; +` + +const RoleBadge = styled.span` + align-items: center; + border: ${th('borderDefault')}; + color: ${th('colorPrimary')}; + display: flex; + font-family: ${th('fontInterface')}; + font-size: 9px; + font-weight: bold; + height: calc(${th('subGridUnit')} * 2); + margin-left: ${th('subGridUnit')}; + padding: calc(${th('subGridUnit')} / 3); + text-align: center; + text-transform: uppercase; +` + +const HeaderContainer = styled.div` + align-items: center; + display: flex; +` + +const ReasonText = DefaultText.extend` + font-family: ${th('fontHeading')}; + font-weight: bold; + text-transform: uppercase; +` + +const EditorContainer = styled.div` + align-items: flex-start; + display: flex; + flex-direction: column; +` + +const Row = styled.div` + align-items: center; + display: flex; + justify-content: space-between; +` + +const Root = styled.div` + background-color: ${th('colorBackground')}; + border: ${th('borderDefault')}; + margin: calc(${th('subGridUnit')} * 2) 0; + padding: calc(${th('subGridUnit')} * 2); + transition: height 0.3s; +` +// #endregion diff --git a/packages/component-manuscript/src/components/EditorialComments.js b/packages/component-manuscript/src/components/EditorialComments.js new file mode 100644 index 000000000..0f6dd4afe --- /dev/null +++ b/packages/component-manuscript/src/components/EditorialComments.js @@ -0,0 +1,33 @@ +import React from 'react' +import { get } from 'lodash' +import { th } from '@pubsweet/ui' +import styled from 'styled-components' +import { setDisplayName, withProps, compose } from 'recompose' + +import { Expandable } from '../molecules' +import { EditorialComment } from './' + +const EditorialComments = ({ recommendations, handlingEditor }) => ( + <Root> + <Expandable label="Editorial comments"> + {recommendations.map(r => ( + <EditorialComment key={r.id} {...r} handlingEditor={handlingEditor} /> + ))} + </Expandable> + </Root> +) + +export default compose( + setDisplayName('EditorialComments'), + withProps(({ project }) => ({ + handlingEditor: get(project, 'handlingEditor'), + })), +)(EditorialComments) + +// #region styled-components +const Root = styled.div` + background-color: ${th('colorBackground')}; + margin-top: calc(${th('subGridUnit')} * 2); + transition: height 0.3s; +` +// #endregion diff --git a/packages/component-manuscript/src/components/ManuscriptDetails.js b/packages/component-manuscript/src/components/ManuscriptDetails.js index 291f0ff94..df5e50181 100644 --- a/packages/component-manuscript/src/components/ManuscriptDetails.js +++ b/packages/component-manuscript/src/components/ManuscriptDetails.js @@ -47,7 +47,6 @@ const Text = styled.span` const Root = styled.div` background-color: ${th('colorBackground')}; - border: ${th('borderDefault')}; margin-top: calc(${th('subGridUnit')} * 2); transition: height 0.3s; ` diff --git a/packages/component-manuscript/src/components/ManuscriptLayout.js b/packages/component-manuscript/src/components/ManuscriptLayout.js index ffaa8a435..4b2ced19b 100644 --- a/packages/component-manuscript/src/components/ManuscriptLayout.js +++ b/packages/component-manuscript/src/components/ManuscriptLayout.js @@ -18,6 +18,7 @@ import { ReviewsAndReports, ManuscriptDetails, ManuscriptVersion, + EditorialComments, } from './' const ManuscriptLayout = ({ @@ -27,6 +28,8 @@ const ManuscriptLayout = ({ currentUserIs, editorInChief, updateManuscript, + canSeeEditorialComments, + editorialRecommendations, project = {}, version = {}, }) => ( @@ -57,6 +60,13 @@ const ManuscriptLayout = ({ project={project} version={version} /> + {canSeeEditorialComments && + editorialRecommendations.length > 0 && ( + <EditorialComments + project={project} + recommendations={editorialRecommendations} + /> + )} </Container> <SideBar flex={1}> <SideBarActions diff --git a/packages/component-manuscript/src/components/ManuscriptPage.js b/packages/component-manuscript/src/components/ManuscriptPage.js index 2c966176c..528eb7afe 100644 --- a/packages/component-manuscript/src/components/ManuscriptPage.js +++ b/packages/component-manuscript/src/components/ManuscriptPage.js @@ -11,9 +11,17 @@ import { selectCurrentUser, } from 'xpub-selectors' import { get as apiGet } from 'pubsweet-client/src/helpers/api' -import { compose, lifecycle, withHandlers, withState } from 'recompose' +import { + compose, + lifecycle, + withState, + withProps, + withHandlers, + setDisplayName, +} from 'recompose' import { getSignedUrl } from 'pubsweet-components-faraday/src/redux/files' import { reviewerDecision } from 'pubsweet-components-faraday/src/redux/reviewers' +import { selectEditorialRecommendations } from 'pubsweet-components-faraday/src/redux/recommendations' import { getHandlingEditors, selectHandlingEditors, @@ -23,6 +31,7 @@ import ManuscriptLayout from './ManuscriptLayout' import { parseSearchParams, redirectToError } from './utils' export default compose( + setDisplayName('ManuscriptPage'), withJournal, withRouter, withState('editorInChief', 'setEiC', 'N/A'), @@ -39,6 +48,7 @@ export default compose( handlingEditors: selectHandlingEditors(state), version: selectFragment(state, match.params.version), project: selectCollection(state, match.params.project), + editorialRecommendations: selectEditorialRecommendations(state), }), { replace, @@ -111,4 +121,9 @@ export default compose( ) }, }), + withProps(({ project }) => ({ + canSeeEditorialComments: ['revisionRequested', 'pendingApproval'].includes( + get(project, 'status'), + ), + })), )(ManuscriptLayout) diff --git a/packages/component-manuscript/src/components/ReviewsAndReports.js b/packages/component-manuscript/src/components/ReviewsAndReports.js index 83f781ca1..a5ccd94ac 100644 --- a/packages/component-manuscript/src/components/ReviewsAndReports.js +++ b/packages/component-manuscript/src/components/ReviewsAndReports.js @@ -137,7 +137,6 @@ export default compose( // #region styled-components const Root = styled.div` background-color: ${th('colorBackground')}; - border: ${th('borderDefault')}; margin-top: calc(${th('subGridUnit')} * 2); transition: height 0.3s; ` diff --git a/packages/component-manuscript/src/components/index.js b/packages/component-manuscript/src/components/index.js index 1171be979..e1d35c49c 100644 --- a/packages/component-manuscript/src/components/index.js +++ b/packages/component-manuscript/src/components/index.js @@ -5,10 +5,12 @@ export { default as MakeDecision } from './MakeDecision' export { default as SideBarRoles } from './SideBarRoles' export { default as ManuscriptPage } from './ManuscriptPage' export { default as SideBarActions } from './SideBarActions' +export { default as EditorialComment } from './EditorialComment' export { default as ReviewReportCard } from './ReviewReportCard' export { default as ManuscriptHeader } from './ManuscriptHeader' export { default as ManuscriptLayout } from './ManuscriptLayout' export { default as ManuscriptDetails } from './ManuscriptDetails' export { default as ManuscriptVersion } from './ManuscriptVersion' export { default as ReviewsAndReports } from './ReviewsAndReports' +export { default as EditorialComments } from './EditorialComments' export { default as ReviewerReportForm } from './ReviewerReportForm' diff --git a/packages/component-manuscript/src/molecules/Expandable.js b/packages/component-manuscript/src/molecules/Expandable.js index b12ab8fbc..a28b668eb 100644 --- a/packages/component-manuscript/src/molecules/Expandable.js +++ b/packages/component-manuscript/src/molecules/Expandable.js @@ -48,25 +48,28 @@ const SectionLabel = styled.span` const ChildrenContainer = styled.div` cursor: default; - padding: calc(${th('subGridUnit')} * 3); + padding: calc(${th('subGridUnit')} * 2); padding-top: 0; ` const Header = styled.div` align-items: center; - border-width: 0 0 ${th('borderWidth')} 0; + border-width: 0; + border-bottom-width: ${({ expanded }) => (expanded ? '1px' : 0)}; border-style: ${th('borderStyle')}; border-color: ${th('colorBorder')}; cursor: pointer; display: flex; justify-content: flex-start; - margin-bottom: calc(${th('subGridUnit')} * 3); + margin-bottom: ${({ expanded }) => (expanded ? th('subGridUnit') : 0)}; ` const Root = styled.div` + border: ${th('borderDefault')}; cursor: pointer; display: flex; flex-direction: column; + margin-top: ${th('subGridUnit')}; transition: all 0.3s; ` diff --git a/packages/components-faraday/src/redux/recommendations.js b/packages/components-faraday/src/redux/recommendations.js index 067386a83..3decec77f 100644 --- a/packages/components-faraday/src/redux/recommendations.js +++ b/packages/components-faraday/src/redux/recommendations.js @@ -1,6 +1,7 @@ import { get } from 'lodash' import { create, update } from 'pubsweet-client/src/helpers/api' +// #region Constants const REQUEST = 'recommendations/REQUEST' const ERROR = 'recommendations/ERROR' @@ -8,7 +9,9 @@ const GET_FRAGMENT_SUCCESS = 'GET_FRAGMENT_SUCCESS' const GET_RECOMMENDATIONS_SUCCESS = 'recommendations/GET_SUCCESS' const CREATE_RECOMMENDATION_SUCCESS = 'recommendations/CREATE_SUCCESS' const UPDATE_RECOMMENDATION_SUCCESS = 'recommendations/UPDATE_SUCCESS' +// #endregion +// #region Action Creators export const recommendationsRequest = () => ({ type: REQUEST, }) @@ -32,15 +35,21 @@ export const updateRecommendationSuccess = recommendation => ({ type: UPDATE_RECOMMENDATION_SUCCESS, payload: { recommendation }, }) +// #endregion -// Selectors +// #region Selectors export const selectFetching = state => get(state, 'recommendations.fetching') || false export const selectError = state => get(state, 'recommendations.error') export const selectRecommendations = state => get(state, 'recommendations.recommendations') || [] +export const selectEditorialRecommendations = state => + selectRecommendations(state).filter( + r => r.recommendationType === 'editorRecommendation', + ) +// #endregion -// Actions +// #region Actions export const createRecommendation = ( collId, fragId, @@ -91,8 +100,9 @@ export const updateRecommendation = ( }, ) } +// #endregion -// State +// #region State const initialState = { fetching: false, error: null, @@ -138,3 +148,4 @@ export default (state = initialState, action = {}) => { return state } } +// #endregion -- GitLab