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