Skip to content
Snippets Groups Projects
Commit bec19d99 authored by Alexandru Munteanu's avatar Alexandru Munteanu
Browse files

feat(invite-reviewers): invite reviewers form and table

parent 6c312a07
No related branches found
No related tags found
2 merge requests!58Sprint #20 - Goal - Reviewers submit report,!50HIN-854: Invite + resend/revoke reviewer invitations
Showing
with 182 additions and 60 deletions
import React, { Fragment } from 'react'
import React from 'react'
import { has } from 'lodash'
import styled from 'styled-components'
import { Icon, H3 } from '@pubsweet/ui'
......@@ -21,12 +21,12 @@ const CustomHeader = ({
onClick={toggle}
transparent={transparent}
>
<Fragment>
<div>
<Icon secondary size={2}>
{expanded ? 'minus' : 'plus'}
</Icon>
<H3>{label}</H3>
</Fragment>
</div>
{typeof rightChildren === 'function'
? rightChildren({ ...props, expanded, transparent })
: rightChildren}
......
import React from 'react'
import { compose } from 'recompose'
import styled from 'styled-components'
import { reduxForm } from 'redux-form'
import { th } from '@pubsweet/ui-toolkit'
import { required } from 'xpub-validators'
import { withModal } from 'pubsweet-component-modal/src/components'
import { Button, H4, Menu, TextField, ValidatedField } from '@pubsweet/ui'
import {
Row,
Item,
Label,
MultiAction,
ItemOverrideAlert,
withFetching,
withCountries,
} from '../'
const InviteReviewers = ({ countries, handleSubmit, reset }) => (
<Root>
<Row justify="space-between" mb={2}>
<H4>Invite reviewer</H4>
<Item justify="flex-end">
<Button onClick={reset} size="small">
Clear Fields
</Button>
<Button ml={2} onClick={handleSubmit} primary size="small">
Invite
</Button>
</Item>
</Row>
<Row>
<Item mr={2} vertical>
<Label required>Email</Label>
<ValidatedField
component={TextField}
name="email"
validate={[required]}
/>
</Item>
<Item mr={2} vertical>
<Label required>First Name</Label>
<ValidatedField
component={TextField}
name="firstName"
validate={[required]}
/>
</Item>
<Item mr={2} vertical>
<Label required>Last Name</Label>
<ValidatedField
component={TextField}
name="lastName"
validate={[required]}
/>
</Item>
<Item mr={2} vertical>
<Label required>Affiliation</Label>
<ValidatedField
component={TextField}
name="affiliation"
validate={[required]}
/>
</Item>
<ItemOverrideAlert vertical>
<Label required>Country</Label>
<ValidatedField
component={input => <Menu options={countries} {...input} />}
name="country"
validate={[required]}
/>
</ItemOverrideAlert>
</Row>
</Root>
)
export default compose(
withFetching,
withCountries,
withModal(({ isFetching, modalKey }) => ({
modalKey,
isFetching,
modalComponent: MultiAction,
})),
reduxForm({
form: 'invite-reviewers-form',
onSubmit: (values, dispatch, { showModal, onInvite, setFetching }) => {
showModal({
title: 'Send invitation to Review?',
subtitle: values.email,
onConfirm: modalProps => {
onInvite(values, { ...modalProps, setFetching })
},
})
},
}),
)(InviteReviewers)
// #region styles
const Root = styled.div`
padding: calc(${th('gridUnit')} * 2);
`
// #endregion
The invite reviewers form.
```js
<InviteReviewers
onInvite={(reviewer, props) => {
props.setFetching(true)
}}
/>
```
......@@ -166,7 +166,6 @@ export default ReviewersTable
// #region styles
const Table = styled.table`
border-collapse: collapse;
padding: ${th('gridUnit')};
& thead {
border-bottom: 1px solid ${th('colorBorder')};
......@@ -175,7 +174,7 @@ const Table = styled.table`
& th,
& td {
border: none;
padding-left: ${th('gridUnit')};
padding-left: calc(${th('gridUnit')} * 2);
text-align: start;
vertical-align: middle;
......
......@@ -10,6 +10,7 @@ import {
ContextualBox,
ReviewersTable,
ReviewerReport,
InviteReviewers,
ReviewerBreakdown,
} from '../'
......@@ -34,7 +35,7 @@ const report = {
],
}
const ReviewerDetails = ({ fragment }) => (
const ReviewerDetails = ({ fragment, onInviteReviewer }) => (
<ContextualBox
label="Reviewer details"
rightChildren={<ReviewerBreakdown fitContent fragment={fragment} mr={1} />}
......@@ -62,7 +63,15 @@ const ReviewerDetails = ({ fragment }) => (
<Tag ml={1}>12</Tag>
</TabButton>
</TabsHeader>
{selectedTab === 0 && <ReviewersTable />}
{selectedTab === 0 && (
<Fragment>
<InviteReviewers
modalKey="invite-reviewers"
onInvite={onInviteReviewer}
/>
<ReviewersTable />
</Fragment>
)}
{selectedTab === 1 && (
<Fragment>
<ReviewerReport report={report} />
......
......@@ -77,7 +77,11 @@ const fragment = {
},
],
}
;
<ReviewerDetails fragment={fragment} />
;<ReviewerDetails
fragment={fragment}
onInviteReviewer={(reviewer, modalProps) => {
modalProps.setFetching(true)
}}
/>
```
......@@ -22,6 +22,7 @@ export { default as Footer } from './Footer'
export { default as IconButton } from './IconButton'
export { default as IconCard } from './IconCard'
export { default as IconTooltip } from './IconTooltip'
export { default as InviteReviewers } from './InviteReviewers'
export { default as Label } from './Label'
export { default as ManuscriptCard } from './ManuscriptCard'
export { default as ReviewerBreakdown } from './ReviewerBreakdown'
......
Handling Editor answer invitation.
```js
const formValues = {
decision: 'decline',
};
<RemoteOpener>
{
({toggle, expanded}) =>
<HandlingEditorAnswer
formValues={formValues}
expanded={expanded}
toggle={toggle}
onResponse={(values, { setFetching }) => {
setFetching(true)
}}
/>
}
</RemoteOpener>
```
import React, { Fragment } from 'react'
import { isEmpty, get } from 'lodash'
import { withProps } from 'recompose'
import {
Text,
......@@ -9,6 +10,7 @@ import {
} from 'pubsweet-component-faraday-ui'
const ManuscriptMetadata = ({
filesLabel,
getSignedUrl,
currentUser: { token },
fragment: { files = {}, conflicts = {}, metadata: { abstract = '' } },
......@@ -34,12 +36,7 @@ const ManuscriptMetadata = ({
)}
{!isEmpty(files) && (
<Item mb={1}>
<ContextualBox
label={`Files (${files.coverLetter.length +
files.manuscripts.length +
files.supplementary.length})`}
transparent
>
<ContextualBox label={filesLabel} transparent>
<ManuscriptFileList
files={files}
getSignedUrl={getSignedUrl}
......@@ -51,4 +48,8 @@ const ManuscriptMetadata = ({
</Fragment>
)
export default ManuscriptMetadata
export default withProps(({ fragment: { files } }) => ({
filesLabel: `Files (${get(files, 'manuscripts', []).length +
get(files, 'coverLetter', []).length +
get(files, 'supplementary', []).length})`,
}))(ManuscriptMetadata)
......@@ -3,6 +3,7 @@ import styled from 'styled-components'
import { isEmpty, get, last } from 'lodash'
import {
Text,
ReviewerDetails,
ManuscriptHeader,
ManuscriptAssignHE,
ManuscriptMetadata,
......@@ -46,6 +47,7 @@ const ManuscriptLayout = ({
toggleHEResponse,
heResponseExpanded,
onHEResponse,
onInviteReviewer,
}) => (
<Root pb={1}>
{!isEmpty(collection) && !isEmpty(fragment) ? (
......@@ -110,6 +112,11 @@ const ManuscriptLayout = ({
submitDecision={createRecommendation}
/>
)}
<ReviewerDetails
fragment={fragment}
onInviteReviewer={onInviteReviewer}
/>
</Fragment>
) : (
<Text>Loading...</Text>
......
......@@ -22,7 +22,10 @@ import {
fromRenderProps,
} from 'recompose'
import { getSignedUrl } from 'pubsweet-components-faraday/src/redux/files'
import { reviewerDecision } from 'pubsweet-components-faraday/src/redux/reviewers'
import {
inviteReviewer,
reviewerDecision,
} from 'pubsweet-components-faraday/src/redux/reviewers'
import {
hasManuscriptFailure,
clearCustomError,
......@@ -65,6 +68,7 @@ export default compose(
ConnectPage(({ match }) => [
actions.getCollection({ id: match.params.project }),
actions.getFragments({ id: match.params.project }),
actions.getUsers(),
]),
connect(
(state, { match }) => ({
......@@ -232,6 +236,26 @@ export default compose(
setModalError('Something went wrong...')
})
},
onInviteReviewer: ({ collection, fragment, fetchUpdatedCollection }) => (
values,
{ hideModal, setModalError, setFetching },
) => {
setFetching(true)
inviteReviewer({
reviewerData: values,
fragmentId: fragment.id,
collectionId: collection.id,
})
.then(() => {
setFetching(false)
hideModal()
fetchUpdatedCollection()
})
.catch(() => {
setFetching(false)
setModalError('Something went wrong...')
})
},
}),
fromRenderProps(RemoteOpener, ({ toggle, expanded }) => ({
toggleAssignHE: toggle,
......
......@@ -118,28 +118,13 @@ export const getCollectionReviewers = (
}
// #endregion
// #region Actions - invitations
export const inviteReviewer = (
reviewerData,
collectionId,
fragmentId,
) => dispatch => {
dispatch(inviteRequest())
return create(
`/collections/${collectionId}/fragments/${fragmentId}/invitations`,
{
...reviewerData,
role: 'reviewer',
},
).then(
() => dispatch(inviteSuccess()),
err => {
dispatch(inviteError(get(JSON.parse(err.response), 'error')))
throw err
},
)
}
export const inviteReviewer = ({ reviewerData, collectionId, fragmentId }) =>
create(`/collections/${collectionId}/fragments/${fragmentId}/invitations`, {
...reviewerData,
role: 'reviewer',
})
// #region Actions - invitations
export const setReviewerPassword = reviewerBody => dispatch => {
dispatch(reviewerDecisionRequest())
return create(`/users/reset-password`, reviewerBody).then(r => {
......
......@@ -6,7 +6,6 @@ export default {
background: ${th('colorBackgroundHue')};
height: calc(${th('gridUnit')} * 4);
border-radius: ${th('borderRadius')};
min-width: 120px;
`,
Value: css`
border: none;
......
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