diff --git a/packages/component-faraday-selectors/src/index.js b/packages/component-faraday-selectors/src/index.js index fbfcf2db03ad8928dbf383f8b80c010fb256d2a0..143fbdb4da42e97aaee2fc7e9f49b80199660060 100644 --- a/packages/component-faraday-selectors/src/index.js +++ b/packages/component-faraday-selectors/src/index.js @@ -59,7 +59,7 @@ export const getHERecommendation = (state, collectionId, fragmentId) => { const heId = chain(state) .get('collections', []) .find(c => c.id === collectionId) - .get('hadlingEditor.id', '') + .get('handlingEditor.id', '') .value() return ( @@ -174,15 +174,32 @@ export const pendingHEInvitation = (state, collectionId) => i.role === 'handlingEditor' && !i.hasAnswer, ) + .value() + +const parseInvitedHE = (handlingEditor, state, collectionId) => + handlingEditor && { + ...handlingEditor, + name: pendingHEInvitation(state, collectionId) + ? 'Invited' + : handlingEditor.name, + } const hideCustomIdStatuses = ['draft', 'technicalChecks'] -export const newestFirstParseCustomId = items => - chain(items) + +export const parseCollectionDetails = (state, collection) => ({ + ...collection, + customId: + !hideCustomIdStatuses.includes(get(collection, 'status', 'draft')) && + collection.customId, + handlingEditor: parseInvitedHE( + collection.handlingEditor, + state, + collection.id, + ), +}) + +export const newestFirstParseDashboard = (state = {}) => + chain(state.collections) .orderBy(['created'], ['desc']) - .map(item => ({ - ...item, - customId: - !hideCustomIdStatuses.includes(get(item, 'status', 'draft')) && - item.customId, - })) + .map(item => parseCollectionDetails(state, item)) .value() diff --git a/packages/component-faraday-ui/src/ManuscriptCard.js b/packages/component-faraday-ui/src/ManuscriptCard.js index 269c0401842819645308600decec57ae403e711b..e9f0e796f79d1b5319c9a2a8faaccb6006619a58 100644 --- a/packages/component-faraday-ui/src/ManuscriptCard.js +++ b/packages/component-faraday-ui/src/ManuscriptCard.js @@ -26,7 +26,7 @@ const ManuscriptCard = ({ onCardClick, fragment = {}, manuscriptType = {}, - collection: { visibleStatus = 'Draft', handlingEditor, customId }, + collection: { visibleStatus = 'Draft', handlingEditor, customId, id: collId }, }) => { const { authors = [], @@ -82,7 +82,7 @@ const ManuscriptCard = ({ <Item justify="flex-end" onClick={e => e.stopPropagation()}> <OpenModal confirmText="Delete" - modalKey={`delete-${customId}`} + modalKey={`delete-${collId}`} onConfirm={onDelete} title="Are you sure you want to delete this submission?" > diff --git a/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptAssignHE.js b/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptAssignHE.js index d1772efc4a54ef7ec1ce00830107821e983866ad..b30f045ad4725e1502b54433ca631d168d17b359 100644 --- a/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptAssignHE.js +++ b/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptAssignHE.js @@ -6,10 +6,10 @@ const ManuscriptAssignHE = ({ assignHE, expanded, isFetching, - currentUser, + currentUser: { permissions: { canAssignHE = false } }, handlingEditors = [], }) => - currentUser.canAssignHE ? ( + canAssignHE ? ( <ContextualBox expanded={expanded} label="Assign Handling Editor" diff --git a/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptAssignHE.md b/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptAssignHE.md index e1eaea01f79c97fc104e357806b54c72f22b11b0..7787e62799867bccde289c719eaad3ece654ea85 100644 --- a/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptAssignHE.md +++ b/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptAssignHE.md @@ -20,13 +20,15 @@ const handlingEditors = [ lastName: 'Cochior', email: 'handling3@edi.com', }, -]; +] const currentUser = { - canAssignHE: true, -}; + permissions: { + canAssignHE: true, + }, +} -<RemoteOpener> +;<RemoteOpener> {(expanded, toggle) => ( <ManuscriptAssignHE toggle={toggle} diff --git a/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptDetailsTop.js b/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptDetailsTop.js index c395539b255c2a929700a26290598067249c1c5d..516a6ca9010541dc82558a0a29f46afbcf0d36cd 100644 --- a/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptDetailsTop.js +++ b/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptDetailsTop.js @@ -18,12 +18,14 @@ const ManuscriptDetailsTop = ({ history, goToEdit, getSignedUrl, - canEditManuscript, goToTechnicalCheck, - canOverrideTechChecks, fragment = {}, collection = {}, - currentUser: { isReviewer, token }, + currentUser: { + isReviewer, + token, + permissions: { canEditManuscript, canOverrideTechChecks }, + }, }) => ( <Row alignItems="center" mb={1} mt={1}> <Item alignItems="center" justify="flex-start"> diff --git a/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptDetailsTop.md b/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptDetailsTop.md index 83b26fc585d2bd7fa43dc8a2e546e57bf3bda120..f4b7fc2d8524c16945c715d88db44f52824025ab 100644 --- a/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptDetailsTop.md +++ b/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptDetailsTop.md @@ -9,6 +9,10 @@ const collection={}; const currentUser = { isReviewer: true, token: 'abc-123', + permissions: { + canOverrideTechChecks: true, + canEditManuscript: true + } }; <ManuscriptDetailsTop @@ -16,7 +20,5 @@ const currentUser = { currentUser={currentUser} fragment={fragment} history={history} - canOverrideTechChecks - canEditManuscript /> ``` diff --git a/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptHeader.js b/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptHeader.js index 6927e7ba41b1ffb70b1158a9382b06651d4c0c46..cc79766601a6e3bf1461efb6c0c1eaa294624d95 100644 --- a/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptHeader.js +++ b/packages/component-faraday-ui/src/manuscriptDetails/ManuscriptHeader.js @@ -40,7 +40,7 @@ const ManuscriptHeader = ({ </Row> )} <Row alignItems="center" justify="flex-start" mb={1}> - <Text customId mr={1}>{`ID ${customId}`}</Text> + {customId && <Text customId mr={1}>{`ID ${customId}`}</Text>} {submitted && ( <DateParser durationThreshold={0} timestamp={submitted}> {timestamp => <Text mr={3}>Submitted on {timestamp}</Text>} @@ -99,18 +99,20 @@ export default compose( heInvitation, resendInvitation, revokeInvitation, - pendingInvitation, + pendingInvitation = {}, handlingEditors = [], - currentUser: { canAssignHE }, + currentUser: { permissions: { canAssignHE }, id: currentUserId }, collection: { handlingEditor }, }) => () => { - if (pendingInvitation) { + if (pendingInvitation.userId === currentUserId) { + return <Text ml={1}>Invited</Text> + } + if (pendingInvitation.userId) { const person = chain(handlingEditors) .filter(he => he.id === pendingInvitation.userId) .map(he => ({ ...he, name: `${he.firstName} ${he.lastName}` })) .first() .value() - return ( <PersonInvitation isFetching={isFetching} @@ -126,6 +128,7 @@ export default compose( if (heInvitation) { return <Text ml={1}>{handlingEditor.name}</Text> } + if (canAssignHE) { return ( <Button ml={1} onClick={inviteHE} primary size="small"> diff --git a/packages/component-fixture-manager/src/fixtures/collections.js b/packages/component-fixture-manager/src/fixtures/collections.js index 61d7cf2acfc1c01ac4a0ecdf77577f71f667ba1f..53904e967b61e820dcb2c517cb16625f2284fc1a 100644 --- a/packages/component-fixture-manager/src/fixtures/collections.js +++ b/packages/component-fixture-manager/src/fixtures/collections.js @@ -44,7 +44,7 @@ const collections = { technicalChecks: { token: chance.guid(), }, - status: 'pendingApproval', + status: 'submitted', customId: chance.natural({ min: 999999, max: 9999999 }), }, collection1: { diff --git a/packages/component-invite/src/routes/collectionsInvitations/post.js b/packages/component-invite/src/routes/collectionsInvitations/post.js index d1c3eae146fcb68eb139b8d5d4374b016ebdfa80..bdd0db7c4cc591eb063a1d4a5aac3fc5449198b0 100644 --- a/packages/component-invite/src/routes/collectionsInvitations/post.js +++ b/packages/component-invite/src/routes/collectionsInvitations/post.js @@ -51,6 +51,14 @@ module.exports = models => async (req, res) => { error: 'Unauthorized.', }) + // check collection status + if (collection.status !== 'submitted') { + return res.status(400).json({ + error: `Cannot invite HE while collection is in the status: ${ + collection.status + }.`, + }) + } const collectionHelper = new Collection({ collection }) const teamHelper = new Team({ diff --git a/packages/component-invite/src/tests/collectionsInvitations/post.test.js b/packages/component-invite/src/tests/collectionsInvitations/post.test.js index 18e7ce2b1944aa5f6c33fbe0ae9fb60e56ee791b..c2f73095391b8869e6cd0b7167af6bc10faa9e23 100644 --- a/packages/component-invite/src/tests/collectionsInvitations/post.test.js +++ b/packages/component-invite/src/tests/collectionsInvitations/post.test.js @@ -167,4 +167,33 @@ describe('Post collections invitations route handler', () => { const data = JSON.parse(res._getData()) expect(data.error).toEqual('Unauthorized.') }) + it('should return an error when the collection is not in the submitted status', async () => { + const { user, editorInChief } = testFixtures.users + const { collection } = testFixtures.collections + collection.status = 'pendingApproval' + + body = { + email: user.email, + role: 'handlingEditor', + } + const res = await requests.sendRequest({ + body, + userId: editorInChief.id, + route, + models, + path, + params: { + collectionId: collection.id, + }, + }) + + // expect(res.statusCode).toBe(200) + const data = JSON.parse(res._getData()) + + expect(data.error).toEqual( + `Cannot invite HE while collection is in the status: ${ + collection.status + }.`, + ) + }) }) diff --git a/packages/component-manuscript/src/components/ManuscriptLayout.js b/packages/component-manuscript/src/components/ManuscriptLayout.js index b1fc53ae09426250b0ebf3c4552d6cdd06e8875e..47d39721cccb24282ad928b7bb9e17e0f2dc6144 100644 --- a/packages/component-manuscript/src/components/ManuscriptLayout.js +++ b/packages/component-manuscript/src/components/ManuscriptLayout.js @@ -38,7 +38,6 @@ const ManuscriptLayout = ({ journal = {}, collection = {}, fragment = {}, - permissions, isFetching, formValues, toggleAssignHE, @@ -56,7 +55,6 @@ const ManuscriptLayout = ({ fragment={fragment} getSignedUrl={getSignedUrl} history={history} - {...permissions} /> <ManuscriptHeader @@ -78,7 +76,7 @@ const ManuscriptLayout = ({ getSignedUrl={getSignedUrl} /> - {permissions.isInvitedHE && ( + {get(currentUser, 'isInvitedHE', false) && ( <HandlingEditorAnswer expanded={heResponseExpanded} formValues={formValues.heInvitation} @@ -96,7 +94,7 @@ const ManuscriptLayout = ({ toggle={toggleAssignHE} /> - {permissions.canMakeDecision && ( + {get(currentUser, 'permissions.canMakeDecision', false) && ( <ManuscriptEicDecision formValues={get(formValues, 'eicDecision')} isFetching={isFetching.recommendationsFetching} diff --git a/packages/component-manuscript/src/components/ManuscriptPage.js b/packages/component-manuscript/src/components/ManuscriptPage.js index fb18e691bd87ebc10877a2f9959d71c5e573b368..c1abbef8a90f4a0203bfc5d4afc5da1021f09d46 100644 --- a/packages/component-manuscript/src/components/ManuscriptPage.js +++ b/packages/component-manuscript/src/components/ManuscriptPage.js @@ -37,6 +37,7 @@ import { pendingHEInvitation, currentUserIsReviewer, canMakeRecommendation, + parseCollectionDetails, canOverrideTechnicalChecks, } from 'pubsweet-component-faraday-selectors' import { RemoteOpener } from 'pubsweet-component-faraday-ui' @@ -72,7 +73,10 @@ export default compose( handlingEditors: selectHandlingEditors(state), hasManuscriptFailure: hasManuscriptFailure(state), fragment: selectFragment(state, match.params.version), - collection: selectCollection(state, match.params.project), + collection: parseCollectionDetails( + state, + selectCollection(state, match.params.project), + ), pendingHEInvitation: pendingHEInvitation(state, match.params.project), editorialRecommendations: selectEditorialRecommendations( state, @@ -103,31 +107,31 @@ export default compose( isEIC: currentUserIs(state, 'adminEiC'), isHE: currentUserIs(state, 'isHE'), isReviewer: currentUserIsReviewer(state), - canAssignHE: canAssignHE(state, match.params.project), + isInvitedHE: !isUndefined(pendingHEInvitation), + permissions: { + canAssignHE: canAssignHE(state, match.params.project), + canMakeRevision: canMakeRevision(state, collection, fragment), + canMakeDecision: canMakeDecision(state, collection, fragment), + canEditManuscript: canEditManuscript(state, collection, fragment), + canOverrideTechChecks: canOverrideTechnicalChecks(state, collection), + canMakeRecommendation: canMakeRecommendation( + state, + collection, + fragment, + ), + }, }, isFetching: { editorsFetching: selectFetching(state), recommendationsFetching: recommendationsFetching(state), }, - permissions: { - isInvitedHE: !isUndefined(pendingHEInvitation), - canMakeRevision: canMakeRevision(state, collection, fragment), - canMakeDecision: canMakeDecision(state, collection, fragment), - canEditManuscript: canEditManuscript(state, collection, fragment), - canOverrideTechChecks: canOverrideTechnicalChecks(state, collection), - canMakeRecommendation: canMakeRecommendation( - state, - collection, - fragment, - ), - }, formValues: { eicDecision: getFormValues('eic-decision')(state), heInvitation: getFormValues('he-answer-invitation')(state), }, }), ), - ConnectPage(({ currentUser, handlingEditors, collection }) => { + ConnectPage(({ currentUser }) => { if (currentUser.isEIC) { return [getHandlingEditors()] } @@ -254,7 +258,7 @@ export default compose( setEditorInChief, clearCustomError, hasManuscriptFailure, - permissions: { isInvitedHE }, + currentUser: { isInvitedHE }, } = this.props if (hasManuscriptFailure) { history.push('/not-found') diff --git a/packages/component-manuscript/src/redux/editors.js b/packages/component-manuscript/src/redux/editors.js index e39a801a48fc179f638dfb1c36d16a50dd664401..794f4266b2b07ebe31d66f3300895fb7c54596c8 100644 --- a/packages/component-manuscript/src/redux/editors.js +++ b/packages/component-manuscript/src/redux/editors.js @@ -23,15 +23,20 @@ export const selectHandlingEditors = state => .filter(editor => editor.isActive && editor.isConfirmed) .value() +const canAssignHEStatuses = ['submitted'] export const canAssignHE = (state, collectionId = '') => { const isEIC = currentUserIs(state, 'adminEiC') - const hasHE = chain(state) + const collection = chain(state) .get('collections', []) .find(c => c.id === collectionId) - .get('handlingEditor') .value() + const hasHE = get(collection, 'handlingEditor') - return isEIC && !hasHE + return ( + isEIC && + !hasHE && + canAssignHEStatuses.includes(get(collection, 'status', 'draft')) + ) } const editorsRequest = () => ({ type: EDITORS_REQUEST }) diff --git a/packages/components-faraday/src/components/Dashboard/DashboardPage.js b/packages/components-faraday/src/components/Dashboard/DashboardPage.js index 6831dbc0265ec953396fafa62fe49e8bae853af9..eda5819b44ca04a7b91afc876a6cec42bba2569c 100644 --- a/packages/components-faraday/src/components/Dashboard/DashboardPage.js +++ b/packages/components-faraday/src/components/Dashboard/DashboardPage.js @@ -9,7 +9,7 @@ import { selectCurrentUser } from 'xpub-selectors' import { getUserPermissions, - newestFirstParseCustomId, + newestFirstParseDashboard, } from 'pubsweet-component-faraday-selectors' import { Dashboard } from './' @@ -21,7 +21,7 @@ export default compose( state => { const { collections, conversion } = state const currentUser = selectCurrentUser(state) - const dashboard = newestFirstParseCustomId(collections) + const dashboard = newestFirstParseDashboard(state) const userPermissions = getUserPermissions(state) return {