diff --git a/packages/component-manuscript/package.json b/packages/component-manuscript/package.json index 110b73775085837e6e623990f86fde486d961003..2aab328d2fff84cd41d153d4b86c4663a54304e2 100644 --- a/packages/component-manuscript/package.json +++ b/packages/component-manuscript/package.json @@ -2,5 +2,17 @@ "name": "pubsweet-component-manuscript", "version": "0.0.1", "main": "src", - "license": "MIT" + "license": "MIT", + "dependencies": { + "prop-types": "^15.5.10", + "recompose": "^0.26.0", + "xpub-connect": "^0.0.10", + "xpub-selectors": "^0.0.5" + }, + "peerDependencies": { + "pubsweet-client": ">=2.1.0", + "react": ">=16", + "react-redux": ">=5.0.2", + "react-router-dom": ">=4.2.2" + } } diff --git a/packages/component-manuscript/src/atoms/index.js b/packages/component-manuscript/src/atoms/index.js new file mode 100644 index 0000000000000000000000000000000000000000..69667e73d9e7019faeb0e2117e287be51cfc5b61 --- /dev/null +++ b/packages/component-manuscript/src/atoms/index.js @@ -0,0 +1,60 @@ +import { th } from '@pubsweet/ui' +import styled, { css } from 'styled-components' + +const defaultText = css` + color: ${th('colorText')}; + font-family: ${th('fontReading')}; + font-size: ${th('fontSizeBaseSmall')}; +` +const Root = styled.div` + display: flex; + flex-direction: column; + margin: auto; + max-width: 60em; +` +const Title = styled.div` + ${defaultText}; + font-size: ${th('fontSizeBase')}; + color: ${th('colorPrimary')}; + margin-bottom: ${th('gridUnit')}; + text-align: left; +` + +const Header = styled.div` + align-items: center; + display: flex; + flex-direction: row; +` + +const BreadCrumbs = styled.div` + & span { + color: ${th('colorPrimary')}; + cursor: pointer; + font-size: ${th('fontSizeBase')}; + text-align: left; + + &:after { + content: '>'; + padding: 0 calc(${th('subGridUnit')}*2); + } + + &:last-child { + font-size: ${th('fontSizeBase')}; + font-weight: bold; + margin-right: ${th('subGridUnit')}; + &:after { + display: none; + } + } +` +const ManuscriptId = styled.div` + ${defaultText}; + color: ${th('colorPrimary')}; + font-size: ${th('fontSizeBase')}; + margin-right: 8px; + text-align: left; + text-transform: uppercase; + white-space: nowrap; +` + +export { Root, BreadCrumbs, Header, Title, ManuscriptId } diff --git a/packages/component-manuscript/src/components/Details.js b/packages/component-manuscript/src/components/Details.js deleted file mode 100644 index 1445da287ee2c392472c5a823cb2cf6bccaee221..0000000000000000000000000000000000000000 --- a/packages/component-manuscript/src/components/Details.js +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' -import { connect } from 'react-redux' -import { replace } from 'react-router-redux' -import { compose, lifecycle } from 'recompose' -import { selectCurrentUser } from 'xpub-selectors' - -import { parseSearchParams } from './utils' -import { reviewerDecision } from '../../../components-faraday/src/redux/reviewers' - -const Details = () => <div>eu sunt manuscript details</div> - -export default compose( - connect( - state => ({ - currentUser: selectCurrentUser(state), - }), - { reviewerDecision, replace }, - ), - lifecycle({ - componentDidMount() { - const { reviewerDecision, location, match, replace } = this.props - const collectionId = match.params.project - const { agree, invitationId } = parseSearchParams(location.search) - if (agree === 'true') { - reviewerDecision(invitationId, collectionId, true) - replace(location.pathname) - } - }, - }), -)(Details) diff --git a/packages/component-manuscript/src/components/ManuscriptLayout.js b/packages/component-manuscript/src/components/ManuscriptLayout.js new file mode 100644 index 0000000000000000000000000000000000000000..3935b6404e23fe80020d82f550e8106cf7e66973 --- /dev/null +++ b/packages/component-manuscript/src/components/ManuscriptLayout.js @@ -0,0 +1,23 @@ +import React from 'react' + +import { Root, BreadCrumbs, Title, Header, ManuscriptId } from '../atoms' + +const ManuscriptLayout = ({ + currentUser, + updateManuscript, + project, + version, +}) => ( + <Root> + <Header> + <BreadCrumbs> + <span>Dashboard</span> + <span>Manuscript Details</span> + </BreadCrumbs> + <ManuscriptId>{`- ID ${project.customId}`}</ManuscriptId> + </Header> + <Title dangerouslySetInnerHTML={{ __html: version.metadata.title }} /> + </Root> +) + +export default ManuscriptLayout diff --git a/packages/component-manuscript/src/components/ManuscriptPage.js b/packages/component-manuscript/src/components/ManuscriptPage.js new file mode 100644 index 0000000000000000000000000000000000000000..091de333fb7acdc054de6f5cb42156ec29ce8030 --- /dev/null +++ b/packages/component-manuscript/src/components/ManuscriptPage.js @@ -0,0 +1,52 @@ +import { connect } from 'react-redux' +import { actions } from 'pubsweet-client' +import { ConnectPage } from 'xpub-connect' +import { + selectCurrentUser, + selectCollection, + selectFragment, +} from 'xpub-selectors' +import { replace } from 'react-router-redux' +import { compose, lifecycle, withHandlers } from 'recompose' + +import ManuscriptLayout from './ManuscriptLayout' +import { parseSearchParams, redirectToError } from './utils' +import { reviewerDecision } from '../../../components-faraday/src/redux/reviewers' + +export default compose( + ConnectPage(({ match }) => [ + actions.getCollection({ id: match.params.project }), + actions.getFragment( + { id: match.params.project }, + { id: match.params.version }, + ), + ]), + connect( + (state, { match }) => ({ + currentUser: selectCurrentUser(state), + project: selectCollection(state, match.params.project), + version: selectFragment(state, match.params.version), + }), + { reviewerDecision, replace, updateVersion: actions.updateFragment }, + ), + lifecycle({ + componentDidMount() { + const { reviewerDecision, replace, location, match } = this.props + const collectionId = match.params.project + const { agree, invitationId } = parseSearchParams(location.search) + if (agree === 'true') { + replace(location.pathname) + reviewerDecision(invitationId, collectionId, true).catch( + redirectToError(replace), + ) + } + }, + }), + withHandlers({ + updateManuscript: ({ updateVersion, project, version }) => data => + updateVersion(project, { + id: version.id, + ...data, + }), + }), +)(ManuscriptLayout) diff --git a/packages/component-manuscript/src/components/index.js b/packages/component-manuscript/src/components/index.js index 08c9ae9088c3f3a1256b067abdab21339cf04098..e2e5b2d910d00c3a5fa12f0d7ebaf5363ea8d28f 100644 --- a/packages/component-manuscript/src/components/index.js +++ b/packages/component-manuscript/src/components/index.js @@ -1 +1 @@ -export { default as ManuscriptDetails } from './Details' +export { default as ManuscriptPage } from './ManuscriptPage' diff --git a/packages/component-manuscript/src/components/utils.js b/packages/component-manuscript/src/components/utils.js index 4f264402c54df9f81ce41908b74ce063b72d3e73..9aed3777b03c8afb27cc7556190c9f7d1130ae83 100644 --- a/packages/component-manuscript/src/components/utils.js +++ b/packages/component-manuscript/src/components/utils.js @@ -1,4 +1,5 @@ /* eslint-disable */ +import { get } from 'lodash' export const parseSearchParams = url => { const params = new URLSearchParams(url) @@ -8,3 +9,13 @@ export const parseSearchParams = url => { } return parsedObject } + +const alreadyAnswered = `You have already answered this invitation.` +export const redirectToError = redirectFn => err => { + const errorText = get(JSON.parse(err.response), 'error') + if (errorText.includes('has already been answered')) { + redirectFn('/error-page', alreadyAnswered) + } else { + redirectFn('/error-page', 'Oops! Something went wrong.') + } +} diff --git a/packages/components-faraday/src/components/Dashboard/DashboardCard.js b/packages/components-faraday/src/components/Dashboard/DashboardCard.js index 12c52f0f6965cbf72b563858f1220453ae3807f4..dfcffae0388b09700a6f4b1eeda3fe7202a171b1 100644 --- a/packages/components-faraday/src/components/Dashboard/DashboardCard.js +++ b/packages/components-faraday/src/components/Dashboard/DashboardCard.js @@ -92,7 +92,7 @@ const DashboardCard = ({ data-test="button-details" onClick={() => history.push( - `/projects/${project.id}/versions/${version.id}/manuscript`, + `/projects/${project.id}/versions/${version.id}/details`, ) } > diff --git a/packages/components-faraday/src/components/Dashboard/ReviewerDecision.js b/packages/components-faraday/src/components/Dashboard/ReviewerDecision.js index 882d92fafe1dc296174320b9c01f87660fb9836a..ac8a194935f891a00f10b28d39bd2d1eeac4e6c2 100644 --- a/packages/components-faraday/src/components/Dashboard/ReviewerDecision.js +++ b/packages/components-faraday/src/components/Dashboard/ReviewerDecision.js @@ -14,7 +14,7 @@ const ReviewerDecision = ({ showAcceptModal, showDeclineModal, ...rest }) => ( <div> <DecisionButton onClick={showDeclineModal}>Decline</DecisionButton> <DecisionButton onClick={showAcceptModal} primary> - Accept + Agree </DecisionButton> </div> ) @@ -49,6 +49,7 @@ export default compose( }) => () => { showModal({ title: 'Agree to review Manuscript?', + confirmText: 'Agree', onConfirm: () => { reviewerDecision(invitation.id, project.id, true).then( decisionSuccess, @@ -65,6 +66,7 @@ export default compose( }) => () => { showModal({ title: 'Decline to review Manuscript?', + confirmText: 'Decline', onConfirm: () => { reviewerDecision(invitation.id, project.id, false).then( decisionSuccess, diff --git a/packages/components-faraday/src/components/Invitations/ReviewerBreakdown.js b/packages/components-faraday/src/components/Invitations/ReviewerBreakdown.js index d75c2509f721ca4a3e92c817e9bf7371bd4eb5fb..7aa400252509bd60d01abca4765389e05cd34785 100644 --- a/packages/components-faraday/src/components/Invitations/ReviewerBreakdown.js +++ b/packages/components-faraday/src/components/Invitations/ReviewerBreakdown.js @@ -24,7 +24,7 @@ const reviewerReduce = (acc, r) => ({ }) const invitationReduce = (acc, i) => { - const key = i.isAccepted ? 'accepted' : 'refused' + const key = i.isAccepted ? 'accepted' : 'declined' return { ...acc, [key]: acc[key] + 1, @@ -60,7 +60,7 @@ export default compose( type === BREAKDOWN_TYPES.invitation ? invitationReduce : reviewerReduce, { accepted: 0, - refused: 0, + declined: 0, }, ) @@ -68,7 +68,7 @@ export default compose( <BreakdownText> <b>{values.length}</b> invited, <b> {report.accepted}</b> agreed, - <b> {report.refused}</b> declined + <b> {report.declined}</b> declined </BreakdownText> ) }, diff --git a/packages/components-faraday/src/components/Reviewers/InviteReviewers.js b/packages/components-faraday/src/components/Reviewers/InviteReviewers.js index bd8722dc070371a724e8511b9b1512349267b61a..600d264a07f23873b542267d0abc5ea2cc50949d 100644 --- a/packages/components-faraday/src/components/Reviewers/InviteReviewers.js +++ b/packages/components-faraday/src/components/Reviewers/InviteReviewers.js @@ -75,7 +75,7 @@ const InviteReviewersModal = compose( /> <Row> - {reviewers.length && ( + {reviewers.length > 0 && ( <Fragment> <Subtitle>Reviewers Info</Subtitle> <ReviewerBreakdown type="review" values={reviewers} /> diff --git a/packages/components-faraday/src/components/SignUp/FormItems.js b/packages/components-faraday/src/components/SignUp/FormItems.js index 26db2ecb4473a8ce079a968a741f98d19c30a7bb..22089ca2c007dc4899737333d059efa744b5f0bd 100644 --- a/packages/components-faraday/src/components/SignUp/FormItems.js +++ b/packages/components-faraday/src/components/SignUp/FormItems.js @@ -22,7 +22,7 @@ export const Title = styled.div` ` export const Subtitle = styled.div` font-family: ${th('fontReading')}; - font-size: ${th('fontSizeBaseSmall')}; + font-size: ${th('fontSizeBase')}; font-weight: normal; margin: 10px auto; text-align: center; @@ -56,8 +56,10 @@ export const Label = styled.div` font-size: ${th('fontSizeBaseSmall')}; text-transform: uppercase; ` -export const Err = styled.div` +export const Err = styled.span` color: ${th('colorError')}; + font-family: ${th('fontReading')}; + font-size: ${th('fontSizeBase')}; margin-top: calc(${th('gridUnit')}*-1); - text-align: left; + text-align: center; ` diff --git a/packages/components-faraday/src/components/SignUp/ReviewerInviteDecision.js b/packages/components-faraday/src/components/SignUp/ReviewerInviteDecision.js index c6db91452b20104118524357941cdaaf968326f9..8065fa65e51127e6eebe88734ec3f6e0866f3cc0 100644 --- a/packages/components-faraday/src/components/SignUp/ReviewerInviteDecision.js +++ b/packages/components-faraday/src/components/SignUp/ReviewerInviteDecision.js @@ -1,9 +1,9 @@ import React from 'react' import { get } from 'lodash' import { connect } from 'react-redux' -import { reduxForm, SubmissionError } from 'redux-form' -import { push } from 'react-router-redux' +import { push, replace } from 'react-router-redux' import { required, minChars } from 'xpub-validators' +import { reduxForm, SubmissionError } from 'redux-form' import { compose, withState, lifecycle } from 'recompose' import { loginUser } from 'pubsweet-component-login/actions' import { Button, ValidatedField, TextField } from '@pubsweet/ui' @@ -20,16 +20,18 @@ import { FormContainer, } from './FormItems' import { reviewerDecision, setReviewerPassword } from '../../redux/reviewers' +import { redirectToError } from '../utils' const agreeText = `You have been invited to review a manuscript on the Hindawi platform. Please set a password and proceed to the manuscript.` const declineText = `You have decline to work on a manuscript.` const min8Chars = minChars(8) const ReviewerInviteDecision = ({ - handleSubmit, + agree, error, + handleSubmit, reviewerEmail, - agree, + errorMessage, }) => ( <RootContainer> <Title>Hindawi Invitation</Title> @@ -61,18 +63,24 @@ const ReviewerInviteDecision = ({ </Row> </FormContainer> )} - {agree === 'false' && <div>nu i-a placut</div>} </RootContainer> ) export default compose( withState('reviewerEmail', 'setEmail', ''), - connect(null, { push, loginUser, setReviewerPassword, reviewerDecision }), + connect(null, { + push, + replace, + loginUser, + setReviewerPassword, + reviewerDecision, + }), lifecycle({ componentDidMount() { const { agree, email, + replace, setEmail, collectionId, invitationId, @@ -81,7 +89,9 @@ export default compose( setEmail(email) if (agree === 'false') { - reviewerDecision(invitationId, collectionId, false) + reviewerDecision(invitationId, collectionId, false).catch( + redirectToError(replace), + ) } }, }), diff --git a/packages/components-faraday/src/components/UIComponents/ErrorPage.js b/packages/components-faraday/src/components/UIComponents/ErrorPage.js new file mode 100644 index 0000000000000000000000000000000000000000..4022bd8a95394667db65658d8d43217a69d044c4 --- /dev/null +++ b/packages/components-faraday/src/components/UIComponents/ErrorPage.js @@ -0,0 +1,34 @@ +import React from 'react' +import { Button } from '@pubsweet/ui' +import styled from 'styled-components' + +const ErrorPage = ({ location: { state }, history }) => ( + <Root> + <Title>{state}</Title> + <Button onClick={() => history.push('/')} primary> + Go to Dashboard + </Button> + </Root> +) + +export default ErrorPage + +// #region styles +const Root = styled.div` + margin: 0 auto; + text-align: center; + width: 70vw; + color: ${({ theme }) => theme.colorText}; + + a { + color: ${({ theme }) => theme.colorText}; + } +` + +const Title = styled.div` + font-size: ${({ theme }) => theme.fontSizeHeading5}; + font-family: ${({ theme }) => theme.fontHeading}; + color: ${({ theme }) => theme.colorPrimary}; + margin: 10px auto; +` +// #endregion diff --git a/packages/components-faraday/src/components/UIComponents/index.js b/packages/components-faraday/src/components/UIComponents/index.js index 0f79f62b6f773549c5da460c041232e47820dfb3..0b3250328118937d535b00e72bb038c6c45fb863 100644 --- a/packages/components-faraday/src/components/UIComponents/index.js +++ b/packages/components-faraday/src/components/UIComponents/index.js @@ -1,3 +1,6 @@ export { default as Logo } from './Logo' export { default as Spinner } from './Spinner' +export { default as NotFound } from './NotFound' export { default as Dropdown } from './Dropdown' +export { default as ErrorPage } from './ErrorPage' +export { default as ConfirmationPage } from './ConfirmationPage' diff --git a/packages/components-faraday/src/components/utils.js b/packages/components-faraday/src/components/utils.js index a6d5812e37cd28eb845dc1bcef44b5e4a0eb9d63..54cd85c4ada4e7724bf918000267b8019a442a3c 100644 --- a/packages/components-faraday/src/components/utils.js +++ b/packages/components-faraday/src/components/utils.js @@ -75,3 +75,13 @@ const emailRegex = new RegExp( export const emailValidator = value => emailRegex.test(value) ? undefined : 'Invalid email' + +const alreadyAnswered = `You have already answered this invitation.` +export const redirectToError = redirectFn => err => { + const errorText = get(JSON.parse(err.response), 'error') + if (errorText.includes('has already been answered')) { + redirectFn('/error-page', alreadyAnswered) + } else { + redirectFn('/error-page', 'Oops! Something went wrong.') + } +} diff --git a/packages/components-faraday/src/redux/utils.js b/packages/components-faraday/src/redux/utils.js index 2f7f9cdca4a2f3b65a248f0ad1bb67d4fe300f04..14560f9260f6bfeec3f768e7a47b2c2147c1d111 100644 --- a/packages/components-faraday/src/redux/utils.js +++ b/packages/components-faraday/src/redux/utils.js @@ -4,7 +4,7 @@ export const orderReviewers = r => { return -1 case 'accepted': return 0 - case 'refused': + case 'declined': default: return 1 } diff --git a/packages/xpub-faraday/app/routes.js b/packages/xpub-faraday/app/routes.js index 4eca5f8fc8556beca13751bca253bd7b6e50e648..bf161391ab7e1d48ff2c7d2241929e5160a7e68c 100644 --- a/packages/xpub-faraday/app/routes.js +++ b/packages/xpub-faraday/app/routes.js @@ -6,10 +6,13 @@ import Login from 'pubsweet-component-login/LoginContainer' import Signup from 'pubsweet-component-signup/SignupContainer' import { Wizard } from 'pubsweet-component-wizard/src/components' -import { ManuscriptDetails } from 'pubsweet-component-manuscript/src/components' +import { ManuscriptPage } from 'pubsweet-component-manuscript/src/components' import DashboardPage from 'pubsweet-components-faraday/src/components/Dashboard' -import NotFound from 'pubsweet-components-faraday/src/components/UIComponents/NotFound' -import ConfirmationPage from 'pubsweet-components-faraday/src/components/UIComponents/ConfirmationPage' +import { + NotFound, + ConfirmationPage, + ErrorPage, +} from 'pubsweet-components-faraday/src/components/UIComponents/' import { AdminDashboard, AdminUsers, @@ -63,10 +66,12 @@ const Routes = () => ( <Route component={SignUpInvitationPage} exact path="/invite" /> <Route component={ReviewerSignUp} exact path="/invite-reviewer" /> <PrivateRoute - component={ManuscriptDetails} + component={ManuscriptPage} exact path="/projects/:project/versions/:version/details" /> + + <Route component={ErrorPage} exact path="/error-page" /> <Route component={NotFound} /> </Switch> </FaradayApp>