From 9e0cf2560ac8bac6ab131b28422876a284030acf Mon Sep 17 00:00:00 2001
From: Bogdan Cochior <bogdan.cochior@thinslices.com>
Date: Tue, 21 Aug 2018 14:04:40 +0300
Subject: [PATCH] feat(theme): add Reviewers Reports breakdown

---
 .../src/ManuscriptCard.js                     | 15 ++-
 .../src/ReviewerBreakdown.js                  | 67 ++++++++++++++
 .../src/ReviewerBreakdown.md                  | 92 +++++++++++++++++++
 packages/component-faraday-ui/src/index.js    |  3 +
 .../component-faraday-ui/src/styledHelpers.js |  4 +
 .../hindawi-theme/src/elements/Heading.js     |  6 ++
 6 files changed, 182 insertions(+), 5 deletions(-)
 create mode 100644 packages/component-faraday-ui/src/ReviewerBreakdown.js
 create mode 100644 packages/component-faraday-ui/src/ReviewerBreakdown.md

diff --git a/packages/component-faraday-ui/src/ManuscriptCard.js b/packages/component-faraday-ui/src/ManuscriptCard.js
index cc85137e4..9f0f816d0 100644
--- a/packages/component-faraday-ui/src/ManuscriptCard.js
+++ b/packages/component-faraday-ui/src/ManuscriptCard.js
@@ -6,11 +6,19 @@ import styled from 'styled-components'
 import { th } from '@pubsweet/ui-toolkit'
 import { compose, withHandlers, setDisplayName, withProps } from 'recompose'
 
