Commit 333fc252 authored by Viacheslav Pavlov's avatar Viacheslav Pavlov Committed by Matija Obreza
Browse files

Added ui part

parent 0e6124f3
......@@ -224,5 +224,17 @@
"sgsv": "Safety duplicated in Svalbard",
"storage": "Type of Germplasm storage"
}
},
"request": {
"preacceptSMTA": "Preaccept SMTA",
"state": {
"0": "Not validated" ,
"1": "Validated",
"2": "Dispatched"
},
"type": {
"in": "Individual",
"or": "Organizational"
}
}
}
import * as _ from 'lodash';
import {getRequest, listRequests, reconfirmRequest, updatePid, validateRequest} from 'actions/genesys/requestService';
// constants
import {RECEIVE_MATERIAL_REQUEST, RECEIVE_MATERIAL_REQUESTS} from 'constants/requests';
// models
import MaterialRequest from 'model/MaterialRequest';
import Page from 'model/Page';
const receiveRequests = (paged: Page<MaterialRequest>, error = null) => ({
type: RECEIVE_MATERIAL_REQUESTS,
payload: { paged, error },
});
const receiveRequest = (request: MaterialRequest, error = null) => ({
type: RECEIVE_MATERIAL_REQUEST,
payload: { request, error },
});
const refreshRequestPID = (request: MaterialRequest) => (dispatch, getState) => {
const page = getState().requests.paged;
if (page) {
const toUpdateIndex = _.findIndex(page.content, (contentItem) => contentItem.uuid === request.uuid);
if (toUpdateIndex !== -1) {
page.content[toUpdateIndex].body.pid = request.body.pid;
}
return dispatch(receiveRequests(page));
}
};
const refreshRequest = (request: MaterialRequest) => (dispatch, getState) => {
const page = getState().requests.paged;
if (page) {
const toUpdateIndex = _.findIndex(page.content, (contentItem) => contentItem.uuid === request.uuid);
if (toUpdateIndex !== -1) {
page.content[toUpdateIndex] = request;
}
return dispatch(receiveRequests(page));
}
};
export const listMaterialRequests = (page: number) => (dispatch) => {
return dispatch(listRequests(page))
.then((page) => {
dispatch(receiveRequests(page));
})
.catch((error) => {
dispatch(receiveRequests(null, error.response));
});
};
export const loadMaterialRequest = (uuid: string) => (dispatch) => {
return dispatch(getRequest(uuid))
.then((request) => {
dispatch(receiveRequest(request));
})
.catch((error) => {
dispatch(receiveRequest(null, error.response));
});
};
export const updatePidAction = (uuid: string) => (dispatch) => {
return dispatch(updatePid(uuid))
.then((request) => {
dispatch(receiveRequest(request));
dispatch(refreshRequestPID(request));
})
.catch((error) => {
dispatch(receiveRequest(null, error.response));
});
};
export const validateRequestAction = (uuid: string) => (dispatch) => {
return dispatch(validateRequest(uuid))
.then((request) => {
dispatch(receiveRequest(request));
dispatch(refreshRequest(request));
})
.catch((error) => {
dispatch(receiveRequest(null, error.response));
});
};
export const reconfirmRequestAction = (uuid: string) => (dispatch) => {
return dispatch(reconfirmRequest(uuid))
.then((request) => {
dispatch(receiveRequest(request));
dispatch(refreshRequest(request));
})
.catch((error) => {
dispatch(receiveRequest(null, error.response));
});
};
export const RECEIVE_MATERIAL_REQUESTS = 'requests/RECEIVE_MATERIAL_REQUESTS';
export const RECEIVE_MATERIAL_REQUEST = 'requests/RECEIVE_MATERIAL_REQUEST';
......@@ -5,7 +5,7 @@ import MaterialSubRequest from 'model/MaterialSubRequest';
*/
class MaterialRequest {
public active: boolean;
public body: string;
public body: any;
public createdBy: number;
public createdDate: Date;
public email: string;
......
......@@ -14,6 +14,7 @@ import institutes from './institute';
import applicationConfig from './applicationConfig';
import crop from './crop';
import user from './user';
import requests from './requests';
const rootReducer = combineReducers({
login,
......@@ -30,6 +31,7 @@ const rootReducer = combineReducers({
institutes,
applicationConfig,
crop,
requests,
user,
});
......
import update from 'immutability-helper';
import { IReducerAction } from 'model/common.model';
import MaterialRequest from 'model/MaterialRequest';
import Page from 'model/Page';
import {RECEIVE_MATERIAL_REQUEST, RECEIVE_MATERIAL_REQUESTS} from 'constants/requests';
const INITIAL_STATE: {
request: Request;
requestError: any;
paged: Page<MaterialRequest>;
pagedError: any;
} = {
request: null,
requestError: null,
paged: null,
pagedError: null,
};
function requests(state = INITIAL_STATE, action: IReducerAction) {
switch (action.type) {
case RECEIVE_MATERIAL_REQUEST: {
const { request, error } = action.payload;
return update(state, {
request: { $set: request },
requestError: { $set: error },
});
}
case RECEIVE_MATERIAL_REQUESTS: {
const { paged, error } = action.payload;
return update(state, {
paged: { $set: paged },
pagedError: { $set: error },
});
}
default:
return state;
}
}
export default requests;
import * as React from 'react';
import Button from '@material-ui/core/Button';
interface IBackButtonProps extends React.ClassAttributes<any> {
variant?: 'text' | 'flat' | 'outlined' | 'contained' | 'raised' | 'fab' | 'extendedFab';
title: string;
action: any;
style?: any;
}
class ActionButton extends React.Component<IBackButtonProps, any> {
public render() {
const { title, action, variant = 'raised', style = { margin: '0 8px'} } = this.props;
return (
<Button style={ style } variant={ variant } onClick={ action }>
{ title }
</Button>
);
}
}
export default ActionButton;
......@@ -4,7 +4,8 @@ import Markdown from 'ui/common/markdown';
import Subset from 'model/Subset';
import Accession from 'model/Accession';
import FaoInstitute from '../../model/FaoInstitute';
import FaoInstitute from 'model/FaoInstitute';
import MaterialRequest from 'model/MaterialRequest';
function SubsetLink({ to: subset, edit = false, children = null }
: { to: Subset, edit?: boolean, children?: any }) {
......@@ -55,4 +56,19 @@ function InstituteLink({ to: institute, edit = false, children = null }
return null;
}
}
export { SubsetLink, AccessionLink, InstituteLink };
function RequestLink({ to: request, edit = false, children = null }
: { to: MaterialRequest, edit?: boolean, children?: any }) {
if (request) {
return (
<Link to={ `/admin/requests/${request.uuid}` }>
{ children || request.uuid }
</Link>
);
} else {
return null;
}
}
export { SubsetLink, AccessionLink, InstituteLink, RequestLink };
import * as React from 'react';
import {withStyles} from '@material-ui/core/styles';
import MaterialRequest from 'model/MaterialRequest';
import {RequestLink} from 'ui/genesys/Links';
import Card, {CardContent} from 'ui/common/Card';
const styles = () => ({
firstRow: {
marginBottom: '1em',
},
});
const RequestCard = ({request, classes, index, ...other}: { request: MaterialRequest, classes: any, index?: number } & React.ClassAttributes<any>) => {
const requestStates = [{label: 'NOT VALIDATED', color: 'orange'}, {label: 'VALIDATED', color: 'green'}, {label: 'DISPATCHED', color: '#06aced'}];
return (
<Card>
<CardContent>
<div className={ classes.firstRow }>
<b>
{ index !== undefined && `${index + 1}. ` }
<RequestLink to={ request }>
{ `Request from ${ request.email }` }
</RequestLink>
</b>
{ request.state !== undefined &&
<span>
<b></b>
<b style={ {color: requestStates[request.state].color} }> { ` ${requestStates[request.state].label}` } </b>
</span>
}
</div>
<div>
<em>
{ `Notes: ${request.body.requestInfo.notes}` }
</em>
</div>
</CardContent>
</Card>
);
};
export default withStyles(styles)(RequestCard);
......@@ -8,7 +8,7 @@ import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import AccountCircle from '@material-ui/icons/AccountCircle';
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
import AuthorizedRouteComponent from 'ui/common/authorized/AuthorizedRouteComponent';
import Authorize from 'ui/common/authorized/Authorize';
interface IUserMenuComponentProps extends React.ClassAttributes<any> {
classes: any;
......@@ -111,9 +111,9 @@ class UserMenuComponent extends React.Component<IUserMenuComponentProps, any> {
onClose={ this.handleRequestClose }
onClick={ this.handleRequestClose }
>
<AuthorizedRouteComponent authorities={ "ROLE_ADMINISTRATOR" }>
<Authorize role={ "ROLE_ADMINISTRATOR" }>
<Link to="/admin/"><MenuItem>{ t('menu.Admin') }</MenuItem></Link>
</AuthorizedRouteComponent>
</Authorize>
<Link to="/dashboard"><MenuItem>{ t('menu.My Dashboard') }</MenuItem></Link>
<Link to="/profile"><MenuItem>{ t('menu.My profile') }</MenuItem></Link>
<Link to="/a"><MenuItem>{ t('menu.Accessions') }</MenuItem></Link>
......
import * as React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
// Actions
import {listMaterialRequests} from 'actions/requests';
import {listRequests} from 'actions/genesys/requestService';
// Models
import MaterialRequest from 'model/MaterialRequest';
// UI
import BrowsePageTemplate from 'ui/pages/_base/BrowsePage';
import { PageContents } from 'ui/layout/PageLayout';
import Loading from 'ui/common/Loading';
import PagedLoader from 'ui/common/PagedLoader';
import RequestCard from 'ui/genesys/materialRequest/RequestCard';
class BrowsePage extends BrowsePageTemplate<any> {
public componentWillMount() {
const {paged, loadDataPage} = this.props;
if (!paged) {
loadDataPage(0);
}
}
public render() {
const { paged } = this.props;
const renderRequest = (r: MaterialRequest, index: number) => {
return <RequestCard key={ r.uuid } index={ index } request={ r } />;
};
return (
<PageContents>
{ ! paged ? <Loading /> :
<PagedLoader
paged={ paged }
loadPage={ this.loadNextPage }
roughItemHeight={ 45 }
itemRenderer={ renderRequest } />
}
</PageContents>
);
}
}
const mapStateToProps = (state) => ({
paged: state.requests.paged || undefined,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
loadDataPage: listMaterialRequests,
loadDataPromise: listRequests,
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(BrowsePage);
import * as React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {translate} from 'react-i18next';
// Actions
import {loadMaterialRequest, reconfirmRequestAction, updatePidAction, validateRequestAction} from 'actions/requests';
// Models
import MaterialRequest from 'model/MaterialRequest';
// UI
import ContentHeaderWithButton from 'ui/common/heading/ContentHeaderWithButton';
import {MainSection, PageContents, PageSection} from 'ui/layout/PageLayout';
import {Properties, PropertiesItem} from 'ui/common/Properties';
import Loading from 'ui/common/Loading';
import PrettyDate from 'ui/common/time/PrettyDate';
import ActionButton from 'ui/common/buttons/ActionButton';
import BackButton from 'ui/common/buttons/BackButton';
interface IDisplayPageProps extends React.ClassAttributes<any> {
t: any;
requestId: string;
request: MaterialRequest;
error: any;
loadMaterialRequest: any;
updatePid: any;
validateRequest: any;
reconfirmRequest: any;
}
class DisplayPage extends React.Component<IDisplayPageProps, any> {
constructor(props: IDisplayPageProps, context: any) {
super(props, context);
}
public componentWillMount() {
const {requestId, request, loadMaterialRequest} = this.props;
if (requestId && (!request || requestId !== request.uuid)) {
loadMaterialRequest(requestId);
}
}
public render() {
const {request, t, updatePid, reconfirmRequest, validateRequest} = this.props;
return request === null ? (<Loading/>) : (
<div>
<ContentHeaderWithButton
title={ `Request from ${request.email}` }
buttons={
<div>
<ActionButton title="Update PID" action={ () => updatePid(request.uuid) }/>
<ActionButton title="Reconfirm Request" action={ () => reconfirmRequest(request.uuid) }/>
<ActionButton title="Validate Request" action={ () => validateRequest(request.uuid) }/>
<BackButton defaultTarget="/admin/requests"/>
</div>
}
/>
{ request.body &&
<PageContents>
<MainSection title="Main information">
<Properties>
<PropertiesItem title="Requested accession ids">{ request.body.accessionIds.reduce((id, acc) => `${acc}, ${id}`) }</PropertiesItem>
<PropertiesItem title="State">{ t(`request.state.${request.state}`) }</PropertiesItem>
<PropertiesItem title="Created "><PrettyDate value={ request.createdDate }/></PropertiesItem>
{ request.body.pid && <PropertiesItem title="Type">{ t(`request.type.${request.body.pid.type}`) }</PropertiesItem> }
{ request.body.requestInfo && <PropertiesItem title="Purpose type">{ t(`request.purposeType.${request.body.requestInfo.purposeType}`) }</PropertiesItem> }
{ request.body.requestInfo && <PropertiesItem title={ t(`request.preacceptSMTA`) }>{ request.body.requestInfo.preacceptSMTA ? 'Yes' : 'No' }</PropertiesItem> }
{ request.body.requestInfo && <PropertiesItem title="Notes">{ request.body.requestInfo.notes }</PropertiesItem> }
</Properties>
</MainSection>
{ request.body.pid && request.body.pid.type === 'in' &&
<PageSection title="Customer info">
<Properties>
<PropertiesItem title="Name">{ request.body.pid.name }</PropertiesItem>
<PropertiesItem title="Surname">{ request.body.pid.surname }</PropertiesItem>
<PropertiesItem title="Address">{ request.body.pid.address }</PropertiesItem>
<PropertiesItem title="Country">{ request.body.pid.country }</PropertiesItem>
<PropertiesItem title="Phone">{ request.body.pid.telephone }</PropertiesItem>
<PropertiesItem title="Email">{ request.body.pid.email }</PropertiesItem>
</Properties>
</PageSection>
}
{ request.body.pid && request.body.pid.type === 'or' &&
<PageSection title="Organization info">
<Properties>
<PropertiesItem title="Name">{ request.body.pid.orgName }</PropertiesItem>
<PropertiesItem title="Address">{ request.body.pid.orgAddress }</PropertiesItem>
<PropertiesItem title="Country">{ request.body.pid.orgCountry }</PropertiesItem>
</Properties>
</PageSection>
}
{ request.body.pid &&
<PageSection title="Ship info">
<Properties>
<PropertiesItem title="Address">{ request.body.pid.shipAddress }</PropertiesItem>
<PropertiesItem title="Country">{ request.body.pid.shipCountry }</PropertiesItem>
<PropertiesItem title="Phone">{ request.body.pid.shipTelephone }</PropertiesItem>
<PropertiesItem title="Null check">{ request.body.pid.shipTelephone }</PropertiesItem>
</Properties>
</PageSection>
}
</PageContents>
}
</div>
);
}
}
const mapStateToProps = (state, ownProps) => ({
request: state.requests.request,
requestId: ownProps.match.params.uuid,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
loadMaterialRequest,
updatePid: updatePidAction,
reconfirmRequest: reconfirmRequestAction,
validateRequest: validateRequestAction,
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)((translate()(DisplayPage)));
......@@ -16,6 +16,8 @@ import InstituteBrowsePage from 'ui/pages/institutes/BrowsePage';
import InstituteDisplayPage from 'ui/pages/institutes/DisplayPage';
import NotFound from 'ui/common/not-found'; // TODO Move to ui/pages!
import AdminDashboardPage from 'ui/pages/admin/DashboardPage';
import RequestBrowsePage from 'ui/pages/requests/BrowsePage';
import RequestDisplayPage from 'ui/pages/requests/DisplayPage';
import {ROLE_ADMINISTRATOR, ROLE_USER} from 'constants/userRoles';
......@@ -135,6 +137,22 @@ const routes = [
title: 'Users',
},
},
{
path: '/requests',
component: RequestBrowsePage,
exact: true,
extraProps: {
title: 'Requests',
},
},
{
path: '/requests/:uuid',
component: RequestDisplayPage,
exact: true,
extraProps: {
title: 'Request info',
},
},
{
path: '/',
component: AdminDashboardPage,
......
Supports Markdown
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