Commit ee446e88 authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch '264-autocompletefilter-component' into 'master'

Resolve "AutocompleteFilter component"

Closes #264

See merge request genesys-pgr/genesys-ui!276
parents 13b887ae 7839e978
...@@ -40,6 +40,7 @@ const apiAccessionsMapInfo = createApiCaller(AccessionService.mapInfo, RECEIVE_A ...@@ -40,6 +40,7 @@ const apiAccessionsMapInfo = createApiCaller(AccessionService.mapInfo, RECEIVE_A
const apiGeoJson = createPureApiCaller(AccessionService.geoJson); const apiGeoJson = createPureApiCaller(AccessionService.geoJson);
const apiClimateInfo = createPureApiCaller(ClimateService.getCurrentClimate); const apiClimateInfo = createPureApiCaller(ClimateService.getCurrentClimate);
const apiAutocomplete = createPureApiCaller(AccessionService.autocomplete);
const apiListAccessionByUuid = createPureApiCaller(AccessionService.listAllByUuid); const apiListAccessionByUuid = createPureApiCaller(AccessionService.listAllByUuid);
const apiAccessionByUuid = createApiCaller(AccessionService.getDetailsByUuid, RECEIVE_ACCESSION); const apiAccessionByUuid = createApiCaller(AccessionService.getDetailsByUuid, RECEIVE_ACCESSION);
...@@ -164,6 +165,10 @@ const makeRange = (variable: number, diff: number) => { ...@@ -164,6 +165,10 @@ const makeRange = (variable: number, diff: number) => {
}; };
}; };
export const autocomplete = (field: string, term: string, filters: string | AccessionFilter) => (dispatch, getState) => {
return dispatch(apiAutocomplete(field, term, filters));
};
export const applyClimateFilters = (climate: TileClimate, extraFilters?: any) => (dispatch) => { export const applyClimateFilters = (climate: TileClimate, extraFilters?: any) => (dispatch) => {
// BIO1 = Annual Mean Temperature !! // BIO1 = Annual Mean Temperature !!
// BIO2 = Mean Diurnal Range (Mean of monthly (max temp - min temp)) // BIO2 = Mean Diurnal Range (Mean of monthly (max temp - min temp))
......
...@@ -63,7 +63,13 @@ class BrowsePage extends BrowsePageTemplate<Accession> { ...@@ -63,7 +63,13 @@ class BrowsePage extends BrowsePageTemplate<Accession> {
return ( return (
<PageLayout sidebar={ <PageLayout sidebar={
<AccessionFilters initialValues={ paged && paged.filter || {} } onSubmit={ this.myApplyFilters } terms={ suggestionTerms } crops={ crops }/> <AccessionFilters
initialValues={ paged && paged.filter || {} }
filterCode={ paged && paged.filterCode || '' }
onSubmit={ this.myApplyFilters }
terms={ suggestionTerms }
crops={ crops }
/>
}> }>
<PageTitle title={ t('accessions.public.p.browse.title') }/> <PageTitle title={ t('accessions.public.p.browse.title') }/>
<ContentHeader title={ t('accessions.public.p.browse.title') } subTitle={ t('accessions.public.p.browse.subTitle') } /> <ContentHeader title={ t('accessions.public.p.browse.title') } subTitle={ t('accessions.public.p.browse.subTitle') } />
......
...@@ -356,9 +356,13 @@ class BrowsePage extends React.Component<IMapPageProps, any> { ...@@ -356,9 +356,13 @@ class BrowsePage extends React.Component<IMapPageProps, any> {
<ContentHeader title={ t('accessions.public.p.browse.title') } subTitle={ t('accessions.public.p.browse.subTitle') } /> <ContentHeader title={ t('accessions.public.p.browse.title') } subTitle={ t('accessions.public.p.browse.subTitle') } />
<Drawer variant="temporary" open={ sidebarOpened } onClose={ this.closeSidebar }> <Drawer variant="temporary" open={ sidebarOpened } onClose={ this.closeSidebar }>
<AccessionsFilters terms={ suggestionTerms } onSubmit={ loadAccessionsMapInfo } initialValues={ mapInfo.data.filter }/> <AccessionsFilters
</Drawer> filterCode={ filterCode || '' }
terms={ suggestionTerms }
onSubmit={ loadAccessionsMapInfo }
initialValues={ mapInfo.data.filter }
/>
</Drawer>
<Dialog open={ dialogOpened } onClose={ this.hideDialog } maxWidth="md" fullWidth> <Dialog open={ dialogOpened } onClose={ this.hideDialog } maxWidth="md" fullWidth>
<BioClimateDisplay classes={ {section: classes.climateDialog} } climateData={ climateData }/> <BioClimateDisplay classes={ {section: classes.climateDialog} } climateData={ climateData }/>
......
...@@ -158,7 +158,12 @@ class BrowsePage extends React.Component<IOverviewPageProps, any> { ...@@ -158,7 +158,12 @@ class BrowsePage extends React.Component<IOverviewPageProps, any> {
return ( return (
<PageLayout <PageLayout
sidebar={ sidebar={
<AccessionFilters terms={ suggestionTerms } initialValues={ overviewWrapper && overviewWrapper.filter || {} } onSubmit={ applyOverviewFilters }/> <AccessionFilters
filterCode={ filterCode || '' }
terms={ suggestionTerms }
initialValues={ overviewWrapper && overviewWrapper.filter || {} }
onSubmit={ applyOverviewFilters }
/>
} }
withFooter withFooter
> >
......
import * as React from 'react'; import * as React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { reduxForm } from 'redux-form'; import { reduxForm } from 'redux-form';
import {translate} from 'react-i18next'; import {translate} from 'react-i18next';
...@@ -10,11 +11,13 @@ import CollapsibleComponentSearch from 'ui/common/filter/CollapsibleComponentSea ...@@ -10,11 +11,13 @@ import CollapsibleComponentSearch from 'ui/common/filter/CollapsibleComponentSea
import NumberFilter from 'ui/common/filter/NumberFilter'; import NumberFilter from 'ui/common/filter/NumberFilter';
import StringFilter from 'ui/common/filter/StringFilter'; import StringFilter from 'ui/common/filter/StringFilter';
import StringArrFilter from 'ui/common/filter/StringArrFilter'; import StringArrFilter from 'ui/common/filter/StringArrFilter';
import AutocompleteFilter from 'ui/common/filter/AutocompleteFilter';
import Accession from 'model/accession/Accession'; import Accession from 'model/accession/Accession';
import DateFilter from 'ui/common/filter/DateFilter'; import DateFilter from 'ui/common/filter/DateFilter';
import BooleanFilter from 'ui/common/filter/BooleanFilter'; import BooleanFilter from 'ui/common/filter/BooleanFilter';
import { autocomplete } from 'accessions/actions/public';
const AccessionFilters = ({handleSubmit, initialize, terms, crops, t, ...other}) => { const AccessionFilters = ({handleSubmit, initialValues, initialize, filterCode, autocomplete, terms, crops, t, ...other}) => {
// console.log('AccessionFilters', initialValues); // console.log('AccessionFilters', initialValues);
return ( return (
<FiltersBlock title={ t('accessions.public.f.filtersTitle') } handleSubmit={ handleSubmit } initialize={ initialize } { ...other }> <FiltersBlock title={ t('accessions.public.f.filtersTitle') } handleSubmit={ handleSubmit } initialize={ initialize } { ...other }>
...@@ -26,8 +29,22 @@ const AccessionFilters = ({handleSubmit, initialize, terms, crops, t, ...other}) ...@@ -26,8 +29,22 @@ const AccessionFilters = ({handleSubmit, initialize, terms, crops, t, ...other})
/> />
</CollapsibleComponentSearch> </CollapsibleComponentSearch>
<CollapsibleComponentSearch title={ t('common:f.textSearch') }> <CollapsibleComponentSearch title={ t('common:f.textSearch') }>
<StringArrFilter name="institute.code" terms={ terms && terms.get('institute.code') } label={ t('accessions.common.instituteCode') } placeholder="NGA039"/> <AutocompleteFilter
<StringArrFilter name="institute.country.code3" label={ t('accessions.model.institute.country.iso3') } placeholder="NGA"/> filterCode={ filterCode }
autocomplete={ autocomplete }
name="institute.code"
terms={ terms && terms.get('institute.code') }
label={ t('accessions.common.instituteCode') }
placeholder="NGA039, IITA, International"
/>
<AutocompleteFilter
filterCode={ filterCode }
autocomplete={ autocomplete }
name="institute.country.code3"
terms={ terms && terms.get('institute.country.iso3') }
label={ t('accessions.model.institute.country.iso3') }
placeholder="NGA, Nigeria"
/>
<StringFilter name="accessionNumber" searchType="contains" label={ t('accessions.common.acceNumb') } placeholder="IRGC"/> <StringFilter name="accessionNumber" searchType="contains" label={ t('accessions.common.acceNumb') } placeholder="IRGC"/>
<NumberFilter name="seqNo" label={ t('accessions.public.f.seqNumber') } /> <NumberFilter name="seqNo" label={ t('accessions.public.f.seqNumber') } />
</CollapsibleComponentSearch> </CollapsibleComponentSearch>
...@@ -45,7 +62,9 @@ const AccessionFilters = ({handleSubmit, initialize, terms, crops, t, ...other}) ...@@ -45,7 +62,9 @@ const AccessionFilters = ({handleSubmit, initialize, terms, crops, t, ...other})
/> />
</CollapsibleComponentSearch> </CollapsibleComponentSearch>
<CollapsibleComponentSearch title={ t('accessions.common.taxonomy') }> <CollapsibleComponentSearch title={ t('accessions.common.taxonomy') }>
<StringArrFilter <AutocompleteFilter
filterCode={ filterCode }
autocomplete={ autocomplete }
name="taxonomy.genus" name="taxonomy.genus"
terms={ terms && terms.get('taxonomy.genus') } terms={ terms && terms.get('taxonomy.genus') }
label={ t('accessions.common.genus') } label={ t('accessions.common.genus') }
...@@ -69,7 +88,14 @@ const AccessionFilters = ({handleSubmit, initialize, terms, crops, t, ...other}) ...@@ -69,7 +88,14 @@ const AccessionFilters = ({handleSubmit, initialize, terms, crops, t, ...other})
<StringFilter name="taxonomy.subtaxa" searchType="contains" label={ t('accessions.public.f.subtaxon') } placeholder=""/> <StringFilter name="taxonomy.subtaxa" searchType="contains" label={ t('accessions.public.f.subtaxon') } placeholder=""/>
</CollapsibleComponentSearch> </CollapsibleComponentSearch>
<CollapsibleComponentSearch title={ t('accessions.public.f.originOfMaterial') }> <CollapsibleComponentSearch title={ t('accessions.public.f.originOfMaterial') }>
<StringArrFilter name="countryOfOrigin.code3" terms={ terms && terms.get('countryOfOrigin.code3') } label={ t('accessions.common.countryOfOrigin') } placeholder="SVN"/> <AutocompleteFilter
filterCode={ filterCode }
autocomplete={ autocomplete }
name="countryOfOrigin.code3"
terms={ terms && terms.get('countryOfOrigin.code3') }
label={ t('accessions.common.countryOfOrigin') }
placeholder="SVN, Slovenia"
/>
<NumberFilter name="geo.latitude" label={ t('geo.common.latitude') } /> <NumberFilter name="geo.latitude" label={ t('geo.common.latitude') } />
<NumberFilter name="geo.longitude" label={ t('geo.common.longitude') } /> <NumberFilter name="geo.longitude" label={ t('geo.common.longitude') } />
<NumberFilter name="geo.elevation" label={ t('accessions.public.f.elevation') } /> <NumberFilter name="geo.elevation" label={ t('accessions.public.f.elevation') } />
...@@ -143,11 +169,14 @@ const AccessionFilters = ({handleSubmit, initialize, terms, crops, t, ...other}) ...@@ -143,11 +169,14 @@ const AccessionFilters = ({handleSubmit, initialize, terms, crops, t, ...other})
const mapStateToProps = (state, ownProps) => ({ const mapStateToProps = (state, ownProps) => ({
crops: state.crop.public.list ? state.crop.public.list.data : undefined, crops: state.crop.public.list ? state.crop.public.list.data : undefined,
}); });
const mapDispatchToProps = (dispatch) => bindActionCreators({
autocomplete,
}, dispatch);
export default translate()(reduxForm({ export default translate()(reduxForm({
enableReinitialize: true, enableReinitialize: true,
destroyOnUnmount: false, destroyOnUnmount: false,
form: ACCESSION_FILTERFORM, form: ACCESSION_FILTERFORM,
})(connect(mapStateToProps)(AccessionFilters))); })(connect(mapStateToProps, mapDispatchToProps)(AccessionFilters)));
/*
* Defined in Swagger as '#/definitions/LabelValue«string»'
*/
class LabelValue<T> {
public label: string;
public value: T;
}
export default LabelValue;
...@@ -11,12 +11,14 @@ import FilteredPage, { IPageRequest } from 'model/FilteredPage'; ...@@ -11,12 +11,14 @@ import FilteredPage, { IPageRequest } from 'model/FilteredPage';
import {AccessionRef} from 'model/accession/AccessionRef'; import {AccessionRef} from 'model/accession/AccessionRef';
import AccessionAuditLog from 'model/accession/AccessionAuditLog'; import AccessionAuditLog from 'model/accession/AccessionAuditLog';
import AccessionSuggestionPage from 'model/accession/AccessionSuggestionPage'; import AccessionSuggestionPage from 'model/accession/AccessionSuggestionPage';
import LabelValue from 'model/LabelValue';
const URL_GET_BY_DOI = `/api/v1/acn/{doi}`; // UrlTemplate doesn't like the / in DOI const URL_GET_BY_DOI = `/api/v1/acn/{doi}`; // UrlTemplate doesn't like the / in DOI
const URL_UUIDS_FROM_ACCE_NUMBERS = `/api/v1/acn/acce-number`; const URL_UUIDS_FROM_ACCE_NUMBERS = `/api/v1/acn/acce-number`;
const URL_UUID_FROM_ACCE_NUMBER = UrlTemplate.parse(`/api/v1/acn/acce-number/{acceNumber}`); const URL_UUID_FROM_ACCE_NUMBER = UrlTemplate.parse(`/api/v1/acn/acce-number/{acceNumber}`);
const URL_GET_BY_UUID = UrlTemplate.parse(`/api/v1/acn/{uuid}`); const URL_GET_BY_UUID = UrlTemplate.parse(`/api/v1/acn/{uuid}`);
const URL_GEO_JSON = `/api/v1/acn/geoJson`; const URL_GEO_JSON = `/api/v1/acn/geoJson`;
const URL_AUTOCOMPLETE = UrlTemplate.parse(`/api/v1/acn/autocomplete/{field}`);
const URL_UUID_FROM_IDS = `/api/v1/acn/id`; const URL_UUID_FROM_IDS = `/api/v1/acn/id`;
const URL_LIST_BY_UUID = `/api/v1/acn/for-uuid`; const URL_LIST_BY_UUID = `/api/v1/acn/for-uuid`;
const URL_GET_ACCESSION_AUDIT_LOG_BY_DOI = `/api/v1/acn/auditlog/{doi}`; // UrlTemplate doesn't like the / in DOI const URL_GET_ACCESSION_AUDIT_LOG_BY_DOI = `/api/v1/acn/auditlog/{doi}`; // UrlTemplate doesn't like the / in DOI
...@@ -366,6 +368,30 @@ class AccessionService { ...@@ -366,6 +368,30 @@ class AccessionService {
...content, ...content,
}).then(({ data }) => data); }).then(({ data }) => data);
} }
/**
* autocomplete at /api/v1/acn/autocomplete/{field}
*
* @param field field
* @param filter filter
* @param term term
*/
public static autocomplete(field: string, term: string, filter: string | AccessionFilter, xhrConfig?): Promise<Array<LabelValue<string>>> {
const qs = QueryString.stringify({
f: typeof filter === 'string' ? filter : undefined,
term: term || undefined,
}, {});
const apiUrl = URL_AUTOCOMPLETE.expand({ field }) + (qs ? `?${qs}` : '');
const content = { data: typeof filter === 'string' ? null : { ...filter } };
return axiosBackend.request({
...xhrConfig,
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as Array<LabelValue<string>>);
}
} }
export default AccessionService; export default AccessionService;
import * as React from 'react';
import { Fields, Field } from 'redux-form';
import { translate } from 'react-i18next';
import * as _ from 'lodash';
import Number from 'ui/common/Number';
import { Properties, PropertiesItem } from 'ui/common/Properties';
import LabelValue from 'model/LabelValue';
import AccessionFilter from 'model/accession/AccessionFilter';
import MaterialAutosuggest from 'ui/common/material-autosuggest';
import { debounce } from 'debounce';
interface IStringListProps extends React.ClassAttributes<any> {
input: any;
notInput: any;
removeByIndex: (index: number, isNot: boolean) => void;
}
class StringList extends React.Component<IStringListProps> {
public state = {
renderList: [],
};
private addToNotList = (item, index) => {
const { input, notInput } = this.props;
const {renderList} = this.state;
renderList[index] = {value: item.value, state: false};
this.setState({renderList});
input.onChange(renderList.filter((renderItem) => renderItem.state).map((renderItem) => renderItem.value));
notInput.onChange(renderList.filter((renderItem) => !renderItem.state).map((renderItem) => renderItem.value));
}
private removeFromNotList = (item, index) => {
const { input, notInput } = this.props;
const {renderList} = this.state;
renderList[index] = {value: item.value, state: true};
this.setState({renderList});
input.onChange(renderList.filter((renderItem) => renderItem.state).map((renderItem) => renderItem.value));
notInput.onChange(renderList.filter((renderItem) => !renderItem.state).map((renderItem) => renderItem.value));
}
public componentWillMount() {
let {input: {value: doList}, notInput: {value: notList}} = this.props;
doList = doList || [];
notList = notList || [];
const {renderList} = this.state;
doList
.filter((doItem) => renderList.findIndex((renderItem) => renderItem.value === doItem) === -1)
.map((doItem) => renderList.push({value: doItem, state: true}));
notList
.filter((notItem) => renderList.findIndex((renderItem) => renderItem.value === notItem) === -1)
.map((notItem) => renderList.push({value: notItem, state: false}));
this.setState({renderList});
}
public componentWillReceiveProps(nextProps) {
let {input: {value: doList}, notInput: {value: notList}} = nextProps;
doList = doList || [];
notList = notList || [];
const {renderList} = this.state;
const newRenderList = [];
renderList.map((renderItem) => {
if (doList.indexOf(renderItem.value) !== -1 || notList.indexOf(renderItem.value) !== -1) {
newRenderList.push(renderItem);
}
});
doList
.filter((doItem) => newRenderList.findIndex((renderItem) => renderItem.value === doItem) === -1)
.map((doItem) => newRenderList.push({value: doItem, state: true}));
notList
.filter((notItem) => newRenderList.findIndex((renderItem) => renderItem.value === notItem) === -1)
.map((notItem) => newRenderList.push({value: notItem, state: false}));
this.setState({renderList: newRenderList});
}
public render() {
const {removeByIndex} = this.props;
const {renderList} = this.state;
return (
<div>
{ renderList.map((renderItem, index) => (
<div style={ { margin: '.2rem 0', padding: '.2rem 1rem', backgroundColor: '#e8e5e1', color: '#202222' } } key={ renderItem.value }>
{ renderItem.state ?
<span className="font-bold float-Left mr-5" onClick={ () => this.addToNotList(renderItem, index) }>+</span>
:
<span className="font-bold float-Left mr-5" onClick={ () => this.removeFromNotList(renderItem, index) }>-</span>
}
{ renderItem.value }
<div className="font-bold float-right" onClick={ () => removeByIndex(index, !renderItem.state) }>X</div>
</div>
)) }
</div>
);
}
}
interface IAutocompleteFilterInternal extends React.ClassAttributes<any> {
field: string;
filterCode: string;
input: any;
placeholder?: string;
label?: string;
options?: { [key: string]: any; };
t: any;
addToNotList: (item: any) => void;
notList: any[];
autocomplete: (field: string, term: string, filter: string | AccessionFilter) => Promise<Array<LabelValue<string>>>;
}
class AutocompleteFilterInternal extends React.Component<IAutocompleteFilterInternal & any, any> {
private constructor(props, context) {
super(props, context);
const notValues = _.get(props, `${props.names[0]}.input.value`);
const values = _.get(props, `${props.names[1]}.input.value`);
this.state = {
values,
notValues,
text: '',
autocompleteObj: [],
};
}
public componentWillMount() {
const input = _.get(this.props, `${this.props.names[0]}.input`);
const value = typeof input.value[0] === 'number' ? input.value.map((key) => `${key}`) : input.value;
const notValue = _.get(this.props, `${this.props.names[1]}.input.value`);
this.setState({
values: [ ...value ],
notValues: [ ...notValue ],
text: '',
});
}
public componentWillReceiveProps(nextProps) {
const input = _.get(nextProps, `${nextProps.names[0]}.input`);
const value = typeof input.value[0] === 'number' ? input.value.map((key) => `${key}`) : input.value;
const notValue = _.get(nextProps, `${nextProps.names[1]}.input.value`);
this.setState({
values: [ ...value ],
notValues: [...notValue],
text: '',
});
}
private maybeAdd = (...newValues: string[]) => {
const values = [ ...this.state.values ];
newValues.forEach((text) => {
if (text && text.length > 0) {
if (values.indexOf(text) < 0) {
values.push(text);
}
}
});
if (!_.isEqual(values, this.state.values)) {
this.setState({
text: '',
values,
});
}
return values;
}
private maybeRemove = (...newValues: string[]) => {
const values = [ ...this.state.values ];
newValues.forEach((text) => {
if (text && text.length > 0) {
const index: number = values.indexOf(text);
if (index >= 0) {
values.splice(index, 1);
}
}
});
if (!_.isEqual(values, this.state.values)) {
this.setState({
text: '',
values,
});
}
return values;
}
private removeByIndex = (index, isNot) => {
const { input } = _.get(this.props, this.props.names[isNot ? 1 : 0]);
const newValues = this.maybeRemove(this.state.values[index]);
input.onChange(newValues);
}
private onInputChange = (e) => {
const { autocomplete, field, filterCode } = this.props;
if (e.target && typeof e.target.value === 'string') {
if (e.target && e.target.value) {
if (e.target.value !== this.state.text) {
if (e.target.value.length >= 3) {
this.setState({text: e.target.value});
autocomplete(field, e.target.value, filterCode)
.then((autocompleteObj) => {
this.setState({...this.state, autocompleteObj});
});
}
}
}
}
}
private onSuggestionSelected = (e, data, autocompleteInput, that) => {
autocompleteInput.onChange.call(that, '');
const { input } = _.get(this.props, this.props.names[0]);
input.onChange(this.maybeAdd(data.suggestion.value));
}
public render() {
const { placeholder, label, t, names, terms, classes } = this.props;
const { input } = _.get(this.props, names[0]);
const { input: notInput } = _.get(this.props, names[1]);
const { values, notValues } = this.state;
return (
<div>
<Field
name={ `auto-${names[0]}` }
component={ MaterialAutosuggest }
label={ label }
placeholder={ placeholder }
onChange={ debounce(this.onInputChange, 1000) }
suggestions={ this.state.autocompleteObj }
onSuggestionSelected={ this.onSuggestionSelected }
suggestionLabel="label"
className="full-width"
/>
{ ((values && values.length > 0) || (notValues && notValues.length > 0)) &&
<StringList input={ input } notInput={ notInput } removeByIndex={ this.removeByIndex } />
}
{ terms &&
<Properties>
<h5 className="pl-10 pt-1rem mb-10">{ t('common:f.suggestedFilters') }</h5>
{ terms && Array.from(terms).slice(0, 10).map(([key, value]) => (
<PropertiesItem key={ key } title={ key } onClick={ () => input.onChange(this.maybeAdd(key)) } classes={ {...classes, propertiesRow: 'cursor-pointer'} }>
<span className="float-right">
<Number value={ value }/>
</span>
</PropertiesItem>
)) }
</Properties>
}
</div>
);
}
}
interface IAutocompleteFilter extends React.ClassAttributes<any> {
name: string;
placeholder?: string;
label?: string;
options?: { [key: string]: any; };
terms?: { [key: string]: any; };
byKey?: boolean;
autocomplete: (field: string, term: string, filter: string | AccessionFilter) => Promise<Array<LabelValue<string>>>;
filterCode: string;
classes: any;
t: any;
}
class AutocompleteFilter extends React.Component<IAutocompleteFilter, any> {
public render() {
const { name, label, placeholder, options, terms, byKey, autocomplete, filterCode, classes, t } = this.props;
return (
<div>
<Fields
names={ [`${name}`, `NOT.${name}`] }
field={ name }
component={ AutocompleteFilterInternal }
label={ t(label) }
placeholder={ placeholder }
options={ options }