-import { Tag, Text, Row, IconButton, AuthorTagList } from './'
+import {
+  Tag,
+  Text,
+  Row,
+  IconButton,
+  AuthorTagList,
+  ReviewerBreakdown,
+} from './'
 
 const ManuscriptCard = ({
   onCardClick,
   submitDate,
+  fragment,
   fragment: {
     authors = [],
     id: fragmentId,
@@ -46,10 +54,7 @@ const ManuscriptCard = ({
         <Text ml={1} mr={3}>
           {get(handlingEditor, 'name', 'Unassigned')}
         </Text>
-        <H4>Reviewer Reports</H4>
-        <Text ml={1} secondary>
-          0 invited
-        </Text>
+        <ReviewerBreakdown fragment={fragment} label="Reviewer Reports" />
       </Row>
     </MainContainer>
     <SideNavigation>
diff --git a/packages/component-faraday-ui/src/ReviewerBreakdown.js b/packages/component-faraday-ui/src/ReviewerBreakdown.js
new file mode 100644
index 000000000..5c0d5a9e5
--- /dev/null
+++ b/packages/component-faraday-ui/src/ReviewerBreakdown.js
@@ -0,0 +1,67 @@
+import React, { Fragment } from 'react'
+import { get } from 'lodash'
+import { H4 } from '@pubsweet/ui'
+import styled from 'styled-components'
+import { th } from '@pubsweet/ui-toolkit'
+import { compose, withHandlers, withProps } from 'recompose'
+
+import { Text } from './'
+
+const ReviewerBreakdown = ({ getReportBreakdown }) => getReportBreakdown()
+
+const roleFilter = role => i => i.role === role
+const submittedFilter = r => r.review && r.review.submittedOn
+const acceptedFilter = i => i.hasAnswer && i.isAccepted
+const declinedFilter = i => i.hasAnswer && !i.isAccepted
+const reviewerReduce = (acc, r) => ({
+  ...acc,
+  accepted: acceptedFilter(r) ? acc.accepted + 1 : acc.accepted,
+  declined: declinedFilter(r) ? acc.declined + 1 : acc.declined,
+  submitted: submittedFilter(r) ? acc.submitted + 1 : acc.submitted,
+})
+
+export default compose(
+  withProps(({ fragment }) => ({
+    invitations: get(fragment, 'invitations', []),
+    recommendations: get(fragment, 'recommendations', []),
+  })),
+  withHandlers({
+    getReportBreakdown: ({
+      invitations,
+      recommendations,
+      label = '',
+    }) => () => {
+      const reviewerInvitations = invitations.filter(roleFilter('reviewer'))
+      const invitationsWithRecommendations = reviewerInvitations.map(r => ({
+        ...r,
+        review: recommendations.find(rec => rec.userId === r.userId),
+      }))
+      const report = invitationsWithRecommendations.reduce(reviewerReduce, {
+        accepted: 0,
+        declined: 0,
+        submitted: 0,
+      })
+      return (
+        <Fragment>
+          {!!label && <Label>{label}</Label>}
+          {reviewerInvitations.length ? (
+            <Text secondary>
+              {`${reviewerInvitations.length} invited, ${
+                report.accepted
+              } agreed, ${report.declined} declined, ${
+                report.submitted
+              } submitted`}
+            </Text>
+          ) : (
+            <Text secondary> {`${reviewerInvitations.length} invited`}</Text>
+          )}
+        </Fragment>
+      )
+    },
+  }),
+)(ReviewerBreakdown)
+
+const Label = styled(H4)`
+  display: inline-block;
+  margin-right: ${th('gridUnit')};
+`
diff --git a/packages/component-faraday-ui/src/ReviewerBreakdown.md b/packages/component-faraday-ui/src/ReviewerBreakdown.md
new file mode 100644
index 000000000..d14eaaf2c
--- /dev/null
+++ b/packages/component-faraday-ui/src/ReviewerBreakdown.md
@@ -0,0 +1,92 @@
+Reviewer Breakdown Component
+
+```js
+const fragment = {
+  invitations: [
+    {
+      id: 'a69c1273-4073-4529-9e65-5424ad8034ea',
+      role: 'reviewer',
+      type: 'invitation',
+      userId: '9c79c3bf-ccba-4540-aad8-ce4609325826',
+      hasAnswer: false,
+      invitedOn: 1534506511008,
+      isAccepted: false,
+      respondedOn: null,
+    },
+    {
+      id: 'ca1c08bb-4da6-4cb4-972d-d16e3b66b884',
+      role: 'reviewer',
+      type: 'invitation',
+      userId: 'd3ab9a0b-d8e8-41e5-ab3b-72b13f84fba1',
+      hasAnswer: true,
+      invitedOn: 1534506542522,
+      isAccepted: true,
+      respondedOn: 1534506569565,
+    },
+  ],
+  recommendations: [
+    {
+      id: '87fb2c45-2685-47cc-9952-d58435ff8f3a',
+      userId: 'd3ab9a0b-d8e8-41e5-ab3b-72b13f84fba1',
+      comments: [
+        {
+          files: [],
+          public: true,
+          content: 'This is a great manuscript',
+        },
+      ],
+      createdOn: 1534506583815,
+      updatedOn: 1534506592527,
+      submittedOn: 1534506591684,
+      recommendation: 'publish',
+      recommendationType: 'review',
+    },
+    {
+      id: '9853453f-1049-4369-8543-1b812930430d',
+      userId: 'ede6770d-8dbf-4bf9-bbe2-98facfd0a114',
+      comments: [
+        {
+          public: true,
+          content: 'nice job',
+        },
+        {
+          public: false,
+          content: 'please publish this',
+        },
+      ],
+      createdOn: 1534506624583,
+      updatedOn: 1534506624583,
+      recommendation: 'publish',
+      recommendationType: 'editorRecommendation',
+    },
+    {
+      id: '64c5b596-fbfc-485c-9068-f3a58306efd7',
+      userId: '5b53da0d-3f88-4e94-b8f9-7eae6a754168',
+      createdOn: 1534506644873,
+      updatedOn: 1534506644873,
+      recommendation: 'publish',
+      recommendationType: 'editorRecommendation',
+    },
+    {
+      id: '3d43ff74-9a20-479d-a218-23bf8eac0b6a',
+      userId: '5b53da0d-3f88-4e94-b8f9-7eae6a754168',
+      createdOn: 1534506813446,
+      updatedOn: 1534506813446,
+      recommendation: 'publish',
+      recommendationType: 'editorRecommendation',
+    },
+  ],
+}
+;
+<ReviewerBreakdown fragment={fragment} />
+```
+
+Without invitations
+```js
+<ReviewerBreakdown fragment={{}} />
+```
+
+With label
+```js
+<ReviewerBreakdown fragment={{}} label='Reviewer Reports' />
+```
diff --git a/packages/component-faraday-ui/src/index.js b/packages/component-faraday-ui/src/index.js
index 0dd450ad6..f9c3b50f4 100644
--- a/packages/component-faraday-ui/src/index.js
+++ b/packages/component-faraday-ui/src/index.js
@@ -12,11 +12,14 @@ export { default as IconButton } from './IconButton'
 export { default as Label } from './Label'
 export { default as Logo } from './Logo'
 export { default as ManuscriptCard } from './ManuscriptCard'
+export { default as ReviewerBreakdown } from './ReviewerBreakdown'
 export { default as PersonInfo } from './PersonInfo'
 export { default as SortableList } from './SortableList'
 export { default as Tag } from './Tag'
 export { default as Text } from './Text'
 
+export * from './styledHelpers'
+
 // modals
 export * from './modals'
 export * from './gridItems'
diff --git a/packages/component-faraday-ui/src/styledHelpers.js b/packages/component-faraday-ui/src/styledHelpers.js
index 2bdd97529..5d1f75bbc 100644
--- a/packages/component-faraday-ui/src/styledHelpers.js
+++ b/packages/component-faraday-ui/src/styledHelpers.js
@@ -61,6 +61,10 @@ export const positionHelper = css`
     has(props, 'right') ? `${get(props, 'right')}px` : 'unset'};
 `
 
+export const displayHelper = css`
+  display: ${({ display }) => display || 'initial'};
+`
+
 export const radiusHelpers = props => {
   const borderTop = props.isFirst
     ? css`
diff --git a/packages/hindawi-theme/src/elements/Heading.js b/packages/hindawi-theme/src/elements/Heading.js
index ec5cf96c3..1d93c1651 100644
--- a/packages/hindawi-theme/src/elements/Heading.js
+++ b/packages/hindawi-theme/src/elements/Heading.js
@@ -1,17 +1,23 @@
 import { css } from 'styled-components'
 import { th } from '@pubsweet/ui-toolkit'
+import { marginHelper, displayHelper } from 'pubsweet-component-faraday-ui'
 
 export default {
   H1: css`
     color: ${th('heading.h1Color')};
+    ${marginHelper};
   `,
   H2: css`
     color: ${th('heading.h2Color')};
+    ${marginHelper};
   `,
   H3: css`
     color: ${th('heading.h3Color')};
+    ${marginHelper};
   `,
   H4: css`
     color: ${th('heading.h4Color')};
+    ${marginHelper};
+    ${displayHelper};
   `,
 }
-- 
GitLab