Commit 0609769b authored by Audrey Hamelers's avatar Audrey Hamelers

Merge branch 'dev' into 'master'

Dev

See merge request !193
parents 1f62b398 7f925b18
Pipeline #13424 failed with stages
in 31 seconds
......@@ -188,7 +188,7 @@ class GrantSearch extends React.Component {
<H2>Funding</H2>
<H3>Add all grants that support this manuscript</H3>
<SearchSelect
displaySelected="`${option.fundingSource}, ${option.awardId} (${option.pi.surname})`"
displaySelected="`${option.fundingSource}, ${option.awardId} (${option.pi.surname}): ${option.title}`"
icon="chevron_down"
invalidTest={this.state.grantsErr}
label="Search by Grant # or by PI surname followed by initial(s), e.g. Smith A"
......
import React from 'react'
import { Icon } from '@pubsweet/ui'
import { th, lighten } from '@pubsweet/ui-toolkit'
import styled from 'styled-components'
import { notification } from 'config'
import moment from 'moment'
import { Notification } from './ui'
const Christmas = styled(Notification)`
background-color: ${lighten('colorWarning', 70)};
div {
display: flex;
align-items: flex-start;
.text {
margin-left: ${th('gridUnit')};
}
}
`
const start = moment(notification.holiday.start).format('dddd, D MMMM')
const end = moment(notification.holiday.end).format('dddd, D MMMM')
const Holiday = props => (
<Christmas>
<Icon color="green" size={2.5}>
gift
</Icon>
<Icon color="#a80202" size={2.5}>
gift
</Icon>
<span className="text">
Please note that the Helpdesk will be unavailable from {start} to {end}.
Any calls or e-mails will be saved and responded to on our return. Happy
Holidays from the Europe PMC team.{' '}
</span>
</Christmas>
)
export default Holiday
import React from 'react'
import { omit } from 'lodash'
import styled, { withTheme } from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { Action, H2, Button, Icon } from '@pubsweet/ui'
import { states } from 'config'
import { Portal, Buttons, CloseModal, Notification } from '../ui'
import { withTheme } from 'styled-components'
import { Button } from '@pubsweet/ui'
import { Portal, Buttons, CloseModal } from '../ui'
import { ManuscriptMutations, NoteMutations } from '../SubmissionMutations'
import SubmissionCancel from '../SubmissionCancel'
import ResolveDuplicates from '../ResolveDuplicates'
import ReviewerEdit from './ReviewerEdit'
import CitationEdit from './CitationEdit'
import GrantsEdit from './GrantsEdit'
import EmbargoEdit from './EmbargoEdit'
import StatusEdit from './StatusEdit'
import { QUERY_ACTIVITY_INFO } from './operations'
const RouteButton = styled(Button)`
align-items: center;
display: inline-flex;
`
const FlexP = styled.p`
display: flex;
align-items: center;
& > button {
flex: 0 0 auto;
}
& > span {
flex: 1 1 50%;
margin-left: ${th('gridUnit')};
}
`
export const Exit = ({ close }) => (
<Buttons right>
<Button onClick={close}>Exit</Button>
</Buttons>
)
const DeleteButton = ({ deleteMan }) => (
<Action onClick={() => deleteMan()}>Confirm deletion</Action>
)
const RecoverButton = ({ callback, recoverMan }) => (
<RouteButton onClick={() => recoverMan(callback())}>
<Icon color="currentColor" size={2.5}>
refresh-cw
</Icon>
Recover manuscript
</RouteButton>
)
const DuplicatesWithMutations = NoteMutations(ResolveDuplicates)
const ReviewerWithMutations = NoteMutations(ReviewerEdit)
......@@ -72,10 +42,7 @@ const MetaEdit = withTheme(
return n
})
: []
const qa = states.indexOf('submitted')
const xml = states.indexOf('xml-triage')
const done = states.indexOf('ncbi-ready')
const curr = states.indexOf(manuscript.status)
if (toEdit === 'dupes' && duplicates) {
return (
<DuplicatesWithMutations
......@@ -160,141 +127,12 @@ const MetaEdit = withTheme(
)
case 'status':
return (
<React.Fragment>
<H2>Send/Remove</H2>
{manuscript.deleted ? (
<React.Fragment>
<Notification type="info">
{`Manuscript succesfully deleted. If not already sent, please send a message to the user(s) explaining the deletion.`}
</Notification>
<FlexP>
<SubmissionCancel
callback={close}
manuscriptId={manuscript.id}
refetch={[
{
query: QUERY_ACTIVITY_INFO,
variables: { id: manuscript.id },
},
]}
showComponent={RecoverButton}
/>
</FlexP>
</React.Fragment>
) : (
<React.Fragment>
{manuscript.status === 'submission-error' ? (
<FlexP>
<RouteButton
onClick={() =>
props.setStatus(lastStatus || 'submitted')
}
>
<Icon color="currentColor" size={2.5}>
check-circle
</Icon>
Cancel submission error
</RouteButton>
<span>
Cancel submission error request and send back to
{(lastStatus &&
lastStatus === 'xml-triage' &&
` XML errors state ('xml-triage')`) ||
(lastStatus &&
lastStatus === 'in-review' &&
` Initial review state (in-review)`) ||
` QA state ('submitted')`}
</span>
</FlexP>
) : (
<React.Fragment>
{lastStatus && (
<FlexP>
<RouteButton
onClick={() =>
props.setStatus(lastStatus, close)
}
>
<Icon color="currentColor" size={2.5}>
skip-back
</Icon>
Send back
</RouteButton>
<span>
to previous status of submission: {lastStatus}
<br />
Note that no emails will be automatically
sent!
</span>
</FlexP>
)}
<FlexP>
<RouteButton
disabled={!(curr > qa && curr !== xml)}
onClick={() =>
props.setStatus(
curr > xml ? 'xml-triage' : 'submitted',
close,
)
}
>
<Icon color="currentColor" size={2.5}>
send
</Icon>
Send for routing
</RouteButton>
{curr > qa && curr !== xml ? (
<span>
{` send to most recent routable status:`}
{curr > xml ? ' xml-triage' : ' submitted'}
</span>
) : (
<span>
{` The submission is already in a routable state (`}
{manuscript.status}
{`). You may send it backward or forward from the manuscript view.`}
</span>
)}
</FlexP>
</React.Fragment>
)}
<FlexP>
<RouteButton
disabled={curr >= done}
onClick={e =>
e.currentTarget.nextElementSibling.classList.remove(
'hidden',
)
}
>
<Icon color="currentColor" size={2.5}>
trash-2
</Icon>
Delete manuscript
</RouteButton>
<span className="hidden">
{` Are you certain? `}
<SubmissionCancel
manuscriptId={manuscript.id}
refetch={[
{
query: QUERY_ACTIVITY_INFO,
variables: { id: manuscript.id },
},
]}
showComponent={DeleteButton}
/>
</span>
{curr >= done && (
<span>
{` Manuscript has been sent to PMC and must be withdrawn.`}
</span>
)}
</FlexP>
</React.Fragment>
)}
<Exit close={close} />
</React.Fragment>
<StatusEdit
close={close}
lastStatus={lastStatus}
manuscript={manuscript}
setStatus={props.setStatus}
/>
)
default:
return null
......
......@@ -65,7 +65,7 @@ const DupeButton = styled(Button)`
padding: calc(${th('gridUnit')} / 2) ${th('gridUnit')};
font-size: ${th('fontSizeBaseSmall')};
`
const EditIcon = () => (
export const EditIcon = () => (
<Icon color="currentColor" size={1.9} style={{ verticalAlign: 'bottom' }}>
edit-3
</Icon>
......
import React from 'react'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { Action, H2, Button, Icon, RadioGroup } from '@pubsweet/ui'
import { states } from 'config'
import { Notification } from '../ui'
import SubmissionCancel from '../SubmissionCancel'
import { QUERY_ACTIVITY_INFO } from './operations'
import { Exit } from './MetaEdit'
import { EditIcon } from './MetaSec'
const FlexP = styled.p`
display: flex;
align-items: center;
& > button {
flex: 0 0 auto;
}
& > span {
flex: 1 1 50%;
margin-left: ${th('gridUnit')};
}
`
const RouteButton = styled(Button)`
align-items: center;
display: inline-flex;
`
const StatusDesc = [
'not yet submitted',
'not yet submitted',
'submission error',
'needs reviewer approval',
'needs submission QA',
'tagging',
'needs XML QA',
'has XML errors',
'needs final review',
'needs citation',
'approved for Europe PMC',
'failed NCBI loading',
'available in Europe PMC',
'being withdrawn',
]
const DeleteButton = ({ deleteMan }) => (
<Action onClick={() => deleteMan()}>Confirm deletion</Action>
)
const RecoverButton = ({ callback, recoverMan }) => (
<RouteButton onClick={() => recoverMan(callback())}>
<Icon color="currentColor" size={2.5}>
refresh-cw
</Icon>
Recover manuscript
</RouteButton>
)
class StatusEdit extends React.Component {
state = {
edit: !this.props.lastStatus,
selected: this.props.lastStatus,
}
render() {
const { manuscript, lastStatus, setStatus, close } = this.props
const { edit, selected } = this.state
const done = states.indexOf('ncbi-ready')
const curr = states.indexOf(manuscript.status)
const options = states.map((s, i) => ({
value: s,
label: `"${s}" – ${StatusDesc[i]}${
s === lastStatus ? ' [most recent]' : ''
}${s === manuscript.status ? ' [current]' : ''}`,
disabled: s === manuscript.status,
}))
return (
<React.Fragment>
<H2>Send/Remove</H2>
{manuscript.deleted ? (
<React.Fragment>
<Notification type="info">
{`Manuscript succesfully deleted. If not already sent, please send a message to the user(s) explaining the deletion.`}
</Notification>
<FlexP>
<SubmissionCancel
callback={close}
manuscriptId={manuscript.id}
refetch={[
{
query: QUERY_ACTIVITY_INFO,
variables: { id: manuscript.id },
},
]}
showComponent={RecoverButton}
/>
</FlexP>
</React.Fragment>
) : (
<React.Fragment>
{manuscript.status === 'submission-error' && (
<FlexP>
<RouteButton
onClick={() => setStatus(lastStatus || 'submitted')}
>
<Icon color="currentColor" size={2.5}>
check-circle
</Icon>
Cancel submission error
</RouteButton>
<span>
Cancel submission error request and send back to
{(lastStatus &&
lastStatus === 'xml-triage' &&
` XML errors state ('xml-triage')`) ||
(lastStatus &&
lastStatus === 'in-review' &&
` Initial review state (in-review)`) ||
` QA state ('submitted')`}
</span>
</FlexP>
)}
<React.Fragment>
<FlexP>
<RouteButton
onClick={() => {
setStatus(selected)
close()
}}
>
<Icon color="currentColor" size={2.5}>
send
</Icon>
Change status
</RouteButton>
<span>
send{selected === lastStatus && ' back'} to <b>{selected}</b>
<Action onClick={() => this.setState({ edit: true })}>
<EditIcon />
</Action>
<em style={{ marginLeft: '8px' }}>
{selected === lastStatus && '(most recent status)'}
</em>
<br />
Please note that no emails will be automatically sent!
</span>
</FlexP>
{edit && (
<RadioGroup
name="Statuses"
onChange={v => this.setState({ selected: v })}
options={options}
value={selected}
/>
)}
<FlexP>
<RouteButton
disabled={curr >= done}
onClick={e =>
e.currentTarget.nextElementSibling.classList.remove(
'hidden',
)
}
>
<Icon color="currentColor" size={2.5}>
trash-2
</Icon>
Delete manuscript
</RouteButton>
<span className="hidden">
{` Are you certain? `}
<SubmissionCancel
manuscriptId={manuscript.id}
refetch={[
{
query: QUERY_ACTIVITY_INFO,
variables: { id: manuscript.id },
},
]}
showComponent={DeleteButton}
/>
</span>
{curr >= done && (
<span>
{` Manuscript has been sent to PMC and must be withdrawn.`}
</span>
)}
</FlexP>
</React.Fragment>
</React.Fragment>
)}
<Exit close={close} />
</React.Fragment>
)
}
}
export default StatusEdit
......@@ -4,7 +4,7 @@ import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { Link, H3, Icon } from '@pubsweet/ui'
import { Loading, LoadingIcon, Table, Notification } from '../ui'
import { COUNT_MANUSCRIPTS } from './operations'
import { COUNT_MANUSCRIPTS, ALERT_MANUSCRIPTS } from './operations'
import DashboardBase from './DashboardBase'
const ListTable = styled(Table)`
......@@ -15,6 +15,12 @@ const ListTable = styled(Table)`
text-align: right;
}
`
const Alert = styled.small`
display: inline-flex;
align-items: center;
color: ${th('colorError')};
margin-left: calc(${th('gridUnit')} * 5);
`
const HelpdeskQueue = {
'needs submission QA': ['submitted'],
'needs XML QA': ['xml-qa'],
......@@ -39,7 +45,32 @@ const Completed = {
deleted: ['deleted'],
}
const QueueTable = ({ title, queue, data }) => {
const AlertQuery = ({ states, interval }) => (
<Query
fetchPolicy="cache-and-network"
query={ALERT_MANUSCRIPTS}
variables={{ states, interval }}
>
{({ data, loading }) => {
if (loading) {
return <LoadingIcon size={1.5} />
}
if (data.checkAge && data.checkAge.alert) {
return (
<Alert>
<Icon color="currentColor" size={2}>
alert-octagon
</Icon>{' '}
Manuscripts older than {interval}
</Alert>
)
}
return null
}}
</Query>
)
const QueueTable = ({ title, queue, data, alerts }) => {
let total = 0
const tableData = Object.keys(queue).map(label => {
const items = data.filter(s => queue[label].includes(s.type))
......@@ -64,11 +95,16 @@ const QueueTable = ({ title, queue, data }) => {
<tr key={row.label}>
<td>{row.count}</td>
<td>
{row.count ? (
<Link to={`/search?status=${row.status}`}>{row.label}</Link>
) : (
row.label
)}
<span style={{ display: 'inline-flex', alignItems: 'center' }}>
{row.count ? (
<Link to={`/search?status=${row.status}`}>{row.label}</Link>
) : (
row.label
)}
{alerts && (
<AlertQuery interval={alerts} states={row.states} />
)}
</span>
</td>
</tr>
))}
......@@ -130,6 +166,7 @@ const Dashboard = ({ currentUser }) => (
title="Submitter/Reviewer queue"
/>
<QueueTable
alerts="5 days"
data={data.countByStatus}
queue={TaggerQueue}
title="Tagger queue"
......
......@@ -24,6 +24,13 @@ export const COUNT_MANUSCRIPTS = gql`
}
}
`
export const ALERT_MANUSCRIPTS = gql`
query CheckAge($states: [String], $interval: String) {
checkAge(states: $states, interval: $interval) {
alert
}
}
`
export const GET_MANUSCRIPT = gql`
query SearchArticleIds($id: String!) {
searchArticleIds(id: $id) {
......
import React from 'react'
import { Field } from 'formik'
import { isEmpty } from 'lodash'
import {
ErrorText,
H1,
Link,
Button,
TextField,
Checkbox,
Icon,
} from '@pubsweet/ui'
import { th, lighten } from '@pubsweet/ui-toolkit'
import moment from 'moment'
import { ErrorText, H1, Link, Button, TextField, Checkbox } from '@pubsweet/ui'
import { th } from '@pubsweet/ui-toolkit'
import styled from 'styled-components'
import { notification } from 'config'
import { Page, Notification } from '../ui'
import SignInFooter from '../SignInFooter'
import Holiday from '../Holiday'
const Signup = styled.p`
margin-bottom: 0;
......@@ -31,19 +26,6 @@ const PasswordField = styled.div`
position: relative;
max-width: 500px;
`
const Christmas = styled(Notification)`
background-color: ${lighten('colorWarning', 70)};
div {
display: flex;
align-items: flex-start;
.text {
margin-left: 8px;
}
}
`
const Container = styled.form`
margin: 0 0;
max-width: 350px;
......@@ -80,24 +62,17 @@ class Login extends React.Component {
passwordReset = true,
location,
} = this.props
const { holiday } = notification
return (
<Page>
<H1>Sign in with your Europe PMC plus account</H1>
{notification.show && (
<Notification type={notification.type}>
{notification.message}
</Notification>
)}
{holiday.show && moment().isBefore(holiday.end) && <Holiday />}
{!isEmpty(errors) && <Notification type="error">{errors}</Notification>}
<Christmas>
<Icon color="green" size={2.5}>
gift
</Icon>
<Icon color="#a80202" size={2.5}>
gift
</Icon>
<span className="text">
Please note that the Helpdesk will be unavailable from Wednesday, 25
December to Tuesday 2 January. Any calls or e-mails will be saved
and responded to on our return. Happy Holidays from the Europe PMC
team.{' '}
</span>
</Christmas>
<Container onSubmit={handleSubmit}>
<Field component={EmailInput} name="email" />
<PasswordField>
......
......@@ -46,6 +46,7 @@ const SelectedLabel = styled.span`
const Selected = styled(Button)`
display: inline-flex;
align-items: center;
text-align: left;
margin: 0 ${th('gridUnit')} ${th('gridUnit')} 0;
${override('ui.SearchSelect.Selected')};
`
......
import { css } from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { th, lighten } from '@pubsweet/ui-toolkit'
export default {
Root: css`
......@@ -13,5 +13,10 @@ export default {
`,
Input: css`
margin-top: calc(${th('gridUnit')} / 2);
&:disabled + span,
&.disabled + span {
color: ${lighten('colorText', 75)};
}
`,
}
import { css } from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { th, lighten } from '@pubsweet/ui-toolkit'
export default {
Root: css`
......@@ -14,5 +14,10 @@ export default {
Input: css`
margin-right: ${th('gridUnit')};
height: calc(${th('gridUnit')} * 3);
&:disabled + span,
&.disabled + span {
color: ${lighten('colorText', 75)};
}
`,
}
......@@ -212,6 +212,7 @@ module.exports = {
'file',
'pageSize',
'states',
'notification',
],
// do we need this elife variable?
elife: {
......@@ -234,6 +235,17 @@ module.exports = {
},
},
states,
notification: {
show: false,
type: 'info',
message:
'The Europe PMC plus website has recently been upgraded. Please use the email address associated with your account to log in.',
holiday: {
show: true,
start: '2019-12-25T00:00:00Z',