diff --git a/packages/component-wizard/src/components/FileItem.js b/packages/component-wizard/src/components/FileItem.js index a0853bf836a5c9d1c0006978f84d05bbb6888e0d..0d70a47081d14940fdffd72db1e0669239b84a46 100644 --- a/packages/component-wizard/src/components/FileItem.js +++ b/packages/component-wizard/src/components/FileItem.js @@ -19,7 +19,15 @@ const parseFileSize = size => { return `${size} bytes` } -const FileItem = ({ dragHandle, name, size, id, removeFile, ...rest }) => ( +const FileItem = ({ + dragHandle, + name, + size, + id, + removeFile, + previewFile, + ...rest +}) => ( <div className={classnames(classes['file-item'])}> {dragHandle} <div className={classnames(classes.info)}> @@ -27,9 +35,9 @@ const FileItem = ({ dragHandle, name, size, id, removeFile, ...rest }) => ( <span>{parseFileSize(size)}</span> </div> <div className={classnames(classes.buttons)}> - <a href={rest.signedUrl} target="_blank"> + <button onClick={previewFile(id)}> <Icon color="#666">eye</Icon> - </a> + </button> <button onClick={removeFile(id)} title="Delete"> <Icon color="#666">trash-2</Icon> </button> diff --git a/packages/component-wizard/src/components/FileSection.js b/packages/component-wizard/src/components/FileSection.js index 245dcc5ffc7d7a7b7c9bae8a6dc20577e68fe129..5f3726de3d4bea1d9aba109b9feeafba4c9e4b73 100644 --- a/packages/component-wizard/src/components/FileSection.js +++ b/packages/component-wizard/src/components/FileSection.js @@ -40,6 +40,7 @@ const FileSection = ({ canDropFile, disabledFilepicker, dropSortableFile, + previewFile, }) => connectFileDrop( connectDropTarget( @@ -87,6 +88,7 @@ const FileSection = ({ listId={listId} listItem={FileItem} moveItem={moveItem} + previewFile={previewFile} removeFile={removeFile} /> <FileDropzone label="Drag files here or use the add button." /> diff --git a/packages/component-wizard/src/components/Files.js b/packages/component-wizard/src/components/Files.js index 69f90a37a3b4b54b5d03e81ac90b1bc661f70e25..a54117c83319b5d95f5dc47f40f0fda6a93b105b 100644 --- a/packages/component-wizard/src/components/Files.js +++ b/packages/component-wizard/src/components/Files.js @@ -21,6 +21,7 @@ import { getRequestStatus, setAllFiles, moveFiles, + getSignedUrl, } from '../redux/files' const Files = ({ @@ -30,6 +31,7 @@ const Files = ({ removeFile, changeList, dropSortableFile, + previewFile, ...rest }) => ( <div> @@ -43,6 +45,7 @@ const Files = ({ listId="manuscripts" maxFiles={Number.POSITIVE_INFINITY} moveItem={moveItem('manuscripts')} + previewFile={previewFile} removeFile={removeFile('manuscripts')} title="Main manuscript" /> @@ -54,6 +57,7 @@ const Files = ({ listId="supplementary" maxFiles={Number.POSITIVE_INFINITY} moveItem={moveItem('supplementary')} + previewFile={previewFile} removeFile={removeFile('supplementary')} title="Supplementarry files" /> @@ -67,6 +71,7 @@ const Files = ({ listId="coverLetter" maxFiles={1} moveItem={moveItem('coverLetter')} + previewFile={previewFile} removeFile={removeFile('coverLetter')} title="Cover letter" /> @@ -86,6 +91,7 @@ export default compose( deleteFile, setAllFiles, moveFiles, + getSignedUrl, }, ), lifecycle({ @@ -95,6 +101,12 @@ export default compose( }, }), withHandlers({ + previewFile: ({ getSignedUrl }) => fileId => e => { + e.preventDefault() + getSignedUrl(fileId).then(({ signedUrl }) => { + window.open(signedUrl) + }) + }, dropSortableFile: ({ files, setAllFiles }) => () => { setAllFiles(files) }, @@ -134,7 +146,7 @@ export default compose( version, }) => type => id => e => { e.preventDefault() - deleteFile(id, version.id) + deleteFile(id) const newFiles = { ...files, [type]: files[type].filter(f => f.id !== id), diff --git a/packages/component-wizard/src/components/WizardStep.js b/packages/component-wizard/src/components/WizardStep.js index 52b7609e4776cb2d1453fbeb0ad8d281f8bc39b6..ff5bdc3b9f3ba264388d94a8e9ca4f0054f84017 100644 --- a/packages/component-wizard/src/components/WizardStep.js +++ b/packages/component-wizard/src/components/WizardStep.js @@ -5,7 +5,6 @@ import { ValidatedField, Button } from '@pubsweet/ui' import classes from './WizardStep.local.scss' import AutosaveIndicator from './AutosaveIndicator' -import Files from './Files' export default ({ children: stepChildren, diff --git a/packages/component-wizard/src/redux/files.js b/packages/component-wizard/src/redux/files.js index 06ef16cc589075cd85c2980cea96336211c34b9d..93dddc958306d9723d6e729a9e85f5112eda6587 100644 --- a/packages/component-wizard/src/redux/files.js +++ b/packages/component-wizard/src/redux/files.js @@ -89,7 +89,7 @@ export const getRequestStatus = state => state.files.isFetching // thunked actions export const uploadFile = (file, type, fragmentId) => dispatch => { dispatch(uploadRequest(type)) - return request('/aws-upload', createFileData(file, type, fragmentId)) + return request('/aws', createFileData(file, type, fragmentId)) .then(r => { dispatch(uploadSuccess()) return r @@ -97,9 +97,9 @@ export const uploadFile = (file, type, fragmentId) => dispatch => { .catch(err => dispatch(uploadFailure(err.message))) } -export const deleteFile = (fileId, fragmentId) => dispatch => { +export const deleteFile = fileId => dispatch => { dispatch(removeRequest()) - return remove(`/aws-delete/${fragmentId}/${fileId}`) + return remove(`/aws/${fileId}`) .then(r => { dispatch(removeSuccess()) return r @@ -107,8 +107,7 @@ export const deleteFile = (fileId, fragmentId) => dispatch => { .catch(err => dispatch(removeFailure(err.message))) } -export const getSignedUrl = (fileId, fragmentId) => - get(`aws-signed-url/${fragmentId}/${fileId}`) +export const getSignedUrl = fileId => dispatch => get(`/aws/${fileId}`) // reducer export default (state = initialState, action) => { diff --git a/packages/components-faraday/src/components/Dashboard/Dashboard.js b/packages/components-faraday/src/components/Dashboard/Dashboard.js index ed359ff49f353a936beb419d806e88fd27149c79..03de9a484ec044a8b9ece5cc9387577e04a92a14 100644 --- a/packages/components-faraday/src/components/Dashboard/Dashboard.js +++ b/packages/components-faraday/src/components/Dashboard/Dashboard.js @@ -1,6 +1,8 @@ import React from 'react' import { get } from 'lodash' import { Button } from '@pubsweet/ui' +import { connect } from 'react-redux' +import { compose, withHandlers } from 'recompose' import classes from './Dashboard.local.scss' import DashboardItems from './DashboardItems' @@ -13,6 +15,8 @@ const Dashboard = ({ dashboard, deleteProject, listView, + filters, + getItems, }) => ( <div className={classes.root}> <div className={classes.header}> @@ -24,10 +28,48 @@ const Dashboard = ({ <DashboardFilters changeView={changeViewMode} listView={listView} /> <DashboardItems deleteProject={deleteProject} - list={get(currentUser, 'admin') ? dashboard.all : dashboard.owner} + list={getItems()} listView={listView} /> </div> ) -export default Dashboard +export default compose( + connect(state => ({ + filters: state.filters.filter, + sortOrder: state.filters.sortValue, + })), + withHandlers({ + getItems: ({ filters, sortOrder, currentUser, dashboard }) => () => { + const userItems = get(currentUser, 'admin') + ? dashboard.all + : dashboard.owner + const statusItems = + filters.status === 'all' + ? userItems + : userItems.filter(item => { + const itemStatus = get(item, 'status') + if (!itemStatus && filters.status === 'draft') { + return true + } + return itemStatus === filters.status + }) + const ownerItems = + filters.owner === 'all' + ? statusItems + : statusItems.filter(item => { + const itemOwnerIds = item.owners.map(o => o.id) + if (filters.owner === 'me') { + return itemOwnerIds.includes(currentUser.id) + } else if (filters.owner === 'other') { + return !itemOwnerIds.includes(currentUser.id) + } + return false + }) + return ownerItems.sort((a, b) => { + if (sortOrder === 'newest') return a.created - b.created < 0 + return a.created - b.created > 0 + }) + }, + }), +)(Dashboard) diff --git a/packages/components-faraday/src/components/Dashboard/Dashboard.local.scss b/packages/components-faraday/src/components/Dashboard/Dashboard.local.scss index bc62ceb698f0cd2642e796653e52ab2a6b4426ac..c4b717e217a6feb5460725abb24d2f7a8944ae02 100644 --- a/packages/components-faraday/src/components/Dashboard/Dashboard.local.scss +++ b/packages/components-faraday/src/components/Dashboard/Dashboard.local.scss @@ -15,11 +15,24 @@ } .filters { + align-items: flex-end; + display: flex; + > span { border: 1px solid gray; margin: 0 0.5em; padding: 0 5px; } + + .filter-group { + align-items: flex-start; + display: flex; + flex-direction: column; + + > span { + margin-left: 10px; + } + } } .disabled { diff --git a/packages/components-faraday/src/components/Dashboard/DashboardFilters.js b/packages/components-faraday/src/components/Dashboard/DashboardFilters.js index 124ca55839ea2442c8289165f2bad12d730233b5..45ceb6508276a3ef3791d6f0bb2eb7c3f0c5ae4e 100644 --- a/packages/components-faraday/src/components/Dashboard/DashboardFilters.js +++ b/packages/components-faraday/src/components/Dashboard/DashboardFilters.js @@ -1,7 +1,27 @@ import React from 'react' -import { Icon } from '@pubsweet/ui' +import { connect } from 'react-redux' +import { Icon, Menu } from '@pubsweet/ui' +import { compose, withHandlers } from 'recompose' import classes from './Dashboard.local.scss' +import { changeFilter, changeSort } from './redux/filters' + +const statusFilterOptions = [ + { label: 'All', value: 'all' }, + { label: 'Submitted', value: 'submitted' }, + { label: 'Draft', value: 'draft' }, +] + +const ownerFilterOptions = [ + { label: 'Everyone', value: 'all' }, + { label: 'My work', value: 'me' }, + { label: `Other's work`, value: 'other' }, +] + +const sortOptions = [ + { label: 'Newest first', value: 'newest' }, + { label: 'Oldest first', value: 'oldest' }, +] const DashboardFilters = ({ view, @@ -9,14 +29,24 @@ const DashboardFilters = ({ createdAt, changeView, listView, + changeFilter, + changeSort, }) => ( <div className={classes.filtersContainer}> <div className={classes.filters}> Filter view: - <span> My work </span> - <span> Type </span> - <span> Status </span> - <span> Newest on top </span> + <div className={classes['filter-group']}> + <span>Owner</span> + <Menu onChange={changeFilter('owner')} options={ownerFilterOptions} /> + </div> + <div className={classes['filter-group']}> + <span>Status</span> + <Menu onChange={changeFilter('status')} options={statusFilterOptions} /> + </div> + <div className={classes['filter-group']}> + <span>Sort</span> + <Menu onChange={changeSort} options={sortOptions} /> + </div> </div> <div className={classes.viewMode} onClick={changeView}> <div className={classes.icon}> @@ -27,4 +57,11 @@ const DashboardFilters = ({ </div> ) -export default DashboardFilters +export default compose( + connect(null, { changeFilter, changeSort }), + withHandlers({ + changeFilter: ({ changeFilter }) => filterKey => value => { + changeFilter(filterKey, value) + }, + }), +)(DashboardFilters) diff --git a/packages/components-faraday/src/components/Dashboard/redux/filters.js b/packages/components-faraday/src/components/Dashboard/redux/filters.js index 4bf55715d55526689310886a10853a6197741d78..e50016723b70f39788b05687f18390c02f650c68 100644 --- a/packages/components-faraday/src/components/Dashboard/redux/filters.js +++ b/packages/components-faraday/src/components/Dashboard/redux/filters.js @@ -1,5 +1,40 @@ -export default (state = {}, action) => { +const initialState = { + filter: { + status: 'all', + owner: 'all', + }, + sortValue: 'newest', +} + +const CHANGE_FILTER = 'filters/CHANGE_FILTER' +const CHANGE_SORT = 'filters/CHANGE_SORT' + +export const changeFilter = (filterKey, value) => ({ + type: CHANGE_FILTER, + filterKey, + value, +}) + +export const changeSort = value => ({ + type: CHANGE_SORT, + value, +}) + +export default (state = initialState, action) => { switch (action.type) { + case CHANGE_FILTER: + return { + ...state, + filter: { + ...state.filter, + [action.filterKey]: action.value, + }, + } + case CHANGE_SORT: + return { + ...state, + sortValue: action.value, + } default: return state }