diff --git a/packages/component-manuscript/src/components/Files.js b/packages/component-manuscript/src/components/Files.js index e48eab69bd603a2d03a5daab1761ed6bbc83116f..100f0563445db1c8838b207f0c91cf81f1893fa0 100644 --- a/packages/component-manuscript/src/components/Files.js +++ b/packages/component-manuscript/src/components/Files.js @@ -1,46 +1,8 @@ import React, { Fragment } from 'react' -import { last } from 'lodash' import styled, { css } from 'styled-components' -import { th, Icon } from '@pubsweet/ui' +import { th } from '@pubsweet/ui' -const parseFileSize = (size = 0) => { - const kbSize = Math.floor(size / 1000) - const mbSize = Math.floor(kbSize / 1000) - const gbSize = Math.floor(mbSize / 1000) - - if (gbSize) { - return `${gbSize} GB` - } else if (mbSize) { - return `${mbSize} MB` - } else if (kbSize) { - return `${kbSize} kB` - } - return `${size} bytes` -} - -const hasPreview = (name = '') => { - const extension = last(name.split('.')) - return ['pdf', 'png', 'jpg'].includes(extension) -} - -const File = ({ name = 'filename', size, id, previewFile, downloadFile }) => ( - <FileRoot> - {hasPreview(name) && ( - <IconButton onClick={previewFile(id)}> - <Icon primary size={3}> - eye - </Icon> - </IconButton> - )} - <IconButton onClick={downloadFile(id, name)}> - <Icon primary size={3}> - download - </Icon> - </IconButton> - <FileName>{name}</FileName> - <FileSize>{parseFileSize(size)}</FileSize> - </FileRoot> -) +import { FileItem } from 'pubsweet-components-faraday/src/components/Files' const Files = ({ previewFile, @@ -55,7 +17,9 @@ const Files = ({ <div /> </Header> {manuscripts.map(file => ( - <File + <FileItem + compact + id={file.id} key={file.id} {...file} downloadFile={downloadFile} @@ -71,7 +35,9 @@ const Files = ({ <div /> </Header> {supplementary.map(file => ( - <File + <FileItem + compact + id={file.id} key={file.id} {...file} downloadFile={downloadFile} @@ -87,7 +53,9 @@ const Files = ({ <div /> </Header> {coverLetter.map(file => ( - <File + <FileItem + compact + id={file.id} key={file.id} {...file} downloadFile={downloadFile} @@ -108,34 +76,6 @@ const defaultText = css` font-size: ${th('fontSizeBaseSmall')}; ` -const FileName = styled.span` - ${defaultText}; - margin: 0 ${th('subGridUnit')}; -` -const FileSize = FileName.extend` - margin-left: ${th('subGridUnit')}; -` - -const IconButton = styled.div` - align-items: center; - cursor: pointer; - display: flex; - justify-content: center; - margin: 0 ${th('subGridUnit')}; - &:hover { - opacity: 0.7; - } -` - -const FileRoot = styled.div` - align-items: center; - border: ${th('borderDefault')}; - display: flex; - flex-direction: row; - margin-bottom: ${th('subGridUnit')}; - padding: ${th('subGridUnit')}; -` - const Header = styled.div` align-self: stretch; align-items: center; diff --git a/packages/component-manuscript/src/components/ReviewerReportForm.js b/packages/component-manuscript/src/components/ReviewerReportForm.js index 300fc7c0041480a9a442fc3f5ade4b3b4667137f..3f8fe374cb496a6363c9657bc576a1a51f2ec3ad 100644 --- a/packages/component-manuscript/src/components/ReviewerReportForm.js +++ b/packages/component-manuscript/src/components/ReviewerReportForm.js @@ -12,6 +12,16 @@ import { } from 'redux-form' import AutosaveIndicator from 'pubsweet-component-wizard/src/components/AutosaveIndicator' +import { + uploadFile, + deleteFile, + getSignedUrl, +} from 'pubsweet-components-faraday/src/redux/files' +import { + FileItem, + FilePicker, +} from 'pubsweet-components-faraday/src/components/Files' + import { ConfirmationModal, withModal2, @@ -57,6 +67,9 @@ const ReviewerReportForm = ({ handleSubmit, formValues = {}, review = {}, + addFile, + removeFile, + previewFile, }) => ( <Root> <Row> @@ -81,10 +94,14 @@ const ReviewerReportForm = ({ /> </Row> <Spacing /> - <Row> - <Label> - Report <ActionText left={12}>Upload file</ActionText> - </Label> + <Row left> + <Label>Report</Label> + <FilePicker + allowedFileExtensions={['pdf', 'doc', 'docx']} + onUpload={addFile} + > + <ActionText left={12}>Upload file</ActionText> + </FilePicker> </Row> <Row> <FullWidth> @@ -102,6 +119,21 @@ const ReviewerReportForm = ({ /> </FullWidth> </Row> + {formValues.files && ( + <Row> + {formValues.files.map(file => ( + <FileItem + compact + id={file.id} + key={file.id} + {...file} + downloadFile={previewFile} + previewFile={previewFile} + removeFile={removeFile} + /> + ))} + </Row> + )} {formValues.hasConfidential ? ( <Fragment> <Row> @@ -176,6 +208,9 @@ export default compose( getFormValues, createRecommendation, updateRecommendation, + uploadFile, + deleteFile, + getSignedUrl, }, ), withProps(({ review = {} }) => ({ @@ -188,6 +223,44 @@ export default compose( changeField: ({ changeForm }) => (field, value) => { changeForm('reviewerReport', field, value) }, + addFile: ({ + formValues: { files = [] }, + uploadFile, + changeForm, + version, + }) => file => { + uploadFile(file, 'review', version.id) + .then(file => { + const newFiles = [...files, file] + + setTimeout(() => { + changeForm('reviewerReport', 'files', newFiles) + }, 1000) + }) + .catch(e => console.error(`Couldn't upload file.`, e)) + }, + removeFile: ({ + formValues: { files = [] }, + changeForm, + deleteFile, + }) => id => e => { + deleteFile(id) + .then(r => { + const newFiles = files.filter(f => f.id !== id) + changeForm('reviewerReport', 'files', newFiles) + }) + .catch(e => console.error(`Couldn't delete the file.`, e)) + + const newFiles = files.filter(f => f.id !== id) + changeForm('reviewerReport', 'files', newFiles) + }, + previewFile: ({ getSignedUrl }) => fileId => e => { + e.preventDefault() + const windowReference = window.open() + getSignedUrl(fileId).then(({ signedUrl }) => { + windowReference.location = signedUrl + }) + }, }), reduxForm({ form: 'reviewerReport', @@ -266,7 +339,7 @@ const Row = styled.div` flex: 1; box-sizing: border-box; flex-wrap: wrap; - justify-content: space-between; + justify-content: ${({ left }) => (left ? 'left' : 'space-between')}; ${defaultText}; ` diff --git a/packages/components-faraday/src/components/Files/FileItem.js b/packages/components-faraday/src/components/Files/FileItem.js index 618292b0234f5e626ec62ff2b520da834572d764..960781cb067ec9d5228709508a714466067b1cc0 100644 --- a/packages/components-faraday/src/components/Files/FileItem.js +++ b/packages/components-faraday/src/components/Files/FileItem.js @@ -1,6 +1,7 @@ -import React from 'react' -import { Icon } from '@pubsweet/ui' -import styled, { withTheme } from 'styled-components' +import React, { Fragment } from 'react' +import { last } from 'lodash' +import { Icon, th } from '@pubsweet/ui' +import styled, { withTheme, css } from 'styled-components' const parseFileSize = size => { const kbSize = size / 1000 @@ -17,6 +18,11 @@ const parseFileSize = size => { return `${size} bytes` } +const hasPreview = (name = '') => { + const extension = last(name.split('.')) + return ['pdf', 'png', 'jpg'].includes(extension) +} + const FileItem = ({ dragHandle, name, @@ -24,42 +30,82 @@ const FileItem = ({ id, removeFile, previewFile, + downloadFile, + compact = false, theme, ...rest }) => ( - <Root data-test={`file-${id}`}> - {dragHandle} - <Info> - <span>{name}</span> - <span>{parseFileSize(size)}</span> - </Info> - <Buttons> - <button onClick={previewFile(id)}> - <Icon color={theme.colorPrimary} size={3}> - eye - </Icon> - </button> - <button onClick={removeFile(id)} title="Delete"> - <Icon color={theme.colorPrimary} size={3}> - trash-2 - </Icon> - </button> - </Buttons> - </Root> + <Fragment> + {compact ? ( + <FileRoot data-test={`file-${id}`}> + {hasPreview(name) && ( + <IconButton onClick={previewFile(id)}> + <Icon primary size={3}> + eye + </Icon> + </IconButton> + )} + {downloadFile && ( + <IconButton onClick={downloadFile(id, name)}> + <Icon primary size={3}> + download + </Icon> + </IconButton> + )} + <FileName>{name}</FileName> + <FileSize>{parseFileSize(size)}</FileSize> + {removeFile && ( + <IconButton onClick={removeFile(id)}> + <Icon primary size={3}> + trash-2 + </Icon> + </IconButton> + )} + </FileRoot> + ) : ( + <Root data-test={`file-${id}`}> + {dragHandle} + <Info> + <span>{name}</span> + <span>{parseFileSize(size)}</span> + </Info> + <Buttons> + <button onClick={previewFile(id)}> + <Icon color={theme.colorPrimary} size={3}> + eye + </Icon> + </button> + {removeFile && ( + <button onClick={removeFile(id)} title="Delete"> + <Icon color={theme.colorPrimary} size={3}> + trash-2 + </Icon> + </button> + )} + </Buttons> + </Root> + )} + </Fragment> ) export default withTheme(FileItem) // #region styles +const defaultText = css` + color: ${th('colorPrimary')}; + font-family: ${th('fontHeading')}; + font-size: ${th('fontSizeBaseSmall')}; +` + const Root = styled.div` align-items: center; - border: ${({ theme }) => theme.borderDefault}; + border: ${th('borderDefault')}; display: flex; margin: 5px; ` const Info = styled.div` - border-right: ${({ theme }) => theme.borderDefault}; + border-right: ${th('borderDefault')}; display: flex; flex: 1; justify-content: space-between; @@ -88,4 +134,31 @@ const Buttons = styled.div` } } ` +const FileName = styled.span` + ${defaultText}; + margin: 0 ${th('subGridUnit')}; +` +const FileSize = FileName.extend` + margin-left: ${th('subGridUnit')}; +` + +const IconButton = styled.div` + align-items: center; + cursor: pointer; + display: flex; + justify-content: center; + margin: 0 ${th('subGridUnit')}; + &:hover { + opacity: 0.7; + } +` + +const FileRoot = styled.div` + align-items: center; + border: ${th('borderDefault')}; + display: flex; + flex-direction: row; + margin-bottom: ${th('subGridUnit')}; + padding: ${th('subGridUnit')}; +` // #endregion diff --git a/packages/components-faraday/src/components/Files/index.js b/packages/components-faraday/src/components/Files/index.js index 6df727395c45e4df9e5db1d031a4d43146ad8440..ab85f18a55158ad1fc24c64e32e0023158ec7ae2 100644 --- a/packages/components-faraday/src/components/Files/index.js +++ b/packages/components-faraday/src/components/Files/index.js @@ -1 +1,3 @@ export { default as Files } from './Files' +export { default as FileItem } from './FileItem' +export { default as FilePicker } from './FilePicker' diff --git a/packages/xpub-faraday/config/upload-validations.js b/packages/xpub-faraday/config/upload-validations.js index 23fa37d7d5a7b0403731429eb910f5f402799000..21b2412ef38c1f8aea0cf3f3e98faf486a3b7107 100644 --- a/packages/xpub-faraday/config/upload-validations.js +++ b/packages/xpub-faraday/config/upload-validations.js @@ -16,4 +16,5 @@ module.exports = { 'application/msword', ]) .error(new Error('Only Word documents and PDFs are allowed')), + review: Joi.any(), }