Commit 34377a52 authored by Aanand Prasad's avatar Aanand Prasad

Convert dashboard to styled components

parent 34e04eb1
Pipeline #4700 failed with stages
in 2 minutes and 58 seconds
......@@ -23,6 +23,7 @@
"react-router-dom": "^4.2.2",
"recompose": "^0.26.0",
"redux": "^3.6.0",
"styled-components": "^2.4.0",
"xpub-connect": "^0.0.2",
"xpub-journal": "^0.0.2",
"xpub-selectors": "^0.0.2",
......
import React from 'react'
import classes from './Dashboard.local.scss'
import { Page, Section, Heading, UploadContainer } from './molecules/Page'
import UploadManuscript from './UploadManuscript'
import withVersion from './withVersion'
import EditorItem from './sections/EditorItem'
......@@ -19,27 +21,25 @@ const Dashboard = ({
reviewerResponse,
uploadManuscript,
}) => (
<div className={classes.root}>
<div className={classes.upload}>
<Page>
<UploadContainer>
<UploadManuscript
conversion={conversion}
uploadManuscript={uploadManuscript}
/>
</div>
</UploadContainer>
{!dashboard.owner.length &&
!dashboard.reviewer.length &&
!dashboard.editor.length && (
<div className={classes.section}>
<div className={classes.empty}>
Nothing to do at the moment. Please upload a document.
</div>
</div>
<UploadContainer>
Nothing to do at the moment. Please upload a document.
</UploadContainer>
)}
{!!dashboard.owner.length && (
<div className={classes.section}>
<div className={classes.heading}>My Submissions</div>
<Section>
<Heading>My Submissions</Heading>
{dashboard.owner.map(project => (
<OwnerItemWithVersion
deleteProject={() =>
......@@ -52,12 +52,12 @@ const Dashboard = ({
project={project}
/>
))}
</div>
</Section>
)}
{!!dashboard.reviewer.length && (
<div className={classes.section}>
<div className={classes.heading}>To review</div>
<Section>
<Heading>To review</Heading>
{dashboard.reviewer.map(project => (
<ReviewerItemWithVersion
currentUser={currentUser}
......@@ -66,12 +66,12 @@ const Dashboard = ({
reviewerResponse={reviewerResponse}
/>
))}
</div>
</Section>
)}
{!!dashboard.editor.length && (
<div className={classes.section}>
<div className={classes.heading}>My Manuscripts</div>
<Section>
<Heading>My Manuscripts</Heading>
{dashboard.editor.map(project => (
<EditorItemWithVersion
AssignEditor={AssignEditor}
......@@ -79,9 +79,9 @@ const Dashboard = ({
project={project}
/>
))}
</div>
</Section>
)}
</div>
</Page>
)
export default Dashboard
.root {
margin: auto;
max-width: 60em;
}
.upload {
display: flex;
justify-content: center;
}
.section {
margin: 3em 0;
}
.heading {
color: var(--color-primary);
font-family: Vollkorn, serif;
font-size: 1.6em;
// margin: 4em 0 2em;
margin: 1em 0 1em;
text-transform: uppercase;
}
.empty {
display: flex;
justify-content: center;
}
.section:not(:last-of-type) {
margin-bottom: 70px;
}
a {
color: var(--color-primary);
}
import React from 'react'
import classes from './EmptySubmissions.local.scss'
const EmptySubmissions = () => (
<div className={classes.root}>
<div>You haven&apos;t submitted any manuscripts yet.</div>
</div>
)
export default EmptySubmissions
.root {
color: grey;
font-family: var(--font-author);
font-size: 1.2em;
font-style: italic;
text-align: center;
}
A message that is displayed when the current user has no submissions.
```js
<EmptySubmissions/>
```
import React from 'react'
import { Link } from 'react-router-dom'
import { Link } from '@pubsweet/ui'
const projectUrl = ({ project, version, page, id }) => {
const parts = []
......
import React from 'react'
import styled from 'styled-components'
import { compose, withProps } from 'recompose'
import { groupBy } from 'lodash'
import { withJournal } from 'xpub-journal'
import { Badge } from '@pubsweet/ui'
import classes from './Reviews.local.scss'
const Root = styled.div`
display: inline-flex;
justify-content: flex-end;
margin-bottom: 0.6em;
margin-top: 0.3em;
padding-left: 1.5em;
font-family: var(--font-reviewer);
font-size: 0.9em;
`
const BadgeContainer = styled.span`
&:not(:last-child) {
margin-right: 10px;
}
`
const Reviews = ({ reviews, journal }) => (
<div className={classes.root}>
<Root>
{journal.reviewStatus.map(status => (
<span className={classes.badge} key={status}>
<BadgeContainer key={status}>
<Badge
count={reviews[status] ? reviews[status].length : 0}
label={status}
/>
</span>
</BadgeContainer>
))}
</div>
</Root>
)
export default compose(
......
.badge:not(:last-child) {
margin-right: 10px;
}
.root {
font-family: var(--font-reviewer);
}
import React from 'react'
import classes from './Status.local.css'
import styled from 'styled-components'
// TODO: move labels to journal config
......@@ -13,8 +13,10 @@ const labels = {
revising: 'Under Revision',
}
const Status = ({ status }) => (
<div className={classes.root}>{labels[status] || 'Unsubmitted'}</div>
)
const Root = styled.div`
color: var(--color-primary);
`
const Status = ({ status }) => <Root>{labels[status] || 'Unsubmitted'}</Root>
export default Status
.root {
color: var(--color-primary);
text-transform: uppercase;
}
import React, { Component } from 'react'
import styled, { keyframes } from 'styled-components'
import Dropzone from 'react-dropzone'
import classnames from 'classnames'
import { Icon } from '@pubsweet/ui'
import classes from './UploadManuscript.local.scss'
const isIdle = conversion => !(conversion.converting || conversion.error)
const StyledDropzone = styled(Dropzone)`
border: none;
cursor: pointer;
display: inline-block;
`
const StatusIcon = ({ children }) => (
<Icon color="var(--color-primary)">{children}</Icon>
)
const Status = styled.div`
align-items: center;
color: var(--color-primary);
display: inline-flex;
`
const StatusIdle = Status.extend.attrs({
children: () => <StatusIcon>plus_circle</StatusIcon>,
})``
const spin = keyframes`
0% {
transform: rotate(0deg);
transform-origin: 50% 50%;
}
100% {
transform: rotate(360deg);
transform-origin: 50% 50%;
}
`
const StatusConverting = Status.extend.attrs({
children: () => <StatusIcon>plus_circle</StatusIcon>,
})`
&:hover {
cursor: wait;
}
line {
stroke-linejoin: round;
}
circle {
animation: ${spin} 2s infinite linear;
stroke-dasharray: 16;
stroke-dashoffset: 0;
stroke-linejoin: round;
}
`
const StatusError = Status.extend.attrs({
children: () => <StatusIcon>plus_circle</StatusIcon>,
})`
color: var(--color-danger);
font-size: 1.5em;
font-style: italic;
font-weight: 400;
.icon circle {
display: none;
}
.icon line {
stroke: var(--color-danger);
transform: rotate(45deg) scale(2.8);
transform-origin: 50% 50%;
}
`
const dash = keyframes`
from {
stroke-dashoffset: -100;
}
to {
stroke-dashoffset: 0;
}
`
const StatusCompleted = Status.extend.attrs({
children: () => <StatusIcon>check_circle</StatusIcon>,
})`
polyline {
animation: ${dash} 1.35s linear;
stroke-dasharray: 100;
stroke-dashoffset: 0;
}
path {
animation: ${dash} 0.75s linear;
stroke-dasharray: 100;
stroke-dashoffset: 0;
}
`
const Root = styled.div`
display: flex;
font-weight: 200;
padding-bottom: 10px;
padding-top: 10px;
&:hover ${StatusIdle} {
circle {
fill: var(--color-primary);
stroke: var(--color-primary);
}
line {
stroke: white;
}
}
`
const Main = styled.div`
margin-left: 10px;
`
const Error = styled.div`
color: var(--color-danger);
font-size: 1.5em;
font-style: italic;
font-weight: 400;
`
const Info = styled.div`
color: var(--color-primary);
font-size: 2em;
font-weight: 400;
text-transform: uppercase;
`
class UploadManuscript extends Component {
constructor(props) {
......@@ -45,42 +174,46 @@ class UploadManuscript extends Component {
}, 3000)
}
get status() {
if (this.state.completed) {
return 'completed'
}
if (this.state.error) {
return 'error'
}
if (this.props.conversion.converting) {
return 'converting'
}
return 'idle'
}
render() {
const { uploadManuscript, conversion } = this.props
return (
<Dropzone
<StyledDropzone
accept="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
className={classes.dropzone}
onDrop={uploadManuscript}
>
<div className={classes.root}>
<div
className={classnames({
[classes.idle]: isIdle(conversion),
[classes.converting]: conversion.converting,
[classes.error]: this.state.error,
})}
>
<span className={classes.icon}>
<Icon color="var(--color-primary)">
{this.state.completed ? 'check_circle' : 'plus_circle'}
</Icon>
</span>
</div>
<div className={classes.main}>
<Root>
{this.status === 'completed' && <StatusCompleted />}
{this.status === 'error' && <StatusError />}
{this.status === 'converting' && <StatusConverting />}
{this.status === 'idle' && <StatusIdle />}
<Main>
{this.state.error ? (
<div className={classes.error}>{conversion.error.message}</div>
<Error>{conversion.error.message}</Error>
) : (
<div className={classes.info}>
<Info>
{this.state.completed
? 'Submission created'
: 'Create submission'}
</div>
</Info>
)}
</div>
</div>
</Dropzone>
</Main>
</Root>
</StyledDropzone>
)
}
}
......
.dropzone {
border: none;
cursor: pointer;
display: inline-block;
}
.root {
display: flex;
font-weight: 200;
padding-bottom: 10px;
padding-top: 10px;
}
.icon {
align-items: center;
color: var(--color-primary);
display: inline-flex;
height: 100%;
}
.converting {
&:hover {
cursor: wait;
}
line {
stroke-linejoin: round;
}
circle {
animation: spin 2s infinite linear;
stroke-dasharray: 16;
stroke-dashoffset: 0;
stroke-linejoin: round;
}
}
.main {
margin-left: 10px;
}
.info {
color: var(--color-primary);
font-size: 2em;
font-weight: 400;
text-transform: uppercase;
}
.error {
color: var(--color-danger);
font-size: 1.5em;
font-style: italic;
font-weight: 400;
.icon circle {
display: none;
}
.icon line {
stroke: var(--color-danger);
transform: rotate(45deg) scale(2.8);
transform-origin: 50% 50%;
}
}
.root:hover .idle {
circle {
fill: var(--color-primary);
stroke: var(--color-primary);
}
line {
stroke: white;
}
}
.complete {
.icon {
polyline {
animation: dash 1.35s linear;
stroke-dasharray: 100;
stroke-dashoffset: 0;
}
path {
animation: dash 0.75s linear;
stroke-dasharray: 100;
stroke-dashoffset: 0;
}
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
transform-origin: 50% 50%;
}
100% {
transform: rotate(360deg);
transform-origin: 50% 50%;
}
}
@keyframes blink {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes dash {
from {
stroke-dashoffset: -100;
}
to {
stroke-dashoffset: 0;
}
}
import styled from 'styled-components'
const Meta = styled.div`
display: flex;
flex-wrap: nowrap;
font-size: var(--font-size-base-small);
white-space: nowrap;
`
export default Meta
import styled from 'styled-components'
const Actions = styled.div``
const ActionContainer = styled.div`
display: inline-block;
`
export { Actions, ActionContainer }
import styled from 'styled-components'
const Item = styled.div`
margin-bottom: var(--grid-unit);
`
const Header = styled.div`
align-items: baseline;
display: flex;
justify-content: space-between;
`
const Body = styled.div`
align-items: flex-end;
display: flex;
justify-content: flex-end;
margin-bottom: var(--grid-unit);
padding-left: 1.5em;
`
const Divider = styled.span.attrs({
children: props => ` ${props.separator} `,
})`
color: var(--color-furniture);
white-space: pre;
`
export { Item, Header, Body, Divider }
import styled from 'styled-components'
const Links = styled.div`
align-items: flex-end;
display: flex;
justify-content: bottom;
`
const LinkContainer = styled.div`
font-size: var(--font-size-base-small);
`
export { Links, LinkContainer }
import styled from 'styled-components'
const Page = styled.div`
margin: auto;
max-width: 60em;
`
const Section = styled.div`
margin: var(--grid-unit) 0;
&:not(:last-of-type) {
margin-bottom: calc(var(--grid-unit) * 2);
}
`
const Heading = styled.div`
color: var(--color-primary);
font-family: var(--font-heading);
font-size: var(--font-size-heading-3);
margin: var(--grid-unit) 0;
`
const UploadContainer = styled.div`
display: flex;
justify-content: center;
`
export { Page, Section, Heading, UploadContainer }
import styled from 'styled-components'
const Roles = styled.div`
display: flex;
font-size: 0.8em;
margin-bottom: 0.6em;
margin-left: 0.5em;
margin-top: 0;
padding-left: 1.5em;
text-transform: uppercase;
`