From b42707e5b3fa8733c1f6488124b1333f4da3ee56 Mon Sep 17 00:00:00 2001 From: Jure Triglav <juretriglav@gmail.com> Date: Mon, 17 Aug 2020 00:45:48 +0200 Subject: [PATCH] feat: very basic public-facing frontpage --- .../component-frontpage/src/Frontpage.js | 91 ++++++++++++++++++ .../src/ManuscriptDetails.js | 5 + .../component-frontpage/src/index.js | 4 + .../component-frontpage/src/queries.js | 51 ++++++++++ .../component-frontpage/src/style.js | 92 +++++++++++++++++++ app/routes.js | 7 ++ cypress/integration/005-publish_spec.js | 3 + server/model-manuscript/src/graphql.js | 21 ++++- 8 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 app/components/component-frontpage/src/Frontpage.js create mode 100644 app/components/component-frontpage/src/ManuscriptDetails.js create mode 100644 app/components/component-frontpage/src/index.js create mode 100644 app/components/component-frontpage/src/queries.js create mode 100644 app/components/component-frontpage/src/style.js diff --git a/app/components/component-frontpage/src/Frontpage.js b/app/components/component-frontpage/src/Frontpage.js new file mode 100644 index 0000000000..b53e5c03c7 --- /dev/null +++ b/app/components/component-frontpage/src/Frontpage.js @@ -0,0 +1,91 @@ +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>"{review.reviewComment?.content}"</p> + ), + )} + </div> + <div>Published: {manuscript.published}</div> + </SectionRow> + </SectionContent> + )) + ) : ( + <Placeholder>No submissions have been published yet.</Placeholder> + )} + </Container> + ) +} +export default Frontpage diff --git a/app/components/component-frontpage/src/ManuscriptDetails.js b/app/components/component-frontpage/src/ManuscriptDetails.js new file mode 100644 index 0000000000..6b5bfdab4e --- /dev/null +++ b/app/components/component-frontpage/src/ManuscriptDetails.js @@ -0,0 +1,5 @@ +import React from 'react' + +const ManuscriptDetails = () => <div>Nothing here yet.</div> + +export default ManuscriptDetails diff --git a/app/components/component-frontpage/src/index.js b/app/components/component-frontpage/src/index.js new file mode 100644 index 0000000000..28cae899c5 --- /dev/null +++ b/app/components/component-frontpage/src/index.js @@ -0,0 +1,4 @@ +import Frontpage from './Frontpage' +import ManuscriptDetails from './ManuscriptDetails' + +export { Frontpage, ManuscriptDetails } diff --git a/app/components/component-frontpage/src/queries.js b/app/components/component-frontpage/src/queries.js new file mode 100644 index 0000000000..a6ae1970d3 --- /dev/null +++ b/app/components/component-frontpage/src/queries.js @@ -0,0 +1,51 @@ +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 + } + } + } + } + } + `, +} diff --git a/app/components/component-frontpage/src/style.js b/app/components/component-frontpage/src/style.js new file mode 100644 index 0000000000..d92c9154fe --- /dev/null +++ b/app/components/component-frontpage/src/style.js @@ -0,0 +1,92 @@ +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; +` diff --git a/app/routes.js b/app/routes.js index 0cc488d3d4..f427249776 100644 --- a/app/routes.js +++ b/app/routes.js @@ -5,6 +5,11 @@ import Login from './components/component-login/src' import AdminPage from './components/AdminPage' +import { + Frontpage, + ManuscriptDetails, +} from './components/component-frontpage/src' + export default ( <Switch> {/* AdminPage has nested routes within */} @@ -12,5 +17,7 @@ export default ( <AdminPage /> </Route> <Route component={Login} path="/login" /> + <Route component={Frontpage} exact path="/" /> + <Route component={ManuscriptDetails} path="/:manuscriptId" /> </Switch> ) diff --git a/cypress/integration/005-publish_spec.js b/cypress/integration/005-publish_spec.js index 7ae8c4490e..97e56ca024 100644 --- a/cypress/integration/005-publish_spec.js +++ b/cypress/integration/005-publish_spec.js @@ -14,6 +14,9 @@ describe('Publishing a submission', () => { cy.contains('Dashboard').click() cy.contains('Accepted & Published') + cy.visit('/') + cy.contains('My URL submission') + cy.task('dump', 'published_submission') }) }) diff --git a/server/model-manuscript/src/graphql.js b/server/model-manuscript/src/graphql.js index f95dbb4cc0..775e3f7ced 100644 --- a/server/model-manuscript/src/graphql.js +++ b/server/model-manuscript/src/graphql.js @@ -1,6 +1,6 @@ // const merge = require('lodash/merge') const detailsForURLResolver = require('./detailsForURLResolver') -const { ref } = require('objection') +const { ref, raw } = require('objection') const resolvers = { Mutation: { @@ -244,6 +244,24 @@ const resolvers = { async manuscripts(_, { where }, ctx) { return ctx.models.Manuscript.query().eager('[teams, reviews]') }, + async publishedManuscripts(_, { offset, limit }, ctx) { + const query = ctx.models.Manuscript.query() + .where(raw('published IS NOT NULL')) + .eager('[reviews.[comments], files, submitter]') + const totalCount = await query.resultSize() + if (limit) { + query.limit(limit) + } + + if (offset) { + query.offset(offset) + } + const manuscripts = await query + return { + totalCount, + manuscripts, + } + }, async paginatedManuscripts(_, { sort, offset, limit, filter }, ctx) { const query = ctx.models.Manuscript.query().eager('submitter') @@ -321,6 +339,7 @@ const typeDefs = ` manuscripts: [Manuscript]! paginatedManuscripts(sort: String, offset: Int, limit: Int, filter: ManuscriptsFilter): PaginatedManuscripts detailsForURL(url: String!): URLMetadata + publishedManuscripts(offset: Int, limit: Int): PaginatedManuscripts } type URLMetadata { -- GitLab