diff --git a/packages/component-email/src/routes/emails/notifications.js b/packages/component-email/src/routes/emails/notifications.js index 23fea64c842350cd283d72b55f58c1b86a920d95..e5783fd5e02ea2586a854a7836fb2096495c2fb0 100644 --- a/packages/component-email/src/routes/emails/notifications.js +++ b/packages/component-email/src/routes/emails/notifications.js @@ -11,7 +11,7 @@ const { getEmailCopy } = require('./emailCopy') module.exports = { async sendNotifications({ user, baseUrl, role, UserModel }) { const userHelper = new User({ UserModel }) - const { firstName, lastName } = await userHelper.getEditorInChief() + const { firstName, lastName } = await userHelper.getEditorsInChief() const eicName = `${firstName} ${lastName}` const email = new Email({ diff --git a/packages/component-faraday-selectors/src/index.js b/packages/component-faraday-selectors/src/index.js index 65af2dd10ea67925098d318bc9f5d7ff13223d28..6508a91a4921237406bd5b9e386e167f1e608977 100644 --- a/packages/component-faraday-selectors/src/index.js +++ b/packages/component-faraday-selectors/src/index.js @@ -7,10 +7,12 @@ export const isHEToManuscript = (state, collectionId) => { return get(collection, 'handlingEditor.id') === currentUserId } +const canMakeRecommendationStatuses = ['reviewCompleted', 'heAssigned'] export const canMakeRecommendation = (state, collection, fragment = {}) => { if (fragment.id !== last(collection.fragments)) return false const isHE = isHEToManuscript(state, collection.id) - return isHE && get(collection, 'status') === 'reviewCompleted' + const status = get(collection, 'status') + return isHE && canMakeRecommendationStatuses.includes(status) } export const currentUserIs = ({ currentUser: { user } }, role) => { @@ -22,6 +24,10 @@ export const currentUserIs = ({ currentUser: { user } }, role) => { return isHe case 'staff': return isAdmin || isEic || isHe + case 'isEiC': + return isEic + case 'isAdmin': + return isAdmin case 'adminEiC': return isAdmin || isEic default: @@ -60,15 +66,22 @@ export const getHERecommendation = (state, collectionId, fragmentId) => { ) } -const cantMakeDecisionStatuses = ['rejected', 'published', 'draft'] +const canMakeDecisionStatuses = ['submitted', 'pendingApproval'] export const canMakeDecision = (state, collection, fragment = {}) => { if (fragment.id !== last(collection.fragments)) return false const status = get(collection, 'status') - if (!status || cantMakeDecisionStatuses.includes(status)) return false - const isEIC = currentUserIs(state, 'adminEiC') - return isEIC && status + return isEIC && canMakeDecisionStatuses.includes(status) +} + +const canEditManuscriptStatuses = ['draft', 'technicalChecks'] +export const canEditManuscript = (state, collection, fragment = {}) => { + if (fragment.id !== last(collection.fragments)) return false + const status = get(collection, 'status') + + const isAdmin = currentUserIs(state, 'isAdmin') + return isAdmin && canEditManuscriptStatuses.includes(status) } export const canSeeReviewersReports = (state, collectionId) => { diff --git a/packages/component-helper-service/src/services/User.js b/packages/component-helper-service/src/services/User.js index 074414d59f43dcf979d912244249fce897b7d7b7..39d03896853ea00f5bb81425bdda1bb551f7af7f 100644 --- a/packages/component-helper-service/src/services/User.js +++ b/packages/component-helper-service/src/services/User.js @@ -41,12 +41,16 @@ class User { return newUser } - async getEditorInChief() { + async getEditorsInChief() { const { UserModel } = this const users = await UserModel.all() - const eic = users.find(user => user.editorInChief || user.admin) - return eic + const eics = users.filter(user => user.editorInChief) + if (eics.length === 0) { + throw new Error('No Editor in Chief has been found') + } + + return eics } async updateUserTeams({ userId, teamId }) { @@ -65,6 +69,12 @@ class User { return authors.filter(author => activeUsers.includes(author.id)) } + + async getEiCName() { + const editorsInChief = await this.getEditorsInChief() + const { firstName, lastName } = editorsInChief[0] + return `${firstName} ${lastName}` + } } module.exports = User diff --git a/packages/component-helper-service/src/services/email/Email.js b/packages/component-helper-service/src/services/email/Email.js index 8d5b688b6993b3ebb9df7ab25630b926aa7762c6..53436b212dd89df765028f90fc779d4f2e0045bf 100644 --- a/packages/component-helper-service/src/services/email/Email.js +++ b/packages/component-helper-service/src/services/email/Email.js @@ -1,6 +1,7 @@ const config = require('config') const helpers = require('./helpers') const SendEmail = require('@pubsweet/component-send-email') +const logger = require('@pubsweet/logger') class Email { constructor({ @@ -64,14 +65,20 @@ class Email { } sendEmail({ text, html }) { + const fromEmail = config.get('mailer.from') const mailData = { - from: config.get('mailer.from'), + from: fromEmail, to: this.toUser.email, subject: this.content.subject, text, html, } + logger.info( + `EMAIL: Sent email from ${fromEmail} to ${ + this.toUser.email + } with subject '${this.content.subject}'`, + ) SendEmail.send(mailData) } } diff --git a/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js b/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js index 44c00f6856cbb40b92f735a8744df41ce73532b7..90df1a7ce364a2445dd79238dd9f4837392be5c8 100644 --- a/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js +++ b/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js @@ -36,7 +36,8 @@ module.exports = { } ${submittingAuthor.lastName}` const userHelper = new User({ UserModel }) - const eic = await userHelper.getEditorInChief() + const eics = await userHelper.getEditorsInChief() + const eic = eics[0] const eicName = `${eic.firstName} ${eic.lastName}` const subjectBaseText = `${collection.customId}: Manuscript ` diff --git a/packages/component-invite/src/routes/fragmentsInvitations/emails/invitations.js b/packages/component-invite/src/routes/fragmentsInvitations/emails/invitations.js index 9dc87ef106e786fc431f3be81acdd054d62313cf..b66445efe0a40c6470d9e04f3485710b5e8432ac 100644 --- a/packages/component-invite/src/routes/fragmentsInvitations/emails/invitations.js +++ b/packages/component-invite/src/routes/fragmentsInvitations/emails/invitations.js @@ -91,7 +91,7 @@ module.exports = { unsubscribeLink: services.createUrl(baseUrl, unsubscribeSlug, { id: invitedUser.id, }), - authorsList, + authorsList: authorsList.join(', '), }, }) diff --git a/packages/component-invite/src/routes/fragmentsInvitations/emails/notifications.js b/packages/component-invite/src/routes/fragmentsInvitations/emails/notifications.js index 12cad6a9d5283e486784a5a08c037b1b4c23d669..9eeb709b629ee52e0ab63624c4a25019b1dc1a95 100644 --- a/packages/component-invite/src/routes/fragmentsInvitations/emails/notifications.js +++ b/packages/component-invite/src/routes/fragmentsInvitations/emails/notifications.js @@ -37,8 +37,7 @@ module.exports = { const handlingEditor = get(collection, 'handlingEditor') const userHelper = new User({ UserModel }) - const { firstName, lastName } = await userHelper.getEditorInChief() - const eicName = `${firstName} ${lastName}` + const eicName = await userHelper.getEiCName() const subjectBaseText = isCanceled ? `${collection.customId}: Reviewer ` : `${collection.customId}: Manuscript ` diff --git a/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js b/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js index b2d0f8e2614db48c467cfe5440c1c156332361ab..ee0f6f2918511fe268a35a1b9bca28c6e8d9e4c9 100644 --- a/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js +++ b/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js @@ -49,9 +49,7 @@ module.exports = { }) const userHelper = new User({ UserModel }) - const eic = await userHelper.getEditorInChief() - const eicName = `${eic.firstName} ${eic.lastName}` - + const eicName = await userHelper.getEiCName() if (isNewVersion) { sendHandlingEditorEmail({ email, @@ -68,6 +66,7 @@ module.exports = { email, baseUrl, titleText, + UserModel, fragmentHelper, subjectBaseText, }) @@ -75,8 +74,8 @@ module.exports = { if (isTechnicalCheck) { sendEQSEmail({ - eic, email, + eicName, baseUrl, collection, subjectBaseText, @@ -156,7 +155,13 @@ const sendReviewersEmail = async ({ }) } -const sendEQSEmail = ({ eic, email, baseUrl, collection, subjectBaseText }) => { +const sendEQSEmail = ({ + email, + eicName, + baseUrl, + collection, + subjectBaseText, +}) => { const emailType = 'eqs-manuscript-submitted' email.toUser = { @@ -165,7 +170,7 @@ const sendEQSEmail = ({ eic, email, baseUrl, collection, subjectBaseText }) => { } email.content.unsubscribeLink = baseUrl - email.content.signatureName = `${eic.firstName} ${eic.lastName}` + email.content.signatureName = eicName email.content.subject = `${subjectBaseText} Submitted` email.content.ctaLink = services.createUrl( baseUrl, diff --git a/packages/component-manuscript-manager/src/routes/fragments/post.js b/packages/component-manuscript-manager/src/routes/fragments/post.js index 6150932b318d711b2eed23d7db81b6588e2a0799..2ec4ab94af0427c9014609b1927f0db01e40a498 100644 --- a/packages/component-manuscript-manager/src/routes/fragments/post.js +++ b/packages/component-manuscript-manager/src/routes/fragments/post.js @@ -52,7 +52,7 @@ module.exports = models => async (req, res) => { }, } - await MTS.sendPackage(packageFragment) + await MTS.sendPackage({ fragment: packageFragment }) notifications.sendNotifications({ fragment, diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js index ef6033c77ff3828f90d71c843fddf047d3f1aa58..b3f2e877a4aad364f605e362495d370193328932 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js @@ -1,5 +1,5 @@ const config = require('config') -const { chain, get } = require('lodash') +const { chain, get, isEmpty } = require('lodash') const { User, @@ -47,12 +47,6 @@ module.exports = { }) const userHelper = new User({ UserModel }) - const { - email: eicEmail, - firstName, - lastName, - } = await userHelper.getEditorInChief() - const eicName = `${firstName} ${lastName}` let comments if (isEditorInChief) { @@ -65,11 +59,17 @@ module.exports = { comments = eicComments } - if (isEditorInChief || newRecommendation.recommendationType === 'review') { + const hasPeerReview = (collection = {}) => + !isEmpty(collection.handlingEditor) + + if ( + (isEditorInChief || newRecommendation.recommendationType === 'review') && + hasPeerReview(collection) + ) { // the request came from either the Editor in Chief or a reviewer, so the HE needs to be notified sendHandlingEditorEmail({ email, - eicName, + eicName: await userHelper.getEiCName(), baseUrl, comments, titleText, @@ -85,38 +85,40 @@ module.exports = { newRecommendation.recommendationType !== 'review' && newRecommendation.recommendation !== 'return-to-handling-editor' ) { - sendAuthorsEmail({ - email, - baseUrl, - titleText, - parsedFragment, - fragmentAuthors, - isEditorInChief, - subjectBaseText, - newRecommendation, - }) - - sendReviewersEmail({ - email, - baseUrl, - UserModel, - titleText, - fragmentHelper, - isEditorInChief, - subjectBaseText, - recommendation: newRecommendation.recommendation, - handlingEditorName: get(collection, 'handlingEditor.name', 'N/A'), - }) + if (isEditorInChief || collection.status === 'revisionRequested') { + sendAuthorsEmail({ + email, + baseUrl, + titleText, + parsedFragment, + fragmentAuthors, + isEditorInChief, + subjectBaseText, + newRecommendation, + }) + } + if (hasPeerReview(collection)) { + sendReviewersEmail({ + email, + baseUrl, + UserModel, + titleText, + fragmentHelper, + isEditorInChief, + subjectBaseText, + recommendation: newRecommendation.recommendation, + handlingEditorName: get(collection, 'handlingEditor.name', 'N/A'), + }) - sendEiCEmail({ - email, - baseUrl, - eicName, - eicEmail, - titleText, - subjectBaseText, - recommendation: newRecommendation, - }) + sendEiCsEmail({ + email, + baseUrl, + userHelper, + titleText, + subjectBaseText, + recommendation: newRecommendation, + }) + } } }, } @@ -201,7 +203,7 @@ const sendAuthorsEmail = async ({ email.content.subject = `${subjectBaseText} Published` } else { emailType = 'author-manuscript-rejected' - email.content.subject = `${subjectBaseText} Rejected` + email.content.subject = `${subjectBaseText} Decision` } authors = fragmentAuthors.activeAuthors.map(author => ({ @@ -328,25 +330,23 @@ const sendReviewersEmail = async ({ }, ) const { html, text } = email.getBody({ - body: { paragraph: reviewer.paragraph }, - hasLink: reviewer.hasLink, + body: { paragraph: reviewer.paragraph, hasLink: reviewer.hasLink }, }) email.sendEmail({ html, text }) }) } -const sendEiCEmail = ({ +const sendEiCsEmail = async ({ email, - eicName, - eicEmail, titleText, - recommendation, + userHelper, subjectBaseText, + recommendation: { recommendation, comments: recComments = [] }, }) => { let emailType email.content.subject = `${subjectBaseText} Recommendation` - switch (recommendation.recommendation) { + switch (recommendation) { case 'minor': case 'major': emailType = 'eic-request-revision-from-he' @@ -361,22 +361,29 @@ const sendEiCEmail = ({ throw new Error(`undefined recommendation: ${recommendation} `) } - email.toUser = { - email: eicEmail, - name: eicName, - } - - const privateNote = recommendation.comments.find(comm => comm.private) + const privateNote = recComments.find(comm => !comm.public) const content = get(privateNote, 'content') const comments = content ? `Note to Editor: "${content}"` : '' - const { html, text } = email.getBody({ - body: getEmailCopy({ + + const editors = (await userHelper.getEditorsInChief()).map(eic => ({ + ...eic, + ...getEmailCopy({ emailType, titleText, comments, }), + })) + + editors.forEach(eic => { + email.toUser = { + email: eic.email, + name: `${eic.firstName} ${eic.lastName}`, + } + const { html, text } = email.getBody({ + body: { paragraph: eic.paragraph, hasLink: eic.hasLink }, + }) + email.sendEmail({ html, text }) }) - email.sendEmail({ html, text }) } const getSubjectByRecommendation = recommendation => diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js index 61b5cc07ca41eac18b8478acf375d291640defd3..69135f088fdff18442320398441a8db3f40562d5 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js @@ -1,5 +1,6 @@ const uuid = require('uuid') -const { pick } = require('lodash') +const { pick, get } = require('lodash') +const config = require('config') const { services, @@ -7,6 +8,10 @@ const { Collection, } = require('pubsweet-component-helper-service') +const s3Config = get(config, 'pubsweet-component-aws-s3', {}) +const mtsConfig = get(config, 'mts-service', {}) +const MTSService = require('pubsweet-component-mts-package') + const notifications = require('./notifications/notifications') module.exports = models => async (req, res) => { @@ -58,17 +63,32 @@ module.exports = models => async (req, res) => { newRecommendation.recommendation = recommendation || undefined newRecommendation.comments = comments || undefined - const collectionHelper = new Collection({ collection }) - collectionHelper.updateStatusOnRecommendation({ - isEditorInChief, - recommendation, - }) + if (recommendationType === 'editorRecommendation') { + const collectionHelper = new Collection({ collection }) - if (!isEditorInChief && ['minor', 'major'].includes(recommendation)) { - fragment.revision = pick(fragment, ['authors', 'files', 'metadata']) - } + collectionHelper.updateStatusOnRecommendation({ + isEditorInChief, + recommendation, + }) + + if (!isEditorInChief && ['minor', 'major'].includes(recommendation)) { + fragment.revision = pick(fragment, ['authors', 'files', 'metadata']) + } + + if (isEditorInChief && recommendation === 'publish') { + const { journal, xmlParser, ftp } = mtsConfig + const MTS = new MTSService(journal, xmlParser, s3Config, ftp) + const packageFragment = { + ...fragment, + metadata: { + ...fragment.metadata, + customId: collection.customId, + }, + } + + await MTS.sendPackage({ fragment: packageFragment, isEQA: true }) + } - if (recommendationType === 'editorRecommendation') { notifications.sendNotifications({ fragment, collection, diff --git a/packages/component-manuscript-manager/src/routes/technicalChecks/notifications/notifications.js b/packages/component-manuscript-manager/src/routes/technicalChecks/notifications/notifications.js index 0da35cdfd11321ba4fbced245cd01a8e8a9ba22f..782cdbb0c0325048800a2f12096f013319fd8b8f 100644 --- a/packages/component-manuscript-manager/src/routes/technicalChecks/notifications/notifications.js +++ b/packages/component-manuscript-manager/src/routes/technicalChecks/notifications/notifications.js @@ -27,18 +27,9 @@ module.exports = { } ${submittingAuthor.lastName}` const userHelper = new User({ UserModel }) - const { - lastName, - firstName, - email: eicEmail, - } = await userHelper.getEditorInChief() const email = new Email({ type: 'user', - toUser: { - email: eicEmail, - name: `${firstName} ${lastName}`, - }, content: { subject: `${collection.customId}: Manuscript Passed Technical Checks`, signatureName: 'EQS Team', @@ -51,12 +42,23 @@ module.exports = { }, }) - const { html, text } = email.getBody({ - body: getEmailCopy({ - titleText, + const editors = (await userHelper.getEditorsInChief()).map(eic => ({ + ...eic, + ...getEmailCopy({ emailType: 'eqs-manuscript-accepted', + titleText, }), + })) + + editors.forEach(eic => { + email.toUser = { + email: eic.email, + name: `${eic.firstName} ${eic.lastName}`, + } + const { html, text } = email.getBody({ + body: { paragraph: eic.paragraph, hasLink: eic.hasLink }, + }) + email.sendEmail({ html, text }) }) - email.sendEmail({ html, text }) }, } diff --git a/packages/component-manuscript/src/components/ManuscriptLayout.js b/packages/component-manuscript/src/components/ManuscriptLayout.js index 7c68d8ef1d2db6d3f13592a8afaa2a4a6e0408a7..c0ed8a1f1635315f2e1f5aa3ea048fec535ab306 100644 --- a/packages/component-manuscript/src/components/ManuscriptLayout.js +++ b/packages/component-manuscript/src/components/ManuscriptLayout.js @@ -30,7 +30,6 @@ const ManuscriptLayout = ({ editorInChief, canMakeRevision, editorialRecommendations, - responseToReviewers, hasResponseToReviewers, project = {}, version = {}, diff --git a/packages/component-manuscript/src/components/SideBarActions.js b/packages/component-manuscript/src/components/SideBarActions.js index d5e8b16b033cf934842da48a3126b4fd686545d0..dbfc3d7cd8c6bcbd6f7ce6a70176913185514c9a 100644 --- a/packages/component-manuscript/src/components/SideBarActions.js +++ b/packages/component-manuscript/src/components/SideBarActions.js @@ -1,6 +1,6 @@ import React from 'react' -import { compose } from 'recompose' -import { Icon } from '@pubsweet/ui' +import { compose, withHandlers } from 'recompose' +import { Icon, Button } from '@pubsweet/ui' import { connect } from 'react-redux' import styled from 'styled-components' import { th } from '@pubsweet/ui-toolkit' @@ -11,21 +11,30 @@ import { Recommendation, } from 'pubsweet-components-faraday/src/components' -import { createRevision } from 'pubsweet-component-wizard/src/redux/conversion' - import { canMakeDecision, canMakeRecommendation, + canEditManuscript, } from 'pubsweet-component-faraday-selectors/src' const SideBarActions = ({ project, version, - createRevision, + goToEdit, canMakeDecision, + canEditManuscript, canMakeRecommendation, }) => ( <Root> + {canEditManuscript && ( + <Button + data-test="button-edit-manuscript" + onClick={goToEdit(project, version)} + primary + > + Edit + </Button> + )} {canMakeDecision && ( <Decision collectionId={project.id} @@ -55,15 +64,18 @@ const SideBarActions = ({ export default compose( withRouter, - connect( - (state, { project, version }) => ({ - canMakeDecision: canMakeDecision(state, project, version), - canMakeRecommendation: canMakeRecommendation(state, project, version), - }), - (dispatch, { project, version, history }) => ({ - createRevision: () => dispatch(createRevision(project, version, history)), - }), - ), + connect((state, { project, version }) => ({ + canMakeDecision: canMakeDecision(state, project, version), + canMakeRecommendation: canMakeRecommendation(state, project, version), + canEditManuscript: canEditManuscript(state, project, version), + })), + withHandlers({ + goToEdit: ({ history }) => (project, version) => () => { + history.push(`/projects/${project.id}/versions/${version.id}/submit`, { + editMode: true, + }) + }, + }), )(SideBarActions) // #region styled-components diff --git a/packages/component-manuscript/src/components/SubmitRevision.js b/packages/component-manuscript/src/components/SubmitRevision.js index a962668fb2bbb1537795e8057505a23526e109ad..afaf7697ebebc111d39a773e01ccf69e594c5e48 100644 --- a/packages/component-manuscript/src/components/SubmitRevision.js +++ b/packages/component-manuscript/src/components/SubmitRevision.js @@ -24,6 +24,7 @@ import { import { AuthorList, Files } from 'pubsweet-components-faraday/src/components' import { submitRevision } from 'pubsweet-component-wizard/src/redux/conversion' import AutosaveIndicator from 'pubsweet-component-wizard/src/components/AutosaveIndicator' +import { selectReviewRecommendations } from 'pubsweet-components-faraday/src/redux/recommendations' import { toClass, compose, @@ -60,6 +61,7 @@ const SubmitRevision = ({ removeFile, handleSubmit, responseFiles, + reviews = [], submitFailed, }) => ( <Root> @@ -108,39 +110,41 @@ const SubmitRevision = ({ /> </CustomValidatedField> </Expandable> - <Expandable label="RESPONSE TO REVIEWER COMMENTS" startExpanded> - <Title>Reply text*</Title> - <Row> - <FullWidth className="full-width"> - <ValidatedField - component={TextAreaField} - name="commentsToReviewers" - validate={ - isEmpty(get(formValues, 'files.responseToReviewers')) - ? [required] - : [] - } - /> - </FullWidth> - </Row> - <Row left> - {responseFiles.map(file => ( - <FileItem - compact - id={file.id} - key={file.id} - {...file} - removeFile={removeFile} - /> - ))} - </Row> - <FilePicker - allowedFileExtensions={['pdf', 'doc', 'docx']} - onUpload={addFile} - > - <ActionText left={12}>Upload file</ActionText> - </FilePicker> - </Expandable> + {!isEmpty(reviews) && ( + <Expandable label="RESPONSE TO REVIEWER COMMENTS" startExpanded> + <Title>Reply text*</Title> + <Row> + <FullWidth className="full-width"> + <ValidatedField + component={TextAreaField} + name="commentsToReviewers" + validate={ + isEmpty(get(formValues, 'files.responseToReviewers')) + ? [required] + : [] + } + /> + </FullWidth> + </Row> + <Row left> + {responseFiles.map(file => ( + <FileItem + compact + id={file.id} + key={file.id} + {...file} + removeFile={removeFile} + /> + ))} + </Row> + <FilePicker + allowedFileExtensions={['pdf', 'doc', 'docx']} + onUpload={addFile} + > + <ActionText left={12}>Upload file</ActionText> + </FilePicker> + </Expandable> + )} <SubmitContainer> {submitFailed && formError && <Error>There are some errors above.</Error>} @@ -169,8 +173,9 @@ export default compose( modalComponent: ConfirmationModal, })), connect( - state => ({ + (state, { version }) => ({ fileFetching: getRequestStatus(state), + reviews: selectReviewRecommendations(state, version.id), formValues: getFormValues('revision')(state), formError: getFormSyncErrors('revision')(state), }), diff --git a/packages/component-mts-package/readme.md b/packages/component-mts-package/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..45347c011043112bece9ecd1a88f0fe9bf233775 --- /dev/null +++ b/packages/component-mts-package/readme.md @@ -0,0 +1,45 @@ +## MTS service integration + +This component is running as a service to integrate current Hindawi Manuscript Tracking System for Editorial Quality Screening and for Editorial Quality Assurance. + +### Use-case + +As an Editor in Chief, I want the manuscript to go through Editorial Quality Screening before I assign a Handling Editor to it and start the review process. + +### Workflow + +Stage 1: When an article is submitted, Faraday needs to convert the submission to XML JATS format, and upload as a .zip file to a FTP server. + +Stage 2: When new .zip file has been detected on FTP server, then third party system MTS (Manuscript Tracking System - Hindawi) needs to consume package and populate DB records. + +Stage 3: When check is completed, then Faraday system needs to be updated to move status from Technical Checks to Submitted - which allows the Editor in Chief to assign a Handling Editor. + + + +### Configuration +| Params | Required | Description | +| ------------- |:-------------:| -----| +| journalConfig | Yes | Journal configuration in .xml file as: doctype, dtdVersion, journalTitle, articleType, journal email, journalIdPublisher, issn | +| xmlParserOptions | No | parsing config options used by xml-js library | +| s3Config | Yes | Access to AWS S3 Bucket where fragment files are saved | +| FTPConfig | Yes | FTP server connection credentials | + + +### Usage + +MTS service gathers all the fragment files, creates an .xml file in a specific JATS format out of fragment JSON, creates a .zip archive and sends it to a specific FTP server with a configurable package name (also a backup it's uploaded to AWS S3). + +.xml structure of Hindawi use-case can be checked `/tests/sample.xml` from generated json `/tests/sample.json`. + +#### Example + +```javascript +const fragment = {} //fragment json here +const { journalConfig, xmlParser, s3Config, ftpConfig } = config //import your config +const MTS = new MTSService(journalConfig, xmlParser, s3Config, ftpConfig) +MTS.sendPackage({ fragment }) +``` + +[GIFs Demo](https://gitlab.coko.foundation/xpub/xpub-faraday/wikis/mts-integration) + + diff --git a/packages/component-mts-package/src/MTS.js b/packages/component-mts-package/src/MTS.js index 0d52a235a9d63630cadd9b88cf5e036aced1e11c..a8a9421139c0beafbf844b974658b8e602822b17 100644 --- a/packages/component-mts-package/src/MTS.js +++ b/packages/component-mts-package/src/MTS.js @@ -216,15 +216,19 @@ class MTS { return this.convertToXML(this.composeJson(fragment)) } - sendPackage(fragment = {}) { + sendPackage({ fragment = {}, isEQA = false }) { const xmlFile = this.convertFragmentToXML(fragment) return PackageManager.createFilesPackage(this.s3Config)({ fragment, xmlFile, + isEQA, }).then(() => { - const manuscriptName = get(xmlFile, 'name', '').replace('.xml', '') - const filename = `${manuscriptName}.zip` + const packageName = get(xmlFile, 'name', '').replace('.xml', '') + const filename = isEQA + ? `ACCEPTED_${packageName}.zip` + : `${packageName}.zip` + return PackageManager.uploadFiles({ filename, s3Config: this.s3Config, diff --git a/packages/component-mts-package/src/PackageManager.js b/packages/component-mts-package/src/PackageManager.js index 43d9386c5037e78176ee3a41116fdaf071dfe366..967985f3a45c6170fbd6a4b5d2d627ebe8cac633 100644 --- a/packages/component-mts-package/src/PackageManager.js +++ b/packages/component-mts-package/src/PackageManager.js @@ -19,9 +19,12 @@ const createFilesPackage = (s3Config, archiver = nodeArchiver) => { const asyncGetObject = promisify(s3.getObject.bind(s3)) const asyncListObjects = promisify(s3.listObjects.bind(s3)) - return async ({ fragment, fileTypes, xmlFile }) => { + return async ({ fragment, fileTypes, xmlFile, isEQA = false }) => { const { id } = fragment - const manuscriptName = get(xmlFile, 'name', '').replace('.xml', '') + let packageName = get(xmlFile, 'name', '').replace('.xml', '') + if (isEQA) { + packageName = `ACCEPTED_${packageName}` + } try { const params = { Bucket: s3Config.bucket, @@ -39,7 +42,7 @@ const createFilesPackage = (s3Config, archiver = nodeArchiver) => { ) if (s3Files) { - const packageOutput = fs.createWriteStream(`${manuscriptName}.zip`) + const packageOutput = fs.createWriteStream(`${packageName}.zip`) const archive = archiver('zip') archive.pipe(packageOutput) @@ -53,6 +56,7 @@ const createFilesPackage = (s3Config, archiver = nodeArchiver) => { }) archive.on('error', err => { + logger.error(err) throw err }) archive.on('end', err => { diff --git a/packages/component-mts-package/tests/sample.json b/packages/component-mts-package/tests/sample.json index 21ccc5f10edeed4f9c776ff30bc1bb0fd58e6053..17f672cb1edf947a633dac1beb4818bb08662dbc 100644 --- a/packages/component-mts-package/tests/sample.json +++ b/packages/component-mts-package/tests/sample.json @@ -24,12 +24,12 @@ "_attributes": { "journal-id-type": "email" }, - "_text": "trashjo@ariessys.com" + "_text": "faraday@hindawi.com" } ], "journal-title-group": { "journal-title": { - "_text": "Research" + "_text": "Bioinorganic Chemistry and Applications" } }, "issn": [ @@ -37,7 +37,7 @@ "_attributes": { "pub-type": "ppub" }, - "_text": "0000-000Y" + "_text": "2474-7394" }, { "_attributes": { @@ -52,60 +52,28 @@ "_attributes": { "pub-id-type": "publisher-id" }, - "_text": "RESEARCH-D-18-00005" + "_text": "RESEARCH-F-3326913" }, { "_attributes": { "pub-id-type": "manuscript" }, - "_text": "RESEARCH-D-18-00005" + "_text": "RESEARCH-F-3326913" } ], "article-categories": { - "subj-group": [ - { - "_attributes": { - "subj-group-type": "Article Type" - }, - "subject": { - "_text": "Research Article" - } - }, - { - "_attributes": { - "subj-group-type": "Category" - }, - "subject": { - "_text": "Information science" - } - }, - { - "_attributes": { - "subj-group-type": "Classification" - }, - "subject": { - "_text": "Applied sciences and engineering" - } + "subj-group": { + "_attributes": { + "subj-group-type": "Article Type" }, - { - "_attributes": { - "subj-group-type": "Classification" - }, - "subject": { - "_text": "Scientific community" - } + "subject": { + "_text": "clinical-study" } - ] + } }, "title-group": { "article-title": { - "_text": "January 24 sample article with new email trigger in place" - }, - "alt-title": { - "_attributes": { - "alt-title-type": "running-head" - }, - "_text": "let's hope this works" + "_text": "Demo sprint 16 no fun" } }, "contrib-group": { @@ -121,17 +89,17 @@ }, "name": { "surname": { - "_text": "Heckner" + "_text": "Raluca" }, "given-names": { - "_text": "Hannah" + "_text": "Authorescu" }, "prefix": { - "_text": "Ms." + "_text": "miss" } }, "email": { - "_text": "hheckner@aaas.org" + "_text": "raluca.gramschi+auth@thinslices.com" }, "xref": { "_attributes": { @@ -144,8 +112,9 @@ "_attributes": { "id": "aff1" }, - "country": { - "_text": "UNITED STATES" + "country": {}, + "addr-line": { + "_text": "Technical University Gheorghe Asachi Iasi" } } }, @@ -155,10 +124,10 @@ "date-type": "received" }, "day": { - "_text": "24" + "_text": "3" }, "month": { - "_text": "01" + "_text": "8" }, "year": { "_text": "2018" @@ -166,78 +135,58 @@ } }, "abstract": { - "p": { - "_text": "Abstract\nThis article explains and illustrates the use of LATEX in preparing manuscripts for submission to the American Journal of Physics (AJP). While it is not a comprehensive reference, we hope it will suffice for the needs of most AJP authors." - } + "_text": "<p>some abstract here</p>" }, - "kwd-group": { - "kwd": [ - { - "_text": "manuscript" + "funding-group": {} + }, + "files": { + "file": [ + { + "item_type": { + "_text": "coverLetter" }, - { - "_text": "submissions" + "item_description": { + "_text": "sample cover letter_ms 1.doc" }, - { - "_text": "ftp site" - } - ] - }, - "funding-group": {}, - "counts": { - "fig-count": { - "_attributes": { - "count": "0" + "item_name": { + "_text": "sample cover letter_ms 1.doc" } - } - }, - "custom-meta-group": { - "custom-meta": [ - { - "meta-name": { - "_text": "Black and White Image Count" - }, - "meta-value": { - "_text": "0" - } + }, + { + "item_type": { + "_text": "manuscripts" }, - { - "meta-name": { - "_text": "Color Image Count" - }, - "meta-value": { - "_text": "0" - } + "item_description": { + "_text": "manuscript.pdf" + }, + "item_name": { + "_text": "manuscript.pdf" } - ] - } - } - }, - "body": { - "fig": [ - { - "label": { - "_text": "Figure 1" }, - "graphic": { - "_attributes": { - "xlink:href": "GasBulbData.eps", - "xmlns:xlink": "http://www.w3.org/1999/xlink" + { + "item_type": { + "_text": "supplementary" + }, + "item_description": { + "_text": "important-emails.md" + }, + "item_name": { + "_text": "important-emails.md" } - } - }, - { - "label": { - "_text": "Figure 2" }, - "graphic": { - "_attributes": { - "xlink:href": "ThreeSunsets.jpg", - "xmlns:xlink": "http://www.w3.org/1999/xlink" + { + "item_type": { + "_text": "supplementary" + }, + "item_description": { + "_text": "important-emails.md" + }, + "item_name": { + "_text": "important-emails (1).md" } } - } - ] + ] + } } } } \ No newline at end of file diff --git a/packages/component-mts-package/tests/sample.xml b/packages/component-mts-package/tests/sample.xml index 697e0e64c760dc9f1acdda45ff087c984303906e..d91397fb44989adaffbcc7d597b457138b71b714 100644 --- a/packages/component-mts-package/tests/sample.xml +++ b/packages/component-mts-package/tests/sample.xml @@ -4,89 +4,71 @@ <front> <journal-meta> <journal-id journal-id-type="publisher">research</journal-id> - <journal-id journal-id-type="email">trashjo@ariessys.com</journal-id> + <journal-id journal-id-type="email">faraday@hindawi.com</journal-id> <journal-title-group> - <journal-title>Research</journal-title> + <journal-title>Bioinorganic Chemistry and Applications</journal-title> </journal-title-group> - <issn pub-type="ppub">0000-000Y</issn> + <issn pub-type="ppub">2474-7394</issn> <issn pub-type="epub"></issn> </journal-meta> <article-meta> - <article-id pub-id-type="publisher-id">RESEARCH-D-18-00005</article-id> - <article-id pub-id-type="manuscript">RESEARCH-D-18-00005</article-id> + <article-id pub-id-type="publisher-id">RESEARCH-F-3326913</article-id> + <article-id pub-id-type="manuscript">RESEARCH-F-3326913</article-id> <article-categories> <subj-group subj-group-type="Article Type"> - <subject>Research Article</subject> - </subj-group> - <subj-group subj-group-type="Category"> - <subject>Information science</subject> - </subj-group> - <subj-group subj-group-type="Classification"> - <subject>Applied sciences and engineering</subject> - </subj-group> - <subj-group subj-group-type="Classification"> - <subject>Scientific community</subject> + <subject>clinical-study</subject> </subj-group> </article-categories> <title-group> - <article-title>January 24 sample article with new email trigger in place</article-title> - <alt-title alt-title-type="running-head">let's hope this works</alt-title> + <article-title>Demo sprint 16 no fun</article-title> </title-group> <contrib-group> <contrib contrib-type="author" corresp="yes"> - <role content-type="1" /> + <role content-type="1"></role> <name> - <surname>Heckner</surname> - <given-names>Hannah</given-names> - <prefix>Ms.</prefix> + <surname>Raluca</surname> + <given-names>Authorescu</given-names> + <prefix>miss</prefix> </name> - <email>hheckner@aaas.org</email> - <xref ref-type="aff" rid="aff1" /> + <email>raluca.gramschi+auth@thinslices.com</email> + <xref ref-type="aff" rid="aff1"></xref> </contrib> <aff id="aff1"> - <country>UNITED STATES</country> + <country></country> + <addr-line>Technical University Gheorghe Asachi Iasi</addr-line> </aff> </contrib-group> <history> <date date-type="received"> - <day>24</day> - <month>01</month> + <day>3</day> + <month>8</month> <year>2018</year> </date> </history> - <abstract> - <p>Abstract -This article explains and illustrates the use of LATEX in preparing manuscripts for submission to the American Journal of Physics (AJP). While it is not a comprehensive reference, we hope it will suffice for the needs of most AJP authors.</p> - </abstract> - <kwd-group> - <kwd>manuscript</kwd> - <kwd>submissions</kwd> - <kwd>ftp site</kwd> - </kwd-group> - <funding-group /> - <counts> - <fig-count count="0" /> - </counts> - <custom-meta-group> - <custom-meta> - <meta-name>Black and White Image Count</meta-name> - <meta-value>0</meta-value> - </custom-meta> - <custom-meta> - <meta-name>Color Image Count</meta-name> - <meta-value>0</meta-value> - </custom-meta> - </custom-meta-group> + <abstract><p>some abstract here</p></abstract> + <funding-group></funding-group> </article-meta> + <files> + <file> + <item_type>coverLetter</item_type> + <item_description>sample cover letter_ms 1.doc</item_description> + <item_name>sample cover letter_ms 1.doc</item_name> + </file> + <file> + <item_type>manuscripts</item_type> + <item_description>manuscript.pdf</item_description> + <item_name>manuscript.pdf</item_name> + </file> + <file> + <item_type>supplementary</item_type> + <item_description>important-emails.md</item_description> + <item_name>important-emails.md</item_name> + </file> + <file> + <item_type>supplementary</item_type> + <item_description>important-emails.md</item_description> + <item_name>important-emails (1).md</item_name> + </file> + </files> </front> - <body> - <fig> - <label>Figure 1</label> - <graphic xlink:href="GasBulbData.eps" xmlns:xlink="http://www.w3.org/1999/xlink" /> - </fig> - <fig> - <label>Figure 2</label> - <graphic xlink:href="ThreeSunsets.jpg" xmlns:xlink="http://www.w3.org/1999/xlink" /> - </fig> - </body> </article> \ No newline at end of file diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js b/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js index d6e87cc86947dee0dd26537455c9ed458a929500..68132122b47e8b66bed920a01a9afcacc16ba8c6 100644 --- a/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js +++ b/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js @@ -1,8 +1,8 @@ const getEmailCopy = ({ emailType, titleText }) => { let paragraph switch (emailType) { - case 'co-author-added-to-manuscript': - paragraph = `You have been added as co-author to ${titleText}. The manuscript will become visible on your dashboard once it's submitted. Please click on the link below to access your dashboard.` + case 'author-added-to-manuscript': + paragraph = `You have been added as an author to ${titleText}. The manuscript will become visible on your dashboard once it's submitted. Please click on the link below to access your dashboard.` break case 'new-author-added-to-manuscript': paragraph = `You have been added as an author to ${titleText}. In order to gain access to the manuscript, please confirm your account and set your account details by clicking on the link below.` diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js b/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js index 8ee396d5206c25e20e52c5145938a8242cbde576..9a7af8dbcd3819870c4d749ba3c85cd910db219b 100644 --- a/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js +++ b/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js @@ -9,17 +9,11 @@ const { services, Fragment, } = require('pubsweet-component-helper-service') + const { getEmailCopy } = require('./emailCopy') module.exports = { - async sendNotifications({ - user, - baseUrl, - isSubmitting, - fragment, - UserModel, - collection, - }) { + async sendNotifications({ user, baseUrl, fragment, UserModel, collection }) { const fragmentHelper = new Fragment({ fragment }) const { title } = await fragmentHelper.getFragmentData({ handlingEditor: collection.handlingEditor, @@ -33,8 +27,6 @@ module.exports = { } ${submittingAuthor.lastName}` const userHelper = new User({ UserModel }) - const { firstName, lastName } = await userHelper.getEditorInChief() - const eicName = `${firstName} ${lastName}` const subjectBaseText = `${collection.customId}: Manuscript` const email = new Email({ @@ -42,25 +34,31 @@ module.exports = { content: { ctaLink: baseUrl, ctaText: 'VIEW DASHBOARD', - signatureName: eicName, + signatureName: await userHelper.getEiCName(), }, }) - if (!isSubmitting) { - sendCoAuthorEmail({ email, baseUrl, user, titleText, subjectBaseText }) + if (!user.isConfirmed) { + sendNewAuthorEmail({ + email, + user, + baseUrl, + titleText, + subjectBaseText, + }) } - sendNewAuthorEmail({ + sendAddedToManuscriptEmail({ email, - user, baseUrl, + user, titleText, subjectBaseText, }) }, } -const sendCoAuthorEmail = ({ +const sendAddedToManuscriptEmail = ({ email, baseUrl, user, @@ -79,7 +77,7 @@ const sendCoAuthorEmail = ({ const { html, text } = email.getBody({ body: getEmailCopy({ - emailType: 'co-author-added-to-manuscript', + emailType: 'author-added-to-manuscript', titleText, }), }) @@ -87,13 +85,7 @@ const sendCoAuthorEmail = ({ email.sendEmail({ html, text }) } -const sendNewAuthorEmail = ({ - email, - baseUrl, - user, - titleText, - subjectBaseText, -}) => { +const sendNewAuthorEmail = ({ email, baseUrl, user, titleText }) => { email.toUser = { email: user.email, name: `${user.firstName} ${user.lastName}`, diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/post.js b/packages/component-user-manager/src/routes/fragmentsUsers/post.js index 0d88c22046537c5d3c63bf08c2ac69d7d855e595..6a7546e6f665ef1b7fb295e92c9b14a88ba8a1b8 100644 --- a/packages/component-user-manager/src/routes/fragmentsUsers/post.js +++ b/packages/component-user-manager/src/routes/fragmentsUsers/post.js @@ -92,6 +92,14 @@ module.exports = models => async (req, res) => { collection.save() } + notifications.sendNotifications({ + user, + baseUrl, + fragment, + collection, + UserModel: models.User, + }) + return res.status(200).json({ ...pick(user, authorKeys), isSubmitting, @@ -128,7 +136,6 @@ module.exports = models => async (req, res) => { collection, user: newUser, UserModel: models.User, - isSubmitting, }) if (!collection.owners.includes(newUser.id)) { diff --git a/packages/component-wizard/src/components/SubmissionWizard.js b/packages/component-wizard/src/components/SubmissionWizard.js index 162de41426cb1e4d6ab014aeac2c8e9f6c632031..76ea5eadbacef57a645c6a36255b53b9c7f1ea55 100644 --- a/packages/component-wizard/src/components/SubmissionWizard.js +++ b/packages/component-wizard/src/components/SubmissionWizard.js @@ -10,7 +10,13 @@ import { DragDropContext } from 'react-dnd' import { Icon, Button } from '@pubsweet/ui' import HTML5Backend from 'react-dnd-html5-backend' import { selectCollection, selectFragment } from 'xpub-selectors' -import { withStateHandlers, compose, toClass, withProps } from 'recompose' +import { + withStateHandlers, + compose, + toClass, + withProps, + withHandlers, +} from 'recompose' import { withModal, @@ -44,9 +50,11 @@ const NewWizard = ({ step, history, prevStep, + isEditMode, isLastStep, isFirstStep, handleSubmit, + getButtonText, journal: { manuscriptTypes = [] }, ...rest }) => ( @@ -76,7 +84,7 @@ const NewWizard = ({ >{`< BACK`}</Button> )} <Button data-test="submission-next" onClick={handleSubmit} primary> - {isLastStep ? `SUBMIT MANUSCRIPT` : `NEXT STEP >`} + {getButtonText()} </Button> </ButtonContainer> </Row> @@ -132,13 +140,22 @@ export default compose( }, ), withProps(setInitialValues), - withProps(({ formValues, formSyncErrors, submitFailed, step }) => ({ + withProps(({ formValues, formSyncErrors, submitFailed, step, location }) => ({ isFirstStep: step === 0, isLastStep: step === wizardSteps.length - 1, filesError: submitFailed && get(formSyncErrors, 'files', ''), authorsError: submitFailed && get(formSyncErrors, 'authors', ''), hasConflicts: get(formValues, 'conflicts.hasConflicts', 'no') === 'yes', + isEditMode: get(location, 'state.editMode', false), })), + withHandlers({ + getButtonText: ({ isLastStep, isEditMode }) => () => { + if (isEditMode && isLastStep) { + return 'SAVE CHANGES' + } + return isLastStep ? `SUBMIT MANUSCRIPT` : `NEXT STEP >` + }, + }), withModal(() => ({ modalComponent: ModalWrapper, })), diff --git a/packages/component-wizard/src/components/utils.js b/packages/component-wizard/src/components/utils.js index eb3f2317884c860d5524a81a1e28fc7d9566fb1b..eaef4c5aa092b554fae967b24d7755f0f5665ebc 100644 --- a/packages/component-wizard/src/components/utils.js +++ b/packages/component-wizard/src/components/utils.js @@ -82,6 +82,7 @@ export const onSubmit = ( nextStep, showModal, hideModal, + isEditMode, setModalError, autosaveRequest, autosaveSuccess, @@ -93,7 +94,7 @@ export const onSubmit = ( ) => { if (step !== 2) { nextStep() - } else { + } else if (!isEditMode) { showModal({ title: 'By submitting the manuscript you agree to the following statements:', @@ -103,6 +104,7 @@ export const onSubmit = ( dispatch(autosaveRequest()) submitManuscript(collectionId, fragmentId) .then(r => { + hideModal() dispatch(autosaveSuccess()) history.push('/confirmation-page', { customId, @@ -111,11 +113,14 @@ export const onSubmit = ( }) }) .catch(e => { + hideModal() dispatch(autosaveFailure(e)) setModalError('Something went wrong.') }) }, onCancel: hideModal, }) + } else { + history.goBack() } } diff --git a/packages/components-faraday/src/components/Dashboard/DashboardCard.js b/packages/components-faraday/src/components/Dashboard/DashboardCard.js index 2d6ec271f17846f5956c043627d6a5302b8235cf..044a3a21e91903f72a444097b665d116eac4aff2 100644 --- a/packages/components-faraday/src/components/Dashboard/DashboardCard.js +++ b/packages/components-faraday/src/components/Dashboard/DashboardCard.js @@ -66,6 +66,7 @@ const DashboardCard = ({ collectionId={project.id} fragmentId={version.id} modalKey={`decide-${version.id}`} + status={project.status} /> )} {canMakeRecommendation && ( @@ -73,6 +74,7 @@ const DashboardCard = ({ collectionId={project.id} fragmentId={version.id} modalKey={`recommend-${version.id}`} + status={project.status} /> )} <ZipFiles diff --git a/packages/components-faraday/src/components/MakeDecision/Decision.js b/packages/components-faraday/src/components/MakeDecision/Decision.js index cf4279e1615dc66ec08c8deb6cd8fab98ca16207..be2b86e66f15032614dea4d9d832b43823d13bc0 100644 --- a/packages/components-faraday/src/components/MakeDecision/Decision.js +++ b/packages/components-faraday/src/components/MakeDecision/Decision.js @@ -1,17 +1,21 @@ import React from 'react' import { th } from '@pubsweet/ui' +import { connect } from 'react-redux' import styled from 'styled-components' -import { compose, withHandlers, setDisplayName } from 'recompose' +import { actions } from 'pubsweet-client' +import { compose, withHandlers, setDisplayName, withProps } from 'recompose' import { ConfirmationModal, withModal, } from 'pubsweet-component-modal/src/components' +import { handleError } from '../utils' +import { createRecommendation } from '../../redux/recommendations' import { DecisionForm } from './' -const Decision = ({ showDecisionModal }) => ( - <Root onClick={showDecisionModal}>Make decision</Root> +const Decision = ({ showDecisionModal, buttonText }) => ( + <Root onClick={showDecisionModal}>{buttonText}</Root> ) const ModalComponent = ({ type, ...rest }) => { @@ -28,19 +32,55 @@ export default compose( withModal(() => ({ modalComponent: ModalComponent, })), + connect(null, { + createRecommendation, + getFragments: actions.getFragments, + getCollections: actions.getCollections, + }), + withProps(({ status }) => ({ + buttonText: status === 'submitted' ? 'Reject' : 'Make Decision', + })), withHandlers({ showDecisionModal: ({ + status, showModal, hideModal, fragmentId, collectionId, + getFragments, + setModalError, + getCollections, + createRecommendation, }) => () => { - showModal({ - type: 'decision', - hideModal, - fragmentId, - collectionId, - }) + status !== 'submitted' + ? showModal({ + type: 'decision', + hideModal, + fragmentId, + collectionId, + }) + : showModal({ + hideModal, + fragmentId, + collectionId, + title: 'Reject Manuscript?', + confirmText: 'Reject', + onConfirm: () => { + const recommendation = { + recommendation: 'reject', + recommendationType: 'editorRecommendation', + } + createRecommendation( + collectionId, + fragmentId, + recommendation, + ).then(() => { + getCollections() + getFragments() + hideModal() + }, handleError(setModalError)) + }, + }) }, }), )(Decision) diff --git a/packages/components-faraday/src/components/MakeDecision/DecisionForm.js b/packages/components-faraday/src/components/MakeDecision/DecisionForm.js index 0a53c9423317ac06571ebf6470b6ee16b957fb0f..3fab58f21c15bcdd312cc0fa0f8df64ef928350e 100644 --- a/packages/components-faraday/src/components/MakeDecision/DecisionForm.js +++ b/packages/components-faraday/src/components/MakeDecision/DecisionForm.js @@ -27,7 +27,6 @@ const { const Form = RootContainer.withComponent(FormContainer) const DecisionForm = ({ - aHERec, decision, hideModal, handleSubmit, diff --git a/packages/components-faraday/src/components/MakeRecommendation/Recommendation.js b/packages/components-faraday/src/components/MakeRecommendation/Recommendation.js index 10d7fc6d98a6a1f1ce932aa5b6622015eee59530..dff35cbd4f9388814ac47cb005a418462845e9a5 100644 --- a/packages/components-faraday/src/components/MakeRecommendation/Recommendation.js +++ b/packages/components-faraday/src/components/MakeRecommendation/Recommendation.js @@ -30,12 +30,14 @@ export default compose( })), withHandlers({ showFirstStep: ({ + status, showModal, hideModal, fragmentId, collectionId, }) => () => { showModal({ + status, hideModal, fragmentId, collectionId, diff --git a/packages/components-faraday/src/components/MakeRecommendation/StepOne.js b/packages/components-faraday/src/components/MakeRecommendation/StepOne.js index 6185a09a1677c060032875ce80ebf83c4e1139be..b6723f9ff0a70dd03fb30668d2e8db606247cd66 100644 --- a/packages/components-faraday/src/components/MakeRecommendation/StepOne.js +++ b/packages/components-faraday/src/components/MakeRecommendation/StepOne.js @@ -7,7 +7,7 @@ import { FormItems } from '../UIComponents' const { Row, Title, RowItem, RootContainer, CustomRadioGroup } = FormItems -const StepOne = ({ hideModal, disabled, onSubmit }) => ( +const StepOne = ({ hideModal, disabled, onSubmit, status }) => ( <RootContainer> <Title>Recommendation for Next Phase</Title> <Row> @@ -20,7 +20,11 @@ const StepOne = ({ hideModal, disabled, onSubmit }) => ( > <RadioGroup name="decision" - options={utils.recommendationOptions} + options={ + status === 'reviewCompleted' + ? utils.recommendationOptions + : utils.recommendationOptions.slice(1) + } {...input} /> </CustomRadioGroup> diff --git a/packages/components-faraday/src/components/MakeRecommendation/utils.js b/packages/components-faraday/src/components/MakeRecommendation/utils.js index 2884cdb904717cfdd4958cc99accf9f852adc489..69e5b63f12e4b979dba34332a98fa8bec20eed17 100644 --- a/packages/components-faraday/src/components/MakeRecommendation/utils.js +++ b/packages/components-faraday/src/components/MakeRecommendation/utils.js @@ -1,8 +1,8 @@ import { omit } from 'lodash' export const recommendationOptions = [ - { value: 'reject', label: 'Reject' }, { value: 'publish', label: 'Publish' }, + { value: 'reject', label: 'Reject' }, { value: 'revise', label: 'Request revision' }, ] diff --git a/packages/components-faraday/src/components/SignUp/utils.js b/packages/components-faraday/src/components/SignUp/utils.js index ae98c93c7df0b2d169affe222f81c2c79f412311..c5564bfe044faa2d7903253efb4ac688082567d5 100644 --- a/packages/components-faraday/src/components/SignUp/utils.js +++ b/packages/components-faraday/src/components/SignUp/utils.js @@ -15,6 +15,7 @@ const generatePasswordHash = () => export const parseSignupAuthor = ({ token, confirmPassword, ...values }) => ({ ...values, admin: false, + isActive: true, isConfirmed: false, editorInChief: false, handlingEditor: false, diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js index 0e8568e0dc11ee83148a032d079c88617345c49a..7ed508e70cd8daf96855461bdca80167a16ff59d 100644 --- a/packages/xpub-faraday/config/default.js +++ b/packages/xpub-faraday/config/default.js @@ -46,7 +46,7 @@ module.exports = { API_ENDPOINT: '/api', baseUrl: process.env.CLIENT_BASE_URL || 'http://localhost:3000', 'login-redirect': '/', - 'redux-log': process.env.NODE_ENV === 'development', + 'redux-log': process.env.NODE_ENV !== 'production', theme: process.env.PUBSWEET_THEME, }, orcid: { @@ -110,9 +110,9 @@ module.exports = { }, }, mailer: { - from: 'faraday@hindawi.com', + from: 'hindawi@thinslices.com', path: `${__dirname}/mailer`, - editorialAssistant: 'hindawi@thinslices.com', + editorialAssistant: 'hindawi+editorial@thinslices.com', }, publicKeys: ['pubsweet-client', 'authsome', 'validations'], statuses: {