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