Commit 5c861e9e authored by Tamlyn Rhodes's avatar Tamlyn Rhodes

feat: send email to authors when decision is made

Move decision processing to new server endpoint
parent 06a97767
{
"name": "pubsweet-component-xpub-review-backend",
"version": "0.1.0",
"main": "src",
"scripts": {},
"keywords": [],
"author": "Collaborative Knowledge Foundation",
"license": "MIT",
"dependencies": {
"body-parser": "^1.17.2",
"config": "^1.26.1",
"moment": "^2.18.1",
"nodemailer": "^4.0.1"
},
"peerDependencies": {
"@pubsweet/logger": ">=0.0.1",
"nodemailer": "^4.4.2",
"pubsweet-component-xpub-review": "^0.0.1",
"pubsweet-server": ">=1.0.0-alpha.1"
}
}
module.exports = {
backend: () => require('./reviewBackend.js'),
}
const { pick } = require('lodash')
const config = require('config')
const nodemailer = require('nodemailer')
const logger = require('@pubsweet/logger')
const User = require('pubsweet-server/src/models/User')
const Fragment = require('pubsweet-server/src/models/Fragment')
const Collection = require('pubsweet-server/src/models/Collection')
const options = config.get('mailer.transport')
const transport = nodemailer.createTransport(options)
module.exports = app => {
app.patch('/api/make-decision', async (req, res, next) => {
try {
const version = await Fragment.find(req.body.versionId)
const project = await Collection.find(req.body.projectId)
const authors = await Promise.all(version.owners.map(id => User.find(id)))
version.decision = req.body.decision
await version.save()
let nextVersion
let message
switch (version.decision.recommendation) {
case 'accept':
project.status = 'accepted'
message = 'Your manuscript has been accepted'
break
case 'reject':
project.status = 'rejected'
message = 'Your manuscript has been rejected'
break
case 'revise': {
project.status = 'revising'
message = 'Revisions to your manuscript have been requested'
const cloned = pick(version, [
'source',
'metadata',
'declarations',
'suggestions',
'files',
'notes',
])
nextVersion = new Fragment({
fragmentType: 'version',
created: new Date(),
...cloned,
version: version.version + 1,
})
await nextVersion.save()
break
}
default:
throw new Error('Unknown decision type')
}
await project.save()
const authorEmails = authors.map(user => user.email)
logger.info(`Sending decision email to ${authorEmails}`)
await transport.sendMail({
from: config.get('mailer.from'),
to: authorEmails,
subject: 'Decision made',
text: message,
})
res.send({ version, project, nextVersion })
} catch (err) {
next(err)
}
})
}
import { debounce, pick } from 'lodash'
import { debounce } from 'lodash'
import { compose, withProps } from 'recompose'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
......@@ -14,66 +14,6 @@ import {
import uploadFile from 'xpub-upload'
import DecisionLayout from './decision/DecisionLayout'
// TODO: this should happen on the server
const handleDecision = (project, version) => dispatch =>
dispatch(
actions.updateFragment(project, {
decision: version.decision,
id: version.id,
rev: version.rev,
}),
).then(() => {
const cloned = pick(version, [
'source',
'metadata',
'declarations',
'suggestions',
'files',
'notes',
])
switch (version.decision.recommendation) {
case 'accept':
return dispatch(
actions.updateCollection({
id: project.id,
rev: project.rev,
status: 'accepted',
}),
)
case 'reject':
return dispatch(
actions.updateCollection({
id: project.id,
rev: project.rev,
status: 'rejected',
}),
)
case 'revise':
return dispatch(
actions.updateCollection({
id: project.id,
rev: project.rev,
status: 'revising',
}),
).then(() =>
dispatch(
actions.createFragment(project, {
fragmentType: 'version',
created: new Date(), // TODO: set on server
...cloned,
version: version.version + 1,
}),
),
)
default:
throw new Error('Unknown decision type')
}
})
const onSubmit = (values, dispatch, { project, version, history }) => {
version.decision = {
...version.decision,
......@@ -82,7 +22,7 @@ const onSubmit = (values, dispatch, { project, version, history }) => {
submitted: new Date(),
}
return dispatch(handleDecision(project, version))
return dispatch(actions.makeDecision(project, version))
.then(() => {
// TODO: show "thanks for your review" message
history.push('/')
......
module.exports = {
frontend: {
components: [() => require('./components')],
actions: () => ({ makeDecision: require('./redux').makeDecision }),
reducers: {
makeDecision: () => require('./redux').default,
},
},
}
import * as api from 'pubsweet-client/src/helpers/api'
import {
GET_COLLECTION_SUCCESS,
GET_FRAGMENT_SUCCESS,
} from 'pubsweet-client/src/actions/types'
export const MAKE_DECISION_REQUEST = 'MAKE_DECISION_REQUEST'
export const MAKE_DECISION_SUCCESS = 'MAKE_DECISION_SUCCESS'
export const MAKE_DECISION_FAILURE = 'MAKE_DECISION_FAILURE'
function makeDecisionRequest(project, version) {
return {
type: MAKE_DECISION_REQUEST,
project,
version,
}
}
function makeDecisionSuccess(project, version, result) {
return {
type: MAKE_DECISION_SUCCESS,
project,
version,
result,
}
}
function makeDecisionFailure(project, version, error) {
return {
type: MAKE_DECISION_FAILURE,
project,
version,
error,
}
}
export function makeDecision(project, version) {
return dispatch => {
dispatch(makeDecisionRequest(project, version))
return api
.update('/make-decision', {
projectId: project.id,
versionId: version.id,
decision: version.decision,
})
.then(result => {
dispatch({
type: GET_COLLECTION_SUCCESS,
collection: result.project,
receivedAt: Date.now(),
})
dispatch({
type: GET_FRAGMENT_SUCCESS,
fragment: result.version,
receivedAt: Date.now(),
})
if (result.nextVersion) {
dispatch({
type: GET_FRAGMENT_SUCCESS,
fragment: result.nextVersion,
receivedAt: Date.now(),
})
}
dispatch(makeDecisionSuccess(project, version, result))
})
.catch(error => dispatch(makeDecisionFailure(project, version, error)))
}
}
const initialState = {}
export default (state = initialState, action) => {
switch (action.type) {
case MAKE_DECISION_REQUEST:
return {}
case MAKE_DECISION_SUCCESS:
return {}
case MAKE_DECISION_FAILURE:
return { error: action.error }
default:
return state
}
}
......@@ -3,6 +3,7 @@
"pubsweet-component-xpub-dashboard",
"pubsweet-component-xpub-manuscript",
"pubsweet-component-xpub-review",
"pubsweet-component-xpub-review-backend",
"pubsweet-component-xpub-submit",
"pubsweet-component-ink-backend",
"pubsweet-component-login",
......
......@@ -27,8 +27,11 @@ module.exports = {
'redux-log': false,
theme: process.env.PUBSWEET_THEME,
},
'mail-transport': {
sendmail: true,
mailer: {
from: 'dev@example.com',
transport: {
sendmail: true,
},
},
'password-reset': {
url:
......
......@@ -28,6 +28,7 @@
"pubsweet-component-xpub-dashboard": "^0.0.2",
"pubsweet-component-xpub-manuscript": "^0.0.2",
"pubsweet-component-xpub-review": "^0.0.2",
"pubsweet-component-xpub-review-backend": "^0.1.0",
"pubsweet-component-xpub-submit": "^0.0.2",
"pubsweet-server": "^1.0.1",
"react": "^16.2.0",
......
......@@ -1399,7 +1399,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.8"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
body-parser@1.18.2, body-parser@^1.15.2:
body-parser@1.18.2, body-parser@^1.15.2, body-parser@^1.17.2:
version "1.18.2"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454"
dependencies:
......@@ -7315,6 +7315,10 @@ node-sass@^4.5.3:
stdout-stream "^1.4.0"
"true-case-path" "^1.0.2"
nodemailer@^4.0.1:
version "4.4.2"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-4.4.2.tgz#f215fb88e8a1052f9f93083909e116d2b79fc8de"
nomnom@~1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment