diff --git a/app/components/component-submit/src/components/Confirm.js b/app/components/component-submit/src/components/Confirm.js index 95413c68ba357ebce6f3ac1702dc4f303ce7ab38..af75a9c8c660f5750583a8c4b040ba7cdc09cc30 100644 --- a/app/components/component-submit/src/components/Confirm.js +++ b/app/components/component-submit/src/components/Confirm.js @@ -28,22 +28,36 @@ const createMarkup = encodedHtml => ({ __html: unescape(encodedHtml), }) -const Confirm = ({ toggleConfirming, form, submitSubmission }) => ( - <Wrapper> - <article> - <Heading1 dangerouslySetInnerHTML={createMarkup(form.popuptitle)} /> - <Paragraph - dangerouslySetInnerHTML={createMarkup(form.popupdescription)} - /> - <Button onClick={submitSubmission} primary type="submit"> - Submit your manuscript - </Button> - <Divider> or </Divider> - <PlainButton onClick={toggleConfirming}> - get back to your submission - </PlainButton> - </article> - </Wrapper> -) +const Confirm = ({ toggleConfirming, form, submit, errors }) => { + return ( + <Wrapper> + <article> + {Object.keys(errors).length > 0 ? ( + <> + <Heading1>Errors in your submission</Heading1> + <Paragraph> + There are errors in your submission, please correct the following:{' '} + {JSON.stringify(errors)} + </Paragraph> + </> + ) : ( + <> + <Heading1 dangerouslySetInnerHTML={createMarkup(form.popuptitle)} /> + <Paragraph + dangerouslySetInnerHTML={createMarkup(form.popupdescription)} + /> + <Button onClick={submit} primary type="submit"> + Submit your manuscript + </Button> + <Divider> or </Divider> + </> + )} + <PlainButton onClick={toggleConfirming}> + get back to your submission + </PlainButton> + </article> + </Wrapper> + ) +} export default Confirm diff --git a/app/components/component-submit/src/components/Confirm.md b/app/components/component-submit/src/components/Confirm.md deleted file mode 100644 index a5773b2ca69e1727b22fa61800b0969d2ed5ae51..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/Confirm.md +++ /dev/null @@ -1,14 +0,0 @@ -A series of confirmation paragraphs that the user must read and agree to before confirming the submission. - -The user can confirm submission using the primary button, or return to the submission using a link. - -```js -const form = { - haspopup: 'true', - id: 'submit', - name: 'Submission information', - popupdescription: faker.lorem.sentences(50), - popuptitle: faker.lorem.words(3), -} -;<Confirm form={form} /> -``` diff --git a/app/components/component-submit/src/components/CurrentVersion.md b/app/components/component-submit/src/components/CurrentVersion.md deleted file mode 100644 index 2dde98ad20be1788b814f5917e4e50725d277746..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/CurrentVersion.md +++ /dev/null @@ -1,62 +0,0 @@ -A form for entering information about the submission. - -```js -const journal = { - id: faker.random.uuid(), -} - -const manuscript = { - id: faker.random.uuid(), - meta: { - title: faker.lorem.sentence(25), - abstract: faker.lorem.sentence(100), - articleType: 'original-research', - declarations: { - openData: 'yes', - openPeerReview: 'no', - preregistered: 'yes', - previouslySubmitted: 'yes', - researchNexus: 'no', - streamlinedReview: 'no', - }, - }, - suggestions: { - reviewers: { - opposed: faker.name.findName(), - }, - }, - reviews: [ - { - comments: { content: 'this needs review' }, - created: 'Thu Oct 11 2018', - open: false, - recommendation: '', - user: { identities: [] }, - }, - ], -} - -const forms = { - children: [ - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.openData', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.openPeerReview', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.previouslySubmitted', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.researchNexus', - }, - ], -} -;<div style={{ position: 'relative', paddingRight: 100 }}> - <CurrentVersion forms={forms} manuscript={manuscript} journal={journal} /> -</div> -``` diff --git a/app/components/component-submit/src/components/Declarations.md b/app/components/component-submit/src/components/Declarations.md deleted file mode 100644 index 8bf81ab7e5ee43e83633583a3cd003888e903e24..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/Declarations.md +++ /dev/null @@ -1,57 +0,0 @@ -A list of questions that must be answered before submission. The questions are -configured via the journal config on the context. - -```js -const forms = { - children: [ - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.openData', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.openPeerReview', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.previouslySubmitted', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.researchNexus', - }, - ], -} - -const manuscript = { - id: faker.random.uuid(), - meta: { - title: faker.lorem.sentence(25), - abstract: faker.lorem.sentence(100), - articleType: 'original-research', - declarations: { - openData: 'yes', - openPeerReview: 'no', - preregistered: 'yes', - previouslySubmitted: 'yes', - researchNexus: 'no', - streamlinedReview: 'no', - }, - }, - suggestions: { - reviewers: { - opposed: faker.name.findName(), - }, - }, - reviews: [ - { - comments: { content: 'this needs review' }, - created: 'Thu Oct 11 2018', - open: false, - recommendation: '', - user: { identities: [] }, - }, - ], -} -;<Declarations forms={forms} manuscript={manuscript} /> -``` diff --git a/app/components/component-submit/src/components/FormTemplate.js b/app/components/component-submit/src/components/FormTemplate.js index 6baed800ae0a1e2957ab05330ddd6472879cf433..92a2cc90ec7a11562350e62bbb43b99feb1a160b 100644 --- a/app/components/component-submit/src/components/FormTemplate.js +++ b/app/components/component-submit/src/components/FormTemplate.js @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components' import { unescape, groupBy, isArray, get, set, cloneDeep } from 'lodash' -import { FieldArray } from 'formik' +import { FieldArray, useFormikContext } from 'formik' import * as uiComponents from '@pubsweet/ui' import * as validators from 'xpub-validators' import { AbstractEditor } from 'xpub-edit' @@ -11,6 +11,19 @@ import AuthorsInput from './AuthorsInput' import Supplementary from './Supplementary' import Confirm from './Confirm' +// TODO: https://github.com/formium/formik/issues/146#issuecomment-474775723 +// const useFocusOnError = ({ fieldRef, name }) => { +// const formik = useFormikContext() +// const prevSubmitCountRef = React.useRef(formik.submitCount) +// const firstErrorKey = Object.keys(formik.errors)[0] +// React.useEffect(() => { +// if (prevSubmitCountRef.current !== formik.submitCount && !formik.isValid) { +// if (fieldRef.current && firstErrorKey === name) fieldRef.current.focus() +// } +// prevSubmitCountRef.current = formik.submitCount +// }, [formik.submitCount, formik.isValid, firstErrorKey]) +// } + // const Wrapper = styled.div` // font-family: ${th('fontInterface')}; // line-height: 1.3; @@ -219,11 +232,12 @@ export default ({ setTouched, values, setFieldValue, - uploadFile, - createFile, + createSupplementaryFile, onChange, onSubmit, submitSubmission, + errors, + validateForm, ...props }) => ( <Container> @@ -247,9 +261,8 @@ export default ({ <Legend dangerouslySetInnerHTML={createMarkup(element.title)} /> {element.component === 'SupplementaryFiles' && ( <Supplementary - createFile={createFile} + createSupplementaryFile={createSupplementaryFile} onChange={onChange} - uploadFile={uploadFile} /> )} {element.component === 'AuthorsInput' && ( @@ -322,7 +335,11 @@ export default ({ {values.status !== 'submitted' && form.haspopup === 'true' && ( <div> - <Button onClick={toggleConfirming} primary type="button"> + <Button + onClick={() => validateForm() && toggleConfirming()} + primary + type="button" + > Submit your research object </Button> </div> @@ -331,7 +348,8 @@ export default ({ <ModalWrapper> <Confirm form={form} - submitSubmission={handleSubmit} + submit={handleSubmit} + errors={errors} toggleConfirming={toggleConfirming} /> </ModalWrapper> diff --git a/app/components/component-submit/src/components/MetadataFields.md b/app/components/component-submit/src/components/MetadataFields.md deleted file mode 100644 index 30ec5fb3430ca2caa016fc367aaecc246f9a8c4f..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/MetadataFields.md +++ /dev/null @@ -1,13 +0,0 @@ -A form for entering the submission's metadata. - -```js -const manuscript = { - meta: { - title: faker.lorem.sentence(25), - abstract: faker.lorem.sentence(50), - articleType: 'original-research', - keywords: 'test, test1', - }, -} -;<MetadataFields manuscript={manuscript} /> -``` diff --git a/app/components/component-submit/src/components/Submit.js b/app/components/component-submit/src/components/Submit.js index 79f7cdf9d1e12a7a7b3768caa9c3591f03f550e7..d1390b346d0e7734b51c183df429cdc3f1008c37 100644 --- a/app/components/component-submit/src/components/Submit.js +++ b/app/components/component-submit/src/components/Submit.js @@ -24,7 +24,7 @@ const SubmittedVersionColumns = props => ( </Container> ) -const Submit = ({ journal, manuscript, forms, ...formProps }) => { +const Submit = ({ manuscript, forms, ...formProps }) => { const decisionSections = [] const manuscriptVersions = manuscript.manuscriptVersions || [] manuscriptVersions.forEach(versionElem => { @@ -32,11 +32,7 @@ const Submit = ({ journal, manuscript, forms, ...formProps }) => { const label = submittedMoment.format('YYYY-MM-DD') decisionSections.push({ content: ( - <SubmittedVersionColumns - forms={forms} - journal={journal} - manuscript={versionElem} - /> + <SubmittedVersionColumns forms={forms} manuscript={versionElem} /> ), key: versionElem.id, label, @@ -46,12 +42,7 @@ const Submit = ({ journal, manuscript, forms, ...formProps }) => { decisionSections.push({ content: ( <Content> - <FormTemplate - {...formProps} - form={forms} - journal={journal} - manuscript={manuscript} - /> + <FormTemplate {...formProps} form={forms} manuscript={manuscript} /> </Content> ), key: manuscript.id, diff --git a/app/components/component-submit/src/components/SubmitPage.js b/app/components/component-submit/src/components/SubmitPage.js index 6bd3e8adab5575cc305e712363ed75d521aafcf0..facfe46d8926ed411a734c5cd7fcc1450c0aa8ee 100644 --- a/app/components/component-submit/src/components/SubmitPage.js +++ b/app/components/component-submit/src/components/SubmitPage.js @@ -1,10 +1,10 @@ +import React, { useState } from 'react' import { debounce, cloneDeep, isEmpty, set } from 'lodash' -import { compose, withProps, withState, withHandlers } from 'recompose' -import { graphql } from '@apollo/client/react/hoc' -import gql from 'graphql-tag' -import { withFormik } from 'formik' -import { withLoader } from 'pubsweet-client' +// import { compose, withProps, withState, withHandlers } from 'recompose' +import { gql, useQuery, useMutation } from '@apollo/client' +import { Formik } from 'formik' import Submit from './Submit' +import { Spinner } from '../../../shared' const nullToEmpty = obj => JSON.parse(JSON.stringify(obj, (k, v) => (v === null ? '' : v))) @@ -121,17 +121,17 @@ const updateMutation = gql` } ` -const uploadSuplementaryFilesMutation = gql` - mutation($file: Upload!) { - upload(file: $file) { - url - } - } -` +// const uploadSuplementaryFilesMutation = gql` +// mutation($file: Upload!) { +// upload(file: $file) { +// url +// } +// } +// ` const createFileMutation = gql` - mutation($file: Upload!) { - createFile(file: $file) { + mutation($file: Upload!, $meta: FileMetaInput) { + createFile(file: $file, meta: $meta) { id created label @@ -144,116 +144,215 @@ const createFileMutation = gql` } ` -export default compose( - graphql(query, { - options: ({ match }) => ({ +// export default compose( +// graphql(query, { +// options: ({ match }) => ({ +// variables: { +// id: match.params.version, +// form: 'submit', +// }, +// }), +// props: ({ data }) => ({ data: nullToEmpty(data) }), +// }), +// graphql(createFileMutation, { +// props: ({ mutate, ownProps }) => ({ +// createFile: value => { +// const file = { +// url: value.url, +// filename: value.filename, +// mimeType: value.mimeType, +// size: value.size, +// fileType: 'supplementary', +// object: 'Manuscript', +// objectId: ownProps.match.params.version, +// } + +// mutate({ +// variables: { +// file, +// }, +// }) +// }, +// }), +// }), +// graphql(uploadSuplementaryFilesMutation, { +// props: ({ mutate, ownProps }) => ({ +// uploadFile: file => +// mutate({ +// variables: { +// file, +// }, +// }), +// }), +// }), +// graphql(updateMutation, { +// props: ({ mutate, ownProps }) => { +// const debouncers = {} +// const onChange = (value, path) => { +// const input = {} +// set(input, path, value) +// debouncers[path] = debouncers[path] || debounce(updateManuscript, 300) +// return debouncers[path](input) +// } + +// const updateManuscript = input => +// mutate({ +// variables: { +// id: ownProps.match.params.version, +// input: JSON.stringify(emptyToUndefined(input)), +// }, +// }) + +// return { +// onChange, +// } +// }, +// }), +// graphql(updateMutation, { +// props: ({ mutate, ownProps }) => ({ +// onSubmit: (manuscript, { history }) => { +// const updateManuscript = { +// status: 'submitted', +// } + +// mutate({ +// variables: { +// id: ownProps.match.params.version, +// input: JSON.stringify(updateManuscript), +// }, +// }).then(() => { +// history.push('/journal/dashboard') +// }) +// }, +// }), +// }), +// withProps(({ getFile, manuscript, match: { params: { journal } } }) => ({ +// journal: { id: journal }, +// forms: cloneDeep(getFile), +// manuscript, +// submitSubmission: ({ validateForm, setSubmitting, handleSubmit }) => +// validateForm().then(props => +// isEmpty(props) ? setSubmitting(false) : handleSubmit(), +// ), +// })), +// withFormik({ +// initialValues: {}, +// mapPropsToValues: ({ manuscript }) => +// Object.assign({}, manuscript, { +// submission: JSON.parse(manuscript.submission), +// }), +// displayName: 'submit', +// handleSubmit: ( +// props, +// { validateForm, setSubmitting, props: { onSubmit, history } }, +// ) => +// validateForm().then(props => +// isEmpty(props) ? onSubmit(props, { history }) : setSubmitting(false), +// ), +// }), +// withState('confirming', 'setConfirming', false), +// withHandlers({ +// toggleConfirming: ({ validateForm, setConfirming, handleSubmit }) => () => +// setConfirming(confirming => !confirming), +// }), +// )(Submit) + +const SubmitPage = ({ match, history, ...props }) => { + const [confirming, setConfirming] = useState(false) + + const toggleConfirming = () => { + setConfirming(confirming => !confirming) + } + + const { data, loading, error } = useQuery(query, { + variables: { id: match.params.version, form: 'submit' }, + }) + + const [createFile] = useMutation(createFileMutation) + + const createSupplementaryFile = file => { + const meta = { + filename: file.name, + mimeType: file.type, + size: file.size, + fileType: 'supplementary', + object: 'Manuscript', + objectId: match.params.version, + } + + createFile({ variables: { - id: match.params.version, - form: 'submit', + file, + meta, }, - }), - props: ({ data }) => ({ data: nullToEmpty(data) }), - }), - graphql(createFileMutation, { - props: ({ mutate, ownProps }) => ({ - createFile: value => { - const file = { - url: value.url, - filename: value.filename, - mimeType: value.mimeType, - size: value.size, - fileType: 'supplementary', - object: 'Manuscript', - objectId: ownProps.match.params.version, - } + }) + } + + const [update] = useMutation(updateMutation) + + if (loading) return <Spinner /> + if (error) return error + + const manuscript = data?.manuscript + const getFile = data?.getFile - mutate({ - variables: { - file, - }, - }) + const updateManuscript = input => + update({ + variables: { + id: match.params.version, + input: JSON.stringify(input), }, - }), - }), - graphql(uploadSuplementaryFilesMutation, { - props: ({ mutate, ownProps }) => ({ - uploadFile: file => - mutate({ - variables: { - file, - }, - }), - }), - }), - graphql(updateMutation, { - props: ({ mutate, ownProps }) => { - const debouncers = {} - const onChange = (value, path) => { - const input = {} - set(input, path, value) - debouncers[path] = debouncers[path] || debounce(updateManuscript, 300) - return debouncers[path](input) - } + }) - const updateManuscript = input => - mutate({ - variables: { - id: ownProps.match.params.version, - input: JSON.stringify(emptyToUndefined(input)), - }, - }) + const debouncers = {} - return { - onChange, - } - }, - }), - graphql(updateMutation, { - props: ({ mutate, ownProps }) => ({ - onSubmit: (manuscript, { history }) => { - const updateManuscript = { - status: 'submitted', - } + const handleChange = (value, path) => { + const input = {} + set(input, path, value) + debouncers[path] = debouncers[path] || debounce(updateManuscript, 300) + return debouncers[path](input) + } + + const onSubmit = async manuscript => { + const updateManuscript = { + status: 'submitted', + } - mutate({ - variables: { - id: ownProps.match.params.version, - input: JSON.stringify(updateManuscript), - }, - }).then(() => { - history.push('/journal/dashboard') - }) + await update({ + variables: { + id: match.params.version, + input: JSON.stringify(updateManuscript), }, - }), - }), - withLoader(), - withProps(({ getFile, manuscript, match: { params: { journal } } }) => ({ - journal: { id: journal }, - forms: cloneDeep(getFile), - manuscript, - submitSubmission: ({ validateForm, setSubmitting, handleSubmit }) => - validateForm().then(props => - isEmpty(props) ? setSubmitting(false) : handleSubmit(), - ), - })), - withFormik({ - initialValues: {}, - mapPropsToValues: ({ manuscript }) => - Object.assign({}, manuscript, { + }) + history.push('/journal/dashboard') + } + + return ( + <Formik + displayName="submit" + handleChange={handleChange} + onSubmit={async (values, { validateForm, setSubmitting, ...other }) => { + // TODO: Change this to a more Formik idiomatic form + const isValid = Object.keys(await validateForm()).length === 0 + return isValid ? onSubmit(values) : setSubmitting(false) + }} + initialValues={Object.assign({}, manuscript, { submission: JSON.parse(manuscript.submission), - }), - displayName: 'submit', - handleSubmit: ( - props, - { validateForm, setSubmitting, props: { onSubmit, history } }, - ) => - validateForm().then(props => - isEmpty(props) ? onSubmit(props, { history }) : setSubmitting(false), - ), - }), - withState('confirming', 'setConfirming', false), - withHandlers({ - toggleConfirming: ({ validateForm, setConfirming, handleSubmit }) => () => - setConfirming(confirming => !confirming), - }), -)(Submit) + })} + > + {props => ( + <Submit + confirming={confirming} + createSupplementaryFile={createSupplementaryFile} + forms={cloneDeep(getFile)} + manuscript={manuscript} + onChange={handleChange} + toggleConfirming={toggleConfirming} + {...props} + /> + )} + </Formik> + ) +} + +export default SubmitPage diff --git a/app/components/component-submit/src/components/Suggestions.md b/app/components/component-submit/src/components/Suggestions.md deleted file mode 100644 index ee1f366c9dd8c798bd9f341d86e6f515d9c6428d..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/Suggestions.md +++ /dev/null @@ -1,12 +0,0 @@ -A list of questions that must be answered before submission. - -```js -const manuscript = { - suggestions: { - reviewers: { - opposed: faker.name.findName(), - }, - }, -} -;<Suggestions manuscript={manuscript} /> -``` diff --git a/app/components/component-submit/src/components/Supplementary.js b/app/components/component-submit/src/components/Supplementary.js index 2580f3e0353de173b074856e9b47d4364f72b351..0432c9488da0896b8e7e21522fc7629516d82cc2 100644 --- a/app/components/component-submit/src/components/Supplementary.js +++ b/app/components/component-submit/src/components/Supplementary.js @@ -3,7 +3,7 @@ import { cloneDeep } from 'lodash' import { FieldArray } from 'formik' import { Flexbox, UploadButton, UploadingFile } from '@pubsweet/ui' -const renderFilesUpload = (onChange, uploadFile, createFile) => ({ +const renderFilesUpload = (onChange, createSupplementaryFile) => ({ form: { values, setFieldValue }, push, insert, @@ -21,15 +21,7 @@ const renderFilesUpload = (onChange, uploadFile, createFile) => ({ }) setFieldValue('files', fileArray.concat(values.files)) Array.from(event.target.files).forEach(file => { - uploadFile(file).then(({ data }) => { - const newFile = { - url: data.upload.url, - filename: file.name, - mimeType: file.type, - size: file.size, - } - createFile(newFile) - }) + createSupplementaryFile(file) }) }} /> @@ -44,10 +36,10 @@ const renderFilesUpload = (onChange, uploadFile, createFile) => ({ </> ) -const Supplementary = ({ onChange, uploadFile, createFile }) => ( +const Supplementary = ({ onChange, createSupplementaryFile }) => ( <FieldArray name="files" - render={renderFilesUpload(onChange, uploadFile, createFile)} + render={renderFilesUpload(onChange, createSupplementaryFile)} /> ) diff --git a/app/components/component-submit/src/components/SupplementaryFiles.md b/app/components/component-submit/src/components/SupplementaryFiles.md deleted file mode 100644 index 46399c40d007bb451511d6d4058816e54ce18ab5..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/SupplementaryFiles.md +++ /dev/null @@ -1,20 +0,0 @@ -A form for entering the submission's supplementary material. - -```js -const file = () => ({ - file: { - url: faker.internet.url(), - name: faker.system.commonFileName(), - }, - filename: faker.system.commonFileName(), - type: 'supplementary', -}) - -const manuscript = { - files: [file(), file(), file()], -} -;<SupplementaryFiles - manuscript={manuscript} - onChange={values => console.log(values)} -/> -``` diff --git a/app/components/component-submit/src/upload.js b/app/components/component-submit/src/upload.js index 006a495cdaac5d1b18806b30bc339a414b42605f..7e7f5d3e62ef51a2bb02c634f019ba87fcf013f4 100644 --- a/app/components/component-submit/src/upload.js +++ b/app/components/component-submit/src/upload.js @@ -156,17 +156,24 @@ const createManuscriptPromise = ( return client.mutate({ mutation: createManuscriptMutation, variables: { input: manuscript }, - update: (proxy, { data: { createManuscript } }) => { - let data = proxy.readQuery({ query: queries.dashboard }) - data.manuscripts.push(createManuscript) - proxy.writeQuery({ query: queries.dashboard, data }) - - data = proxy.readQuery({ - query: queries.getUser, - variables: { id: currentUser.id }, + update: (cache, { data: { createManuscript } }) => { + cache.modify({ + fields: { + manuscripts(existingManuscritps = []) { + return [...existingManuscritps, createManuscript] + }, + }, }) - data.user.teams.push(createManuscript.teams[0]) - proxy.writeQuery({ query: queries.getUser, data }) + // let data = proxy.readQuery({ query: queries.dashboard }) + // data.manuscripts.push(createManuscript) + // proxy.writeQuery({ query: queries.dashboard, data }) + + // data = proxy.readQuery({ + // query: queries.getUser, + // variables: { id: currentUser.id }, + // }) + // data.user.teams.push(createManuscript.teams[0]) + // proxy.writeQuery({ query: queries.getUser, data }) }, }) }