Commit 96b98dec authored by Matija Obreza's avatar Matija Obreza

Merge branch '171-admin-requests' into 'master'

Admin: Requests

Closes #171

See merge request genesys-pgr/genesys-ui!172
parents f5d0bf16 8dba63ac
......@@ -115,6 +115,12 @@
"email": "E-mail address",
"uuid": "UUID"
},
"requests": {
"email": "E-mail address",
"uuid": "UUID",
"state": "State",
"pid": "PID"
},
"dataset": {
"_text": "Keywords",
"accessionIdentifier": {
......@@ -1583,6 +1589,10 @@
}
},
"admin": {
"f": {
"title": "Filter requests",
"pid": "PID"
},
"p": {
"browse": {
"title": "{{totalElements, number}} requests for PGR material"
......@@ -1602,8 +1612,9 @@
}
},
"common": {
"modelMame": "Request",
"modelMame_plural": "Requests",
"modelName": "Request",
"modelName_plural": "Requests",
"emailAddress": "E-mail address",
"preacceptSMTA": "Preaccept SMTA",
"stateLabel": "State",
"state": {
......@@ -1621,6 +1632,10 @@
"0": "Other (please elaborate in Notes field)",
"1": "Research for food and agriculture"
}
},
"sort": {
"lastModifiedDateAsc": "Last modified date (old to new)",
"lastModifiedDateDesc": "Last modified date (new to old)"
}
}
,"subsets": {
......
......@@ -24,6 +24,17 @@ class MaterialRequest {
direction: 'DESC',
};
public static STATE: { [key: number]: string; } = {
0: 'requests.common.state.0',
1: 'requests.common.state.1',
2: 'requests.common.state.2',
};
public static SORT_OPTIONS = {
lastModifiedDate: { property: 'lastModifiedDate', label: 'requests.sort.lastModifiedDateAsc', direction: 'ASC' },
lastModifiedDateD: { property: 'lastModifiedDate', label: 'requests.sort.lastModifiedDateDesc', direction: 'DESC' },
email: { property: 'email', label: 'requests.common.emailAddress', direction: 'ASC' },
};
}
export default MaterialRequest;
......@@ -3,11 +3,17 @@ import * as _ from 'lodash';
// constants
import {ADMIN_APPEND_MATERIAL_REQUESTS, ADMIN_RECEIVE_MATERIAL_REQUEST, ADMIN_RECEIVE_MATERIAL_REQUESTS} from 'requests/constants';
// actions
import navigateTo from 'actions/navigation';
// services
import RequestService from 'service/genesys/RequestService';
// models
import MaterialRequest from 'model/request/MaterialRequest';
import FilteredPage from 'model/FilteredPage';
import RequestService from 'service/genesys/RequestService';
import FilteredPage, { IPageRequest } from 'model/FilteredPage';
import Page from 'model/Page';
import MaterialRequestFilter from 'model/request/MaterialRequestFilter';
const receiveRequests = (paged: FilteredPage<MaterialRequest>, error = null) => ({
......@@ -94,3 +100,23 @@ export const sendValidationEmailAction = (uuid: string) => (dispatch) => {
dispatch(receiveRequest(null, error));
});
};
export const applyFilters = (filters: string | MaterialRequestFilter, page: IPageRequest = { page: 0 }) => (dispatch) => {
return RequestService.list(filters, page)
.then((paged) => {
if (paged.number === 0) {
dispatch(receiveRequests(paged));
} else {
dispatch(appendRequests(paged));
}
dispatch(updateRoute(paged));
}).catch((error) => {
console.log(`API error`, error);
dispatch(receiveRequests(null, error));
});
};
export const updateRoute = (paged: FilteredPage<MaterialRequest>) => (dispatch) => {
dispatch(navigateTo(paged.filterCode ? `/admin/requests/${paged.filterCode}` : '/admin/requests'));
};
......@@ -8,3 +8,5 @@ export const REQUEST_INFO_FORM = 'requests/form/REQUEST_INFO_FORM';
export const ADMIN_RECEIVE_MATERIAL_REQUESTS = 'requests/admin/RECEIVE_MATERIAL_REQUESTS';
export const ADMIN_APPEND_MATERIAL_REQUESTS = 'requests/admin/APPEND_MATERIAL_REQUESTS';
export const ADMIN_RECEIVE_MATERIAL_REQUEST = 'requests/admin/RECEIVE_MATERIAL_REQUEST';
export const REQUEST_FILTER_FORM = 'Form/request/admin/REQUEST_FILTER_FORM';
......@@ -21,6 +21,17 @@ const publicRoutes = [
];
const adminRoutes = [
{
path: '/requests/:filterCode(v.+)?',
component: Loadable({
loader: () => import(/* webpackMode:"lazy", webpackChunkName: "requests" */'requests/ui/admin/BrowsePage'),
loading: Loading,
}),
exact: true,
extraProps: {
title: 'Requests',
},
},
{
path: '/requests',
component: Loadable({
......
......@@ -24,6 +24,10 @@
}
},
"admin": {
"f": {
"title": "Filter requests",
"pid": "PID"
},
"p": {
"browse": {
"title": "{{totalElements, number}} requests for PGR material"
......@@ -43,8 +47,9 @@
}
},
"common": {
"modelMame": "Request",
"modelMame_plural": "Requests",
"modelName": "Request",
"modelName_plural": "Requests",
"emailAddress": "E-mail address",
"preacceptSMTA": "Preaccept SMTA",
"stateLabel": "State",
"state": {
......@@ -62,5 +67,9 @@
"0": "Other (please elaborate in Notes field)",
"1": "Research for food and agriculture"
}
},
"sort": {
"lastModifiedDateAsc": "Last modified date (old to new)",
"lastModifiedDateDesc": "Last modified date (new to old)"
}
}
\ No newline at end of file
......@@ -2,12 +2,14 @@ import * as React from 'react';
import { translate } from 'react-i18next';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { parse } from 'query-string';
// Actions
import { loadMoreRequests } from 'requests/actions/admin';
import { applyFilters, updateRoute, loadMoreRequests } from 'requests/actions/admin';
// Models
import MaterialRequest from 'model/request/MaterialRequest';
import FilteredPage from 'model/FilteredPage';
// UI
import BrowsePageTemplate from 'ui/pages/_base/BrowsePage';
......@@ -15,51 +17,77 @@ import { PageContents } from 'ui/layout/PageLayout';
import Loading from 'ui/common/Loading';
import PagedLoader from 'ui/common/PagedLoader';
import RequestCard from 'requests/ui/admin/c/RequestCard';
import ContentHeaderWithButton from 'ui/common/heading/ContentHeaderWithButton';
import PrettyFilters from 'ui/common/filter/PrettyFilters';
import ContentLayout from 'ui/layout/ContentLayout';
import MaterialRequestFilter from 'requests/ui/admin/c/MaterialRequestFilter';
import Grid from '@material-ui/core/Grid';
import PaginationComponent from 'ui/common/pagination';
class BrowsePage extends BrowsePageTemplate<any> {
protected static needs = [
({ }) => loadMoreRequests(),
({ search, params: { filterCode } }) => {
const qs = parse(search || '');
return applyFilters(filterCode || '', FilteredPage.fromQueryString(qs));
},
];
public componentWillMount() {
const { paged, loadMoreData } = this.props;
const { paged, applyFilters, filterCode } = this.props;
if (!paged) {
loadMoreData();
applyFilters(filterCode);
}
}
public render() {
const { paged, t, loadMoreData} = this.props;
const { paged, loadMoreData} = this.props;
const renderRequest = (r: MaterialRequest, index: number) => {
return <RequestCard key={ r.uuid } index={ index } request={ r } />;
};
return (
<div>
<ContentHeaderWithButton title={ t('requests.admin.p.browse.title', {totalElements: paged ? paged.totalElements : '-' }) }/>
<PageContents className="pt-1rem container-spacing-horizontal">
{ ! paged ? <Loading /> :
<PagedLoader
paged={ paged }
loadMore={ loadMoreData }
roughItemHeight={ 80 }
itemRenderer={ renderRequest } />
}
</PageContents>
</div>
<ContentLayout left={
<MaterialRequestFilter initialValues={ paged && paged.filter || {} } onSubmit={ this.myApplyFilters }/>
} customHeaderHeight>
<Grid container spacing={ 0 }>
<Grid item xs={ 12 }>
<PaginationComponent
pageObj={ paged }
onSortChange={ this.onSortChange }
displayName="requests.common.modelName"
sortOptions={ MaterialRequest.SORT_OPTIONS }
/>
<PrettyFilters
prefix="requests"
filterObj={ paged && paged.filter || {} }
onSubmit={ this.myApplyFilters }
/>
<PageContents className="pt-1rem container-spacing-horizontal">
{ ! paged ? <Loading /> :
<PagedLoader
paged={ paged }
loadMore={ loadMoreData }
roughItemHeight={ 80 }
itemRenderer={ renderRequest } />
}
</PageContents>
</Grid>
</Grid>
</ContentLayout>
);
}
}
const mapStateToProps = (state) => ({
const mapStateToProps = (state, ownProps) => ({
paged: state.requests.admin.paged || undefined,
filterCode: ownProps.match.params.filterCode,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
applyFilters,
loadMoreData: loadMoreRequests,
updateRoute,
}, dispatch);
......
import * as React from 'react';
import { reduxForm } from 'redux-form';
import { REQUEST_FILTER_FORM } from 'requests/constants';
import FiltersBlock from 'ui/common/filter/FiltersBlock';
import CollapsibleComponentSearch from 'ui/common/filter/CollapsibleComponentSearch';
import StringFilter from 'ui/common/filter/StringFilter';
import StringArrFilter from 'ui/common/filter/StringArrFilter';
import StatusFilter from 'ui/catalog/dashboard/c/StatusFilter'; // move
const MaterialRequestFilters = ({handleSubmit, initialValues, initialize, ...other}) => {
return (
<FiltersBlock title="requests.admin.f.title" handleSubmit={ handleSubmit } initialize={ initialize } { ...other }>
<CollapsibleComponentSearch title="common:f.textSearch">
<StringFilter name="email" searchType="contains" label="requests.common.emailAddress" placeholder="name@domain.com"/>
<StringArrFilter name="uuid" label="common:label.UUID" placeholder="ihope-youk-noww-hatyouredoing"/>
<StringArrFilter name="pid" label="requests.admin.f.pid" placeholder="ihope-youk-noww-hatyouredoing"/>
</CollapsibleComponentSearch>
<CollapsibleComponentSearch title="common:label.status">
<StatusFilter isMaterialRequestFilter/>
</CollapsibleComponentSearch>
</FiltersBlock>
);
};
export default reduxForm({
enableReinitialize: true,
form: REQUEST_FILTER_FORM,
})(MaterialRequestFilters);
......@@ -114,6 +114,12 @@
"email": "E-mail address",
"uuid": "UUID"
},
"requests": {
"email": "E-mail address",
"uuid": "UUID",
"state": "State",
"pid": "PID"
},
"dataset": {
"_text": "Keywords",
"accessionIdentifier": {
......
......@@ -4,13 +4,24 @@ import { translate } from 'react-i18next';
import StringArrFilter from 'ui/common/filter/StringArrFilter';
import { PublishState } from 'model/common.model';
class StatusFilter extends React.Component<any, any> {
interface IStatusFilter {
isMaterialRequestFilter?: true;
}
class StatusFilter extends React.Component<IStatusFilter, any> {
private options: object = {};
public componentWillMount() {
this.options[PublishState.DRAFT] = 'status.inProgress';
this.options[PublishState.REVIEWING] = 'status.inReview';
this.options[PublishState.PUBLISHED] = 'status.published';
const isMaterialRequestFilter = this.props;
if (isMaterialRequestFilter) {
this.options[0] = 'requests.common.state.0';
this.options[1] = 'requests.common.state.1';
this.options[2] = 'requests.common.state.2';
} else {
this.options[PublishState.DRAFT] = 'status.inProgress';
this.options[PublishState.REVIEWING] = 'status.inReview';
this.options[PublishState.PUBLISHED] = 'status.published';
}
}
public render() {
......@@ -21,3 +32,4 @@ class StatusFilter extends React.Component<any, any> {
}
export default translate()(StatusFilter);
......@@ -12,6 +12,7 @@ import * as _ from 'lodash';
import { cleanFilters } from 'utilities';
import Accession from 'model/accession/Accession';
import {User} from 'model/user/User';
import MaterialRequest from 'model/request/MaterialRequest';
import PrettyDate from 'ui/common/time/PrettyDate';
/**
......@@ -192,6 +193,7 @@ const mapStateToProps = (state, ownProps) => ({
true: 'Yes',
false: 'No',
},
state: MaterialRequest.STATE,
},
labels: state.uuidDecoder.labels,
});
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment