From 1a8f49243f6ad1f4baef219ccefbe7a984d8329f Mon Sep 17 00:00:00 2001
From: Bogdan Cochior <bogdan.cochior@thinslices.com>
Date: Wed, 9 May 2018 16:35:16 +0300
Subject: [PATCH] refactor(reports): integrate with API

---
 .../src/components/ReviewerReportForm.js      |  73 +++--------
 .../src/components/ReviewsAndReports.js       |  17 +--
 .../src/components/utils.js                   |  73 ++++++++++-
 .../src/redux/recommendations.js              | 120 ++++++++----------
 4 files changed, 142 insertions(+), 141 deletions(-)

diff --git a/packages/component-manuscript/src/components/ReviewerReportForm.js b/packages/component-manuscript/src/components/ReviewerReportForm.js
index a82a853ef..0f7056cc0 100644
--- a/packages/component-manuscript/src/components/ReviewerReportForm.js
+++ b/packages/component-manuscript/src/components/ReviewerReportForm.js
@@ -1,9 +1,8 @@
 import React, { Fragment } from 'react'
-import { debounce, isEqual } from 'lodash'
 import { connect } from 'react-redux'
 import { required } from 'xpub-validators'
 import styled, { css } from 'styled-components'
-import { th, Menu, ValidatedField, Icon, Button } from '@pubsweet/ui'
+import { th, Menu, ValidatedField, Icon, Button, ErrorText } from '@pubsweet/ui'
 import { compose, withHandlers, withProps } from 'recompose'
 import {
   reduxForm,
@@ -12,10 +11,7 @@ import {
   getFormValues,
 } from 'redux-form'
 import AutosaveIndicator from 'pubsweet-component-wizard/src/components/AutosaveIndicator'
-import {
-  autosaveRequest,
-  autosaveSuccess,
-} from 'pubsweet-component-wizard/src/redux/autosave'
+
 import {
   ConfirmationModal,
   withModal2,
@@ -25,10 +21,13 @@ import {
   selectFetching,
   createRecommendation,
   updateRecommendation,
-  getFragmentRecommendations,
 } from 'pubsweet-components-faraday/src/redux/recommendations'
 
-import { parseReviewResponseToForm, parseReviewRequest } from './utils'
+import {
+  parseReviewResponseToForm,
+  onReviewSubmit,
+  onReviewChange,
+} from './utils'
 
 const guidelinesLink =
   'https://about.hindawi.com/authors/peer-review-at-hindawi/'
@@ -51,28 +50,13 @@ const options = [
   },
 ]
 
-const onChange = (
-  values,
-  dispatch,
-  { project, version, createRecommendation, updateRecommendation },
-) => {
-  dispatch(autosaveRequest())
-  if (values.id) {
-    updateRecommendation(parseReviewRequest(values))
-      .then(console.log)
-      .then(dispatch(autosaveSuccess(new Date())))
-  } else {
-    createRecommendation(parseReviewRequest(values))
-      .then(console.log)
-      .then(dispatch(autosaveSuccess(new Date())))
-  }
-}
-
 const ReviewerReportForm = ({
+  error,
   isSubmitting,
   changeField,
   handleSubmit,
   formValues = {},
+  recommendations,
 }) => (
   <Root>
     <Row>
@@ -157,6 +141,11 @@ const ReviewerReportForm = ({
     )}
 
     <Spacing />
+    {error && (
+      <Row>
+        <ErrorText>{error}</ErrorText>
+      </Row>
+    )}
     <Row>
       <ActionButton onClick={handleSubmit}> Submit report </ActionButton>
       <AutosaveIndicator formName="reviewerReport" />
@@ -182,7 +171,6 @@ export default compose(
     {
       changeForm,
       getFormValues,
-      getFragmentRecommendations,
       createRecommendation,
       updateRecommendation,
     },
@@ -200,35 +188,10 @@ export default compose(
   }),
   reduxForm({
     form: 'reviewerReport',
-    onChange: debounce(onChange, 1000, { maxWait: 5000 }),
-    onSubmit: (
-      values,
-      dispatch,
-      { isSubmitting, showModal, hideModal, project, version },
-    ) => {
-      showModal({
-        title: 'Ready to Submit your Report?',
-        subtitle: 'Once submitted, the report can`t be modified',
-        confirmText: 'Submit report',
-        onConfirm: () => {
-          dispatch(autosaveRequest())
-          updateRecommendation(parseReviewRequest(values))
-            .then(console.log)
-            .then(hideModal)
-            .then(dispatch(autosaveSuccess(new Date())))
-          // sleep(1000)
-          //   .then(hideModal)
-          //   .then(() => {
-          //     // TODO: link to backend
-          //     const review = parseReviewRequest(values)
-          //     window.alert(
-          //       `You submitted:\n\n${JSON.stringify(review, null, 2)}`,
-          //     )
-          //   })
-        },
-        onCancel: hideModal,
-      })
-    },
+    enableReinitialize: true,
+    keepDirtyOnReinitialize: true,
+    onChange: onReviewChange,
+    onSubmit: onReviewSubmit,
   }),
 )(ReviewerReportForm)
 
diff --git a/packages/component-manuscript/src/components/ReviewsAndReports.js b/packages/component-manuscript/src/components/ReviewsAndReports.js
index dc1ff11fc..8e27da044 100644
--- a/packages/component-manuscript/src/components/ReviewsAndReports.js
+++ b/packages/component-manuscript/src/components/ReviewsAndReports.js
@@ -12,10 +12,7 @@ import {
   getCollectionReviewers,
   currentUserIsReviewer,
 } from 'pubsweet-components-faraday/src/redux/reviewers'
-import {
-  getFragmentRecommendations,
-  selectRecommendations,
-} from 'pubsweet-components-faraday/src/redux/recommendations'
+import { selectRecommendations } from 'pubsweet-components-faraday/src/redux/recommendations'
 
 import Tabs from '../molecules/Tabs'
 import Expandable from '../molecules/Expandable'
@@ -82,25 +79,17 @@ export default compose(
       fetchingReviewers: selectFetchingReviewers(state),
       isReviewer: currentUserIsReviewer(state, project.id),
     }),
-    { getCollectionReviewers, getFragmentRecommendations },
+    { getCollectionReviewers },
   ),
   withHandlers({
     getReviewers: ({ project, getCollectionReviewers }) => () => {
       getCollectionReviewers(project.id)
     },
-    getRecommendations: ({
-      project,
-      version,
-      getFragmentRecommendations,
-    }) => () => {
-      getFragmentRecommendations(project.id, version.id)
-    },
   }),
   lifecycle({
     componentDidMount() {
-      const { getReviewers, getRecommendations } = this.props
+      const { getReviewers } = this.props
       getReviewers()
-      getRecommendations()
     },
   }),
 )(ReviewsAndReports)
diff --git a/packages/component-manuscript/src/components/utils.js b/packages/component-manuscript/src/components/utils.js
index 17ad97ee8..d6411a9a3 100644
--- a/packages/component-manuscript/src/components/utils.js
+++ b/packages/component-manuscript/src/components/utils.js
@@ -1,5 +1,10 @@
 import moment from 'moment'
-import { get, find, capitalize, omit } from 'lodash'
+import { get, find, capitalize, omit, isEmpty, isEqual, debounce } from 'lodash'
+
+import {
+  autosaveRequest,
+  autosaveSuccess,
+} from 'pubsweet-component-wizard/src/redux/autosave'
 
 export const parseTitle = version => {
   const title = get(version, 'metadata.title')
@@ -82,6 +87,7 @@ export const redirectToError = redirectFn => err => {
 }
 
 export const parseReviewResponseToForm = (review = {}) => {
+  if (isEmpty(review)) return null
   const comments = review.comments || []
   const publicComment = comments.find(c => c.public)
   const privateComment = comments.find(c => !c.public)
@@ -95,10 +101,11 @@ export const parseReviewResponseToForm = (review = {}) => {
 }
 
 export const parseReviewRequest = (review = {}) => {
+  if (isEmpty(review)) return null
   const comments = [
     {
       public: true,
-      content: review.public,
+      content: review.public || '',
       files: review.files || [],
     },
   ]
@@ -111,7 +118,67 @@ export const parseReviewRequest = (review = {}) => {
     })
   }
   return {
-    ...omit(review, ['public', 'confidential', 'hasConfidential', 'files']),
+    ...omit(review, [
+      'public',
+      'confidential',
+      'hasConfidential',
+      'files',
+      'userId',
+    ]),
+    recommendationType: 'review',
     comments,
   }
 }
+
+const onChange = (
+  values,
+  dispatch,
+  { project, version, createRecommendation, updateRecommendation },
+  previousValues,
+) => {
+  const newValues = parseReviewRequest(values)
+  const prevValues = parseReviewRequest(previousValues)
+
+  if (!isEqual(newValues, prevValues) && !isEmpty(prevValues)) {
+    dispatch(autosaveRequest())
+    if (newValues.id) {
+      updateRecommendation(project.id, version.id, newValues).then(r =>
+        dispatch(autosaveSuccess(r.updatedOn)),
+      )
+    } else {
+      createRecommendation(project.id, version.id, newValues).then(r =>
+        dispatch(autosaveSuccess(r.updatedOn)),
+      )
+    }
+  }
+}
+
+export const onReviewChange = debounce(onChange, 1000, { maxWait: 5000 })
+
+export const onReviewSubmit = (
+  values,
+  dispatch,
+  {
+    isSubmitting,
+    showModal,
+    hideModal,
+    project,
+    version,
+    updateRecommendation,
+  },
+) => {
+  showModal({
+    title: 'Ready to Submit your Report?',
+    subtitle: 'Once submitted, the report can`t be modified',
+    confirmText: 'Submit report',
+    onConfirm: () => {
+      const newValues = parseReviewRequest(values)
+      newValues.submittedOn = Date.now()
+      dispatch(autosaveRequest())
+      updateRecommendation(project.id, version.id, newValues)
+        .then(r => dispatch(autosaveSuccess(r.updatedOn)))
+        .then(hideModal)
+    },
+    onCancel: hideModal,
+  })
+}
diff --git a/packages/components-faraday/src/redux/recommendations.js b/packages/components-faraday/src/redux/recommendations.js
index 2754da667..381cceb15 100644
--- a/packages/components-faraday/src/redux/recommendations.js
+++ b/packages/components-faraday/src/redux/recommendations.js
@@ -1,16 +1,11 @@
 import { get } from 'lodash'
-import {
-  get as apiGet,
-  create,
-  remove,
-  update,
-} from 'pubsweet-client/src/helpers/api'
+import { SubmissionError } from 'redux-form'
+import { create, update } from 'pubsweet-client/src/helpers/api'
 
 const REQUEST = 'recommendations/REQUEST'
 const ERROR = 'recommendations/ERROR'
 
 const GET_RECOMMENDATIONS_SUCCESS = 'recommendations/GET_SUCCESS'
-const GET_RECOMMENDATION_SUCCESS = 'recommendations/GET_ITEM_SUCCESS'
 const CREATE_RECOMMENDATION_SUCCESS = 'recommendations/CREATE_SUCCESS'
 const UPDATE_RECOMMENDATION_SUCCESS = 'recommendations/UPDATE_SUCCESS'
 
@@ -28,11 +23,6 @@ export const getRecommendationsSuccess = recommendations => ({
   payload: { recommendations },
 })
 
-export const getRecommendationSuccess = recommendation => ({
-  type: GET_RECOMMENDATION_SUCCESS,
-  payload: { recommendation },
-})
-
 export const createRecommendationSuccess = recommendation => ({
   type: CREATE_RECOMMENDATION_SUCCESS,
   payload: { recommendation },
@@ -50,58 +40,61 @@ export const selectError = state => get(state, 'recommendations.error')
 export const selectRecommendations = state =>
   get(state, 'recommendations.recommendations') || []
 
-const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
-
 // Actions
-export const getFragmentRecommendations = (
-  collectionId,
-  fragmentId,
+export const createRecommendation = (
+  collId,
+  fragId,
+  recommendation,
 ) => dispatch => {
   dispatch(recommendationsRequest())
-  const review = {
-    id: 'revuewiuuuid',
-    userId: 'uuuuuuid',
-    recommendation: 'publish',
-    recommendationType: 'review',
-    comments: [
-      {
-        content: 'Here is public text',
-        public: true,
-        files: [],
-      },
-      {
-        content: 'Here is PRIVATE text',
-        public: false,
-        files: [],
-      },
-    ],
-  }
-  return sleep(1000).then(() => {
-    dispatch(getRecommendationsSuccess([review]))
-    return [review]
-  })
-  // return apiGet(
-  //   `/collections/${collectionId}/fragments/${fragmentId}/recommendations`,
-  // ).then(
-  //   r => dispatch(getRecommendationsSuccess(r)),
-  //   err => dispatch(recommendationsError(err)),
-  // )
-}
-
-export const updateRecommendation = recommendation => dispatch => {
-  dispatch(recommendationsRequest())
-  return sleep(1000).then(() => {
-    dispatch(updateRecommendationSuccess(recommendation))
-    return recommendation
-  })
+  return create(
+    `/collections/${collId}/fragments/${fragId}/recommendations`,
+    recommendation,
+  ).then(
+    r => {
+      dispatch(getRecommendationsSuccess([r]))
+      return r
+    },
+    err => {
+      const error = get(err, 'response')
+      if (error) {
+        const errorMessage = get(JSON.parse(error), 'error')
+        dispatch(recommendationsError(errorMessage))
+        throw new SubmissionError({
+          _error: errorMessage || 'Something went wrong',
+        })
+      }
+    },
+  )
 }
 
-export const createRecommendation = recommendation => dispatch => {
+export const updateRecommendation = (
+  collId,
+  fragId,
+  recommendation,
+) => dispatch => {
   dispatch(recommendationsRequest())
-  return sleep(1000).then(() => {
-    dispatch(updateRecommendationSuccess(recommendation))
-    return recommendation
-  })
+  return update(
+    `/collections/${collId}/fragments/${fragId}/recommendations/${
+      recommendation.id
+    }`,
+    recommendation,
+  ).then(
+    r => {
+      dispatch(getRecommendationsSuccess([r]))
+      return r
+    },
+    err => {
+      const error = get(err, 'response')
+      if (error) {
+        const errorMessage = get(JSON.parse(error), 'error')
+        dispatch(recommendationsError(errorMessage))
+        throw new SubmissionError({
+          _error: errorMessage || 'Something went wrong',
+        })
+      }
+    },
+  )
 }
 
 // State
@@ -109,7 +102,6 @@ const initialState = {
   fetching: false,
   error: null,
   recommendations: [],
-  recommendation: {},
 }
 
 export default (state = initialState, action = {}) => {
@@ -118,8 +110,6 @@ export default (state = initialState, action = {}) => {
       return {
         ...state,
         fetching: true,
-        recommendations: [],
-        recommendation: {},
       }
     case ERROR:
       return {
@@ -134,13 +124,6 @@ export default (state = initialState, action = {}) => {
         error: null,
         recommendations: action.payload.recommendations,
       }
-    case GET_RECOMMENDATION_SUCCESS:
-      return {
-        ...state,
-        fetching: false,
-        error: null,
-        recommendation: action.payload.recommendation,
-      }
     case UPDATE_RECOMMENDATION_SUCCESS:
     case CREATE_RECOMMENDATION_SUCCESS:
       return {
@@ -148,7 +131,6 @@ export default (state = initialState, action = {}) => {
         fetching: false,
         error: null,
         recommendations: [action.payload.recommendation],
-        recommendation: action.payload.recommendation,
       }
     default:
       return state
-- 
GitLab