diff --git a/src/components/SubmissionFailures/FailuresList/FailuresList.js b/src/components/SubmissionFailures/FailuresList/FailuresList.js new file mode 100644 index 000000000..a285873d4 --- /dev/null +++ b/src/components/SubmissionFailures/FailuresList/FailuresList.js @@ -0,0 +1,72 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table } from 'react-bootstrap'; +import { FormattedMessage } from 'react-intl'; +import FailuresListItem from '../FailuresListItem'; + +const FailuresList = ({ failures, createActions }) => + + + + + + + + + + + + {failures.map((failure, i) => + + )} + + {failures.length === 0 && + + + } + +
+ + + + + + + + + + +
+ +
; + +FailuresList.propTypes = { + failures: PropTypes.array, + createActions: PropTypes.func, + failure: PropTypes.object +}; + +export default FailuresList; diff --git a/src/components/SubmissionFailures/FailuresList/index.js b/src/components/SubmissionFailures/FailuresList/index.js new file mode 100644 index 000000000..5290225a3 --- /dev/null +++ b/src/components/SubmissionFailures/FailuresList/index.js @@ -0,0 +1 @@ +export default from './FailuresList'; diff --git a/src/components/SubmissionFailures/FailuresListItem/FailuresListItem.js b/src/components/SubmissionFailures/FailuresListItem/FailuresListItem.js new file mode 100644 index 000000000..bd36c1553 --- /dev/null +++ b/src/components/SubmissionFailures/FailuresListItem/FailuresListItem.js @@ -0,0 +1,60 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedDate, FormattedTime } from 'react-intl'; +import Icon from 'react-fontawesome'; +import { OverlayTrigger, Tooltip } from 'react-bootstrap'; + +const FailuresListItem = ({ id, createActions, failure }) => + + + + {failure.type} + + } + > +
+ {failure.type === 'broker_reject' && } + {failure.type === 'evaluation_failure' && } + {failure.type === 'loading_failure' && } +
+
+ + + {failure.description} + + + + {', '} + + + + {failure.resolvedAt + ? + + {', '} + + + : } + + + {failure.resolutionNote + ? + {failure.resolutionNote} + + : } + + + {createActions && createActions(id)} + + ; + +FailuresListItem.propTypes = { + id: PropTypes.string.isRequired, + failure: PropTypes.object.isRequired, + createActions: PropTypes.func +}; + +export default FailuresListItem; diff --git a/src/components/SubmissionFailures/FailuresListItem/index.js b/src/components/SubmissionFailures/FailuresListItem/index.js new file mode 100644 index 000000000..87ad69694 --- /dev/null +++ b/src/components/SubmissionFailures/FailuresListItem/index.js @@ -0,0 +1 @@ +export default from './FailuresListItem'; diff --git a/src/pages/SubmissionFailures/SubmissionFailures.js b/src/pages/SubmissionFailures/SubmissionFailures.js index 19cb0f1a1..9406c1be8 100644 --- a/src/pages/SubmissionFailures/SubmissionFailures.js +++ b/src/pages/SubmissionFailures/SubmissionFailures.js @@ -3,12 +3,18 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { FormattedMessage } from 'react-intl'; -import Page from '../../components/layout/Page'; -import ResourceRenderer from '../../components/helpers/ResourceRenderer'; +import PageContent from '../../components/layout/PageContent'; +import FetchManyResourceRenderer from '../../components/helpers/FetchManyResourceRenderer'; import { fetchAllFailures } from '../../redux/modules/submissionFailures'; +import { + fetchManyStatus, + readySubmissionFailuresSelector +} from '../../redux/selectors/submissionFailures'; +import FailuresList from '../../components/SubmissionFailures/FailuresList/FailuresList'; +import Box from '../../components/widgets/Box/Box'; class SubmissionFailures extends Component { - static loadAsync = ({}, dispatch) => + static loadAsync = (params, dispatch) => Promise.all([dispatch(fetchAllFailures)]); componentWillMount() { @@ -16,48 +22,100 @@ class SubmissionFailures extends Component { } render() { - const { submissionFailures = [] } = this.props; + const { submissionFailures, fetchStatus } = this.props; return ( - + } + description={ + + } /> } - description={ - + } + description={ + + } /> } - breadcrumbs={[ - { - text: ( + > + {() => + - ), - iconName: 'fort-awesome' - } - ]} - > -
- + } + description={ + + } + breadcrumbs={[ + { + text: ( + + ), + iconName: 'fort-awesome' + } + ]} + > + + } + unlimitedHeight + noPadding + > + + + } + ); } } SubmissionFailures.propTypes = { - loadAsync: PropTypes.func.isRequired + loadAsync: PropTypes.func.isRequired, + fetchStatus: PropTypes.string, + submissionFailures: PropTypes.array }; export default connect( - (state, { params: {} }) => ({}), - (dispatch, { params }) => ({ - loadAsync: () => SubmissionFailures.loadAsync(params, dispatch) + state => ({ + fetchStatus: fetchManyStatus(state), + submissionFailures: readySubmissionFailuresSelector(state) + }), + dispatch => ({ + loadAsync: () => SubmissionFailures.loadAsync({}, dispatch) }) )(SubmissionFailures); diff --git a/src/redux/modules/submissionFailures.js b/src/redux/modules/submissionFailures.js index 0deb02561..90ce9e28c 100644 --- a/src/redux/modules/submissionFailures.js +++ b/src/redux/modules/submissionFailures.js @@ -1,14 +1,13 @@ -import { handleActions, createAction } from 'redux-actions'; -import factory, { - initialState, - resourceStatus -} from '../helpers/resourceManager'; +import { handleActions } from 'redux-actions'; +import factory, { initialState } from '../helpers/resourceManager'; import { createApiAction } from '../middleware/apiMiddleware'; const resourceName = 'submissionFailures'; -var { actions, actionTypes, reduceActions } = factory({ resourceName }); +var { actions, reduceActions } = factory({ resourceName }); -export const additionalActionTypes = {}; +export const additionalActionTypes = { + RESOLVE: 'recodex/submissionFailures/RESOLVE' +}; /** * Actions @@ -19,6 +18,15 @@ export const fetchAllFailures = actions.fetchMany({ endpoint: fetchManyEndpoint }); +export const resolveFailure = (id, note) => + createApiAction({ + type: additionalActionTypes.RESOLVE, + method: 'POST', + endpoint: `/submission-failures/${id}/resolve`, + body: { note }, + meta: { id } + }); + /** * Reducer takes mainly care about all the state of individual attachments */ diff --git a/src/redux/selectors/submissionFailures.js b/src/redux/selectors/submissionFailures.js new file mode 100644 index 000000000..f8b9ab099 --- /dev/null +++ b/src/redux/selectors/submissionFailures.js @@ -0,0 +1,31 @@ +import { createSelector } from 'reselect'; +import { fetchManyEndpoint } from '../modules/submissionFailures'; +import { isReady, getJsData } from '../helpers/resourceManager'; + +const getSubmissionFailures = state => state.submissionFailures; +const getResources = failures => failures.get('resources'); + +export const submissionFailuresSelector = createSelector( + getSubmissionFailures, + getResources +); + +export const fetchManyStatus = createSelector(getSubmissionFailures, state => + state.getIn(['fetchManyStatus', fetchManyEndpoint]) +); + +export const getSubmissionFailure = failureId => + createSelector(submissionFailuresSelector, failures => + failures.get(failureId) + ); + +export const readySubmissionFailuresSelector = createSelector( + submissionFailuresSelector, + failures => + failures + .toList() + .filter(isReady) + .map(getJsData) + .sort((a, b) => a.createdAt - b.createdAt) + .toArray() +);