Commit 31d75ddc authored by Jure's avatar Jure
Browse files

Merge branch 'publishing' into 'main'

feat: initial publishing capabilities

Closes #12

See merge request !58
parents d2226d44 b42707e5
Pipeline #14001 passed with stages
in 17 minutes and 34 seconds
......@@ -58,7 +58,11 @@ const EditorItem = ({ version }) => (
// <Authorize object={[version]} operation="can view my manuscripts section">
<>
<Item>
<StatusBadge minimal status={version.status} />
<StatusBadge
minimal
published={version.published}
status={version.status}
/>
<Meta>
<MetadataStreamLined
streamlinedReview={getDeclarationsObject(
......
......@@ -32,7 +32,11 @@ const OwnerItem = ({ version, journals, deleteManuscript }) => {
<Item>
<div>
{' '}
<StatusBadge minimal status={version.status} />
<StatusBadge
minimal
published={version.published}
status={version.status}
/>
<VersionTitle version={version} />
</div>
{actions}
......
......@@ -60,6 +60,7 @@ export default {
date
}
}
published
_currentRoles @client
}
}
......
import React, { useContext } from 'react'
import { useQuery } from '@apollo/client'
import { JournalContext } from '../../xpub-journal/src'
import queries from './queries'
import { Container, Placeholder } from './style'
import {
Spinner,
SectionHeader,
Title,
SectionRow,
SectionContent,
Heading,
HeadingWithAction,
} from '../../shared'
const Frontpage = ({ history, ...props }) => {
const { loading, data, error } = useQuery(queries.frontpage)
const journal = useContext(JournalContext)
if (loading) return <Spinner />
if (error) return JSON.stringify(error)
const frontpage = (data.publishedManuscripts?.manuscripts || []).map(m => ({
...m,
submission: JSON.parse(m.submission),
}))
return (
<Container>
<HeadingWithAction>
<Heading>Recent publications in {journal.metadata.name}</Heading>
</HeadingWithAction>
{frontpage.length > 0 ? (
frontpage.map(manuscript => (
<SectionContent>
<SectionHeader>
<Title>{manuscript.meta.title}</Title>
</SectionHeader>
<SectionRow key={`manuscript-${manuscript.id}`}>
<p>
{manuscript.submitter.defaultIdentity.name} (
{manuscript.submission.affiliation})
</p>
<div>
Submitted files:
{manuscript.files.map(file => (
<p>
<a
href={file.url}
rel="noopener noreferrer"
target="_blank"
>
{file.filename}
</a>
</p>
))}
</div>
<div>
Submitted research objects:
{manuscript.submission.links.map(link => (
<p>
<a
href={link.url}
rel="noopener noreferrer"
target="_blank"
>
{link.url}
</a>
</p>
))}
</div>
<div>
Reviews:
{manuscript.reviews.map(
review =>
review.reviewComment && (
<p>&quot;{review.reviewComment?.content}&quot;</p>
),
)}
</div>
<div>Published: {manuscript.published}</div>
</SectionRow>
</SectionContent>
))
) : (
<Placeholder>No submissions have been published yet.</Placeholder>
)}
</Container>
)
}
export default Frontpage
import React from 'react'
const ManuscriptDetails = () => <div>Nothing here yet.</div>
export default ManuscriptDetails
import Frontpage from './Frontpage'
import ManuscriptDetails from './ManuscriptDetails'
export { Frontpage, ManuscriptDetails }
import gql from 'graphql-tag'
export default {
frontpage: gql`
{
publishedManuscripts {
totalCount
manuscripts {
id
reviews {
id
open
recommendation
reviewComment {
content
}
created
isDecision
user {
id
username
}
}
status
files {
id
url
filename
}
meta {
manuscriptId
title
articleSections
articleType
history {
type
date
}
}
published
submission
submitter {
defaultIdentity {
name
}
}
}
}
}
`,
}
import styled from 'styled-components'
import { th, grid } from '@pubsweet/ui-toolkit'
export { Section, Content } from '../../shared'
const Actions = styled.div``
export const Container = styled.div`
background: ${th('colorBackgroundHue')};
padding: ${grid(2)};
min-height: 100vh;
}`
const ActionContainer = styled.div`
display: inline-block;
`
export { Actions, ActionContainer }
const Item = styled.div`
display: grid;
grid-template-columns: 1fr auto;
margin-bottom: calc(${th('gridUnit') * 4});
`
const Header = styled.div`
align-items: baseline;
display: flex;
justify-content: space-between;
text-transform: uppercase;
`
const Body = styled.div`
align-items: space-between;
display: flex;
justify-content: space-between;
margin-bottom: calc(${th('gridUnit')} * 4);
padding-left: 1.5em;
& > div:last-child {
flex-shrink: 0;
}
`
const Divider = styled.span.attrs(props => ({
children: ` ${props.separator} `,
}))`
color: ${th('colorFurniture')};
white-space: pre;
`
export { Item, Header, Body, Divider }
const Links = styled.div`
align-items: flex-end;
display: flex;
justify-content: bottom;
`
const LinkContainer = styled.div`
font-size: ${th('fontSizeBaseSmall')};
line-height: ${th('lineHeightBaseSmall')};
`
export { Links, LinkContainer }
const Page = styled.div`
padding: ${grid(2)};
`
const Heading = styled.div`
color: ${th('colorPrimary')};
font-family: ${th('fontReading')};
font-size: ${th('fontSizeHeading3')};
line-height: ${th('lineHeightHeading3')};
`
export { Page, Heading }
export const HeadingWithAction = styled.div`
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
`
export { StatusBadge } from '../../shared'
export const Placeholder = styled.div`
display: grid;
place-items: center;
color: ${th('colorTextPlaceholder')};
height: 100%;
padding: 4em;
`
......@@ -11,165 +11,22 @@ import AssignEditor from './assignEditors/AssignEditor'
import ReviewMetadata from './metadata/ReviewMetadata'
import Decision from './decision/Decision'
import EditorSection from './decision/EditorSection'
import Publish from './Publish'
import { AdminSection, Columns, Manuscript, Chat } from './style'
import { Spinner } from '../../../shared'
import MessageContainer from '../../../component-chat/src'
import { query, updateReviewMutation, makeDecisionMutation } from './queries'
const addEditor = (manuscript, label) => ({
content: <EditorSection manuscript={manuscript} />,
key: `editor_${manuscript.id}`,
label,
})
const commentFields = `
id
commentType
content
files {
id
created
label
filename
fileType
mimeType
size
url
}
`
const reviewFields = `
id
created
updated
decisionComment {
${commentFields}
}
reviewComment {
${commentFields}
}
confidentialComment {
${commentFields}
}
isDecision
recommendation
user {
id
username
}
`
const fragmentFields = `
id
created
files {
id
created
label
filename
fileType
mimeType
size
url
}
reviews {
${reviewFields}
}
decision
teams {
id
name
role
manuscript {
id
}
members {
id
user {
id
username
}
status
}
}
status
meta {
manuscriptId
title
source
abstract
declarations {
openData
openPeerReview
preregistered
previouslySubmitted
researchNexus
streamlinedReview
}
articleSections
articleType
history {
type
date
}
notes {
notesType
content
}
keywords
}
submission
suggestions {
reviewers {
opposed
suggested
}
editors {
opposed
suggested
}
}
`
const query = gql`
query($id: ID!) {
currentUser {
id
username
admin
}
manuscript(id: $id) {
${fragmentFields}
manuscriptVersions {
${fragmentFields}
}
channels {
id
type
topic
}
}
}
`
const updateReviewMutationQuery = gql`
mutation($id: ID, $input: ReviewInput) {
updateReview(id: $id, input: $input) {
${reviewFields}
}
}
`
const makeDecisionMutation = gql`
mutation($id: ID!, $decision: String) {
makeDecision(id: $id, decision: $decision) {
id
${fragmentFields}
}
}
`
const dateLabel = date => moment(date).format('YYYY-MM-DD')
const decisionSections = ({
......@@ -226,6 +83,9 @@ const decisionSections = ({
uploadFile={uploadFile}
/>
</AdminSection>
<AdminSection>
<Publish manuscript={manuscript} />
</AdminSection>
</>
),
key: manuscript.id,
......@@ -293,7 +153,7 @@ const decisionSections = ({
const DecisionPage = ({ match }) => {
// Hooks from the old world
const [makeDecision] = useMutation(makeDecisionMutation)
const [updateReviewMutation] = useMutation(updateReviewMutationQuery)
const [doUpdateReview] = useMutation(updateReviewMutation)
const { loading, error, data } = useQuery(query, {
variables: {
......@@ -342,7 +202,7 @@ const DecisionPage = ({ match }) => {
},
}
return updateReviewMutation({
return doUpdateReview({
variables: {
id: existingReview.current.id || undefined,
input: reviewData,
......
import React from 'react'
import { useMutation } from '@apollo/client'
import { Button } from '@pubsweet/ui'
import { publishManuscriptMutation } from './queries'
import {
Container,
Title,
SectionHeader,
SectionRowGrid,
SectionActionInfo,
SectionAction,
} from './style'
const Publish = ({ manuscript }) => {
// Hooks from the old world
const [publishManuscript] = useMutation(publishManuscriptMutation)
const notAccepted = manuscript.status !== 'accepted'
return (
<Container>
<SectionHeader>
<Title>Publishing</Title>
</SectionHeader>
<SectionRowGrid>
<SectionActionInfo>
{manuscript.published &&
`This submission was published on ${manuscript.published}`}
{!manuscript.published &&
notAccepted &&
`You can only publish accepted submissions.`}
{!manuscript.published &&
!notAccepted &&
`Publishing will add a new entry on the public website and can not be undone.`}
</SectionActionInfo>
<SectionAction>
<Button
disabled={manuscript.published || notAccepted}
onClick={() =>
publishManuscript({ variables: { id: manuscript.id } })
}
primary
>
Publish
</Button>
</SectionAction>
</SectionRowGrid>
</Container>
)
}
export default Publish
......@@ -25,6 +25,7 @@ import {
FormStatus,
ErrorText,
ErrorWrap,
RecommendationInputContainer,
} from '../style'
// import Wax from '../../../../wax-collab/src/Editoria'
......@@ -113,7 +114,7 @@ const RecommendationInput = ({
}) => {
const journal = useContext(JournalContext)
return (
<div>
<RecommendationInputContainer>
<RadioGroup
{...field}
inline
......@@ -125,7 +126,7 @@ const RecommendationInput = ({
value={field.value === '' ? null : field.value}
/>
<ErrorMessage name="recommendation" />
</div>
</RecommendationInputContainer>
)
}
......
import { gql } from '@apollo/client'
const commentFields = `
id
commentType
content
files {
id
created
label
filename
fileType
mimeType
size
url
}
`
const reviewFields = `
id
created
updated
decisionComment {
${commentFields}
}
reviewComment {
${commentFields}
}
confidentialComment {