Commit e6eb655a authored by Viacheslav Pavlov's avatar Viacheslav Pavlov

Merge branch '299-subsets-creators' into 'master'

Subsets: Creators

Closes #299

See merge request genesys-pgr/genesys-ui!316
parents 5ea28249 4c92bfc7
......@@ -2257,7 +2257,8 @@
"faxPlaceholder": "+1 555 1231 Ext. 42",
"address": "Address",
"addressPlaceholder": "Address",
"addSubsetCreator": "Add subset creator"
"addSubsetCreator": "Add subset creator",
"delete": "Delete subset creator"
},
"review": {
"stepName": "Review and publish"
......@@ -2278,6 +2279,12 @@
"COLLECTOR": "Data collector",
"CURATOR": "Data curator",
"null": "Not specified"
},
"roledesc": {
"MANAGER": "Responsible of the planning and execution of the germplasm characterization and evaluation activity which resulted in the dataset. Oversees the collection and management of characterization and evaluation data, and has final sign-off on publication.",
"COLLECTOR": "Records germplasm characterization or evaluation data in the field.",
"DIGITIZER": "Digitizes data.",
"CURATOR": "Organizes and validates data and metadata in correct format, ensures quality of both."
}
}
},
......
......@@ -24,11 +24,13 @@ class DatasetCreatorAutocompleteField extends React.Component<IDatasetCreatorAut
};
private onInputChange = (e) => {
const { input } = this.props;
if (e.target && typeof e.target.value === 'string') {
if (e.target && (e.target.value || e.target.value === '')) {
if (e.target.value !== this.state.searchText) {
if (e.target.value.length >= 3) {
this.doAutocomplete(e.target.value);
input.onChange(e.target.value);
}
}
this.setState({searchText: e.target.value});
......@@ -41,7 +43,7 @@ class DatasetCreatorAutocompleteField extends React.Component<IDatasetCreatorAut
// this.updateMenuPosition();
const suggestions = terms.map((dCr, index) => ({
value: dCr.fullName,
label: `${index + 1}. ${ dCr.fullName } ${ dCr.email && `- ${dCr.email}` } ${dCr.role && this.props.t(`datasets.common.creator.role.${dCr.role}`)}`,
label: `${ dCr.fullName } ${ dCr.email && `- ${dCr.email}` } ${dCr.role && this.props.t(`datasets.common.creator.role.${dCr.role}`)}`,
creator: dCr,
}));
......
......@@ -25,7 +25,7 @@ const URL_REMOVE_ACCESSIONS = UrlTemplate.parse(`/api/v1/subset/remove-accession
const URL_UPDATE = `/api/v1/subset/update`;
const URL_GET = UrlTemplate.parse(`/api/v1/subset/{uuid}`);
const URL_REMOVE = UrlTemplate.parse(`/api/v1/subset/{uuid},{version}`);
const URL_AUTOCOMPLETE = `/api/v1/subset/{uuid}/subsetcreator/autocomplete`;
const URL_AUTOCOMPLETE = UrlTemplate.parse(`/api/v1/subset/{uuid}/subsetcreator/autocomplete`);
const URL_CREATE_SUBSET_CREATOR = UrlTemplate.parse(`/api/v1/subset/{uuid}/subsetcreator/create`);
const URL_DELETE_SUBSET_CREATOR = UrlTemplate.parse(`/api/v1/subset/{uuid}/subsetcreator/delete`);
const URL_LIST_SUBSET_CREATORS = UrlTemplate.parse(`/api/v1/subset/{uuid}/subsetcreator/list`);
......@@ -378,12 +378,12 @@ class SubsetService {
*
* @param c c
*/
public static autocomplete(c: string, xhrConfig?): Promise<SubsetCreator[]> {
public static autocomplete(uuid: string, c: string, xhrConfig?): Promise<SubsetCreator[]> {
const qs = QueryString.stringify({
c: c || undefined,
}, {});
const apiUrl = URL_AUTOCOMPLETE + (qs ? `?${qs}` : '');
const apiUrl = URL_AUTOCOMPLETE.expand({uuid}) + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
......
// Actions
import navigateTo from 'actions/navigation';
import {loadMoreAccessions} from './dashboard';
import {createApiCaller} from 'actions/ApiCall';
import { createApiCaller, createPureApiCaller } from 'actions/ApiCall';
// Constants
import {
......@@ -36,6 +36,7 @@ const apiDeleteSubset = createApiCaller(SubsetService.remove, DASHBOARD_REMOVE_S
const apiCreateSubsetCreator = createApiCaller(SubsetService.createSubsetCreator, ADD_CREATOR_TO_SUBSET);
const apiUpdateSubsetCreator = createApiCaller(SubsetService.updateSubsetCreator, UPDATE_SUBSET_CREATOR);
const apiDeleteSubsetCreator = createApiCaller(SubsetService.deleteSubsetCreator, REMOVE_CREATOR_FROM_SUBSET);
const apiAutocompleteSubsetCreator = createPureApiCaller(SubsetService.autocomplete);
const apiCreateSubset = createApiCaller(SubsetService.create, DASHBOARD_RECEIVE_SUBSET);
const apiUpdateSubset = createApiCaller(SubsetService.update, DASHBOARD_RECEIVE_SUBSET);
......@@ -105,6 +106,12 @@ export const deleteSubsetCreatorRequest = (creator: SubsetCreator) => (dispatch,
return dispatch(apiDeleteSubsetCreator(currentSubset.uuid, creator));
};
export const autocompleteSubsetCreators = (text: string) => {
return (dispatch) => {
return dispatch(apiAutocompleteSubsetCreator('uuid', text));
};
};
// Common
const gotoNextStep = (subset: Subset) => {
return (dispatch, getState) => {
......
......@@ -121,7 +121,8 @@
"faxPlaceholder": "+1 555 1231 Ext. 42",
"address": "Address",
"addressPlaceholder": "Address",
"addSubsetCreator": "Add subset creator"
"addSubsetCreator": "Add subset creator",
"delete": "Delete subset creator"
},
"review": {
"stepName": "Review and publish"
......@@ -142,6 +143,12 @@
"COLLECTOR": "Data collector",
"CURATOR": "Data curator",
"null": "Not specified"
},
"roledesc": {
"MANAGER": "Responsible of the planning and execution of the germplasm characterization and evaluation activity which resulted in the subset. Oversees the collection and management of characterization and evaluation data, and has final sign-off on publication.",
"COLLECTOR": "Records germplasm characterization or evaluation data in the field.",
"DIGITIZER": "Digitizes data.",
"CURATOR": "Organizes and validates data and metadata in correct format, ensures quality of both."
}
}
},
......
import * as React from 'react';
import { Field } from 'redux-form';
import { translate } from 'react-i18next';
import { debounce } from 'debounce';
import FormControl from 'ui/common/forms/FormControl';
import MaterialAutosuggest from 'ui/common/material-autosuggest';
interface ISubsetCreatorAutocompleteFieldProps extends React.ClassAttributes<any> {
label: any;
input: any;
meta?: any;
t: any;
autocompleteCreators: (text: any) => Promise<any>;
updateSubsetCreator: (newSubsetCreator: string) => void;
}
class SubsetCreatorAutocompleteField extends React.Component<ISubsetCreatorAutocompleteFieldProps> {
public state = {
disabled: false,
searchText: null,
suggestions: [],
};
private onInputChange = (e) => {
const { input } = this.props;
if (e.target && typeof e.target.value === 'string') {
if (e.target && (e.target.value || e.target.value === '')) {
if (e.target.value !== this.state.searchText) {
if (e.target.value.length >= 3) {
this.doAutocomplete(e.target.value);
input.onChange(e.target.value);
}
}
this.setState({searchText: e.target.value});
}
}
}
private doAutocomplete = debounce((value) => {
this.props.autocompleteCreators(value).then((creators) => {
const suggestions = creators.map((sCr) => ({
value: sCr.fullName,
label: `${ sCr.fullName } ${ sCr.email && `- ${sCr.email}` } ${sCr.role && this.props.t(`datasets.common.creator.role.${sCr.role}`)}`,
creator: sCr,
}));
this.setState({suggestions});
});
}, 1000);
private onSuggestionSelected = (e, data) => {
const {updateSubsetCreator} = this.props;
this.setState({searchText: data.suggestion.value});
updateSubsetCreator(this.state.suggestions.find((creator) => creator.value === data.suggestion.value).creator);
}
public render() {
const {label, meta, input} = this.props;
const {disabled, searchText} = this.state;
return (
<FormControl fullWidth meta={ meta } disabled={ disabled }>
<Field
name={ `auto-${ input.name }` }
component={ MaterialAutosuggest }
label={ label }
suggestions={ this.state.suggestions }
onSuggestionSelected={ this.onSuggestionSelected }
suggestionLabel="label"
className="full-width"
input={ {
...input,
value: searchText || searchText === '' ? searchText : input && input.value,
onChange: this.onInputChange,
} }
/>
</FormControl>
);
}
}
export default translate()(SubsetCreatorAutocompleteField);
......@@ -19,13 +19,10 @@ import Button from '@material-ui/core/Button';
import Grid from '@material-ui/core/Grid';
import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete';
import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormLabel from '@material-ui/core/FormLabel';
import FormControl from 'ui/common/forms/FormControl';
import {TextField} from 'ui/common/text-field';
import confirm from 'utilities/confirmAlert';
import SubsetCreatorAutocompleteField from './SubsetCreatorAutocompleteField';
import RadioSelection from 'ui/common/forms/RadioSelection';
interface ISubsetCreatorFormProps extends React.ClassAttributes<any> {
......@@ -36,6 +33,8 @@ interface ISubsetCreatorFormProps extends React.ClassAttributes<any> {
subset: Subset;
createSubsetCreator: () => Promise<SubsetCreator>;
deleteCreatorRequest: (creator: SubsetCreator) => Promise<SubsetCreator>;
updateCreatorRequest: (creator: SubsetCreator) => Promise<SubsetCreator>;
autocompleteCreators: (text: string) => Promise<any>;
}
const styles = (theme) => ({
......@@ -49,29 +48,6 @@ const styles = (theme) => ({
}
});
/* tslint:enable */
const renderRadioGroup = translate()(({input, meta, t, classes}) => {
const onInputChange = (event, value) => input.onChange(value);
return (
<FormControl fullWidth required meta={ meta }>
<FormLabel>{ t(`subsets.dashboard.p.stepper.creators.Role`) }</FormLabel>
<RadioGroup
{ ...input }
value={ input.value }
onChange={ onInputChange }
className={ classes.RadioGrid }
>
{ Object.keys(CreatorRole).map((role) => (
<FormControlLabel
value={ role }
label={ t(`subsets.common.creators.role.${role}`) }
control={ <Radio/> }
/>
)) }
</RadioGroup>
</FormControl>
);
});
class SubsetCreatorForm extends React.Component<ISubsetCreatorFormProps, any> {
......@@ -82,7 +58,7 @@ class SubsetCreatorForm extends React.Component<ISubsetCreatorFormProps, any> {
public deleteCreator = (fields, index) => () => {
const {deleteCreatorRequest, t} = this.props;
confirm(<span>{ `${t('subset.dashboard.p.stepper.creators.delete')}?` }</span>, {
confirm(<span>{ `${t('subsets.dashboard.p.stepper.creators.delete')}?` }</span>, {
description: t('common:label.deleteDescription'),
confirmLabel: t('common:action.delete'),
abortLabel: t('common:action.cancel'),
......@@ -93,7 +69,31 @@ class SubsetCreatorForm extends React.Component<ISubsetCreatorFormProps, any> {
});
}
public updateCreatorData = (fields, index) => (newCreator) => {
this.props.updateCreatorRequest({
...fields.get(index),
...newCreator,
uuid: fields.get(index).uuid,
id: fields.get(index).id,
version: fields.get(index).version,
});
}
private renderRoleLabel = (role) => (
<div style={ {paddingTop: '6px', marginBottom: '-6px'} }>
<b style={ {fontSize: '14px'} }>
{ this.props.t(`subsets.common.creators.role.${role}`) }
</b>
<div>
<p style={ {whiteSpace: 'pre-line'} }>
{ this.props.t(`subsets.common.creators.roledesc.${role}`) }
</p>
</div>
</div>
)
public renderCreators = ({ classes, fields, meta: { touched, error, submitFailed } , t}) => {
const { autocompleteCreators } = this.props;
return(
<div>
......@@ -106,14 +106,20 @@ class SubsetCreatorForm extends React.Component<ISubsetCreatorFormProps, any> {
</IconButton>
</div>
<Field required name={ `${creator}.fullName` }
component={ TextField }
component={ SubsetCreatorAutocompleteField }
label={ t(`subsets.dashboard.p.stepper.creators.fullName`) }
placeholder={ t(`subsets.dashboard.p.stepper.creators.fullNamePlaceholder`) }
validate={ [ Validators.required ] }
updateSubsetCreator={ this.updateCreatorData(fields, index) }
autocompleteCreators={ autocompleteCreators }
/>
<Field required name={ `${creator}.role` }
classes={ classes }
component={ renderRadioGroup }
component={ RadioSelection }
options={ Object.keys(CreatorRole) }
formLabel={ t(`subsets.dashboard.p.stepper.creators.Role`) }
renderOptionLabel={ this.renderRoleLabel }
singleColumn
/>
<Field name={ `${creator}.institutionalAffiliation` }
component={ TextField }
......
......@@ -5,7 +5,12 @@ import {getFormValues} from 'redux-form';
import * as _ from 'lodash';
// actions
import {createSubsetCreator, deleteSubsetCreatorRequest, updateSubsetCreatorRequest} from 'subsets/actions/editor';
import {
createSubsetCreator,
deleteSubsetCreatorRequest,
updateSubsetCreatorRequest,
autocompleteSubsetCreators,
} from 'subsets/actions/editor';
// constants
import { SUBSET_CREATOR_FORM} from 'subsets/constants';
......@@ -24,6 +29,7 @@ interface ICreatorsStepProps extends React.ClassAttributes<any> {
createSubsetCreator: () => Promise<Location>;
updateSubsetCreatorRequest: (creator: SubsetCreator) => Promise<SubsetCreator>;
deleteSubsetCreatorRequest: (creator: SubsetCreator) => Promise<SubsetCreator>;
autocompleteSubsetCreators: (text: string) => Promise<any>;
formValues: any;
}
......@@ -31,7 +37,7 @@ class CreatorsStep extends StepperTemplate<ICreatorsStepProps> {
protected renderContent = () => {
const {item, deleteSubsetCreatorRequest} = this.props;
const {item, deleteSubsetCreatorRequest, autocompleteSubsetCreators, updateSubsetCreatorRequest} = this.props;
return !item ? <Loading /> : (
<SubsetCreatorForm
......@@ -39,6 +45,8 @@ class CreatorsStep extends StepperTemplate<ICreatorsStepProps> {
initialValues={ item }
createSubsetCreator={ this.create }
deleteCreatorRequest={ deleteSubsetCreatorRequest }
updateCreatorRequest={ updateSubsetCreatorRequest }
autocompleteCreators={ autocompleteSubsetCreators }
/>
);
}
......@@ -78,6 +86,7 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({
createSubsetCreator,
deleteSubsetCreatorRequest,
updateSubsetCreatorRequest,
autocompleteSubsetCreators,
}, dispatch);
export default connect(
......
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