Commit ee4bcf4d authored by Maksym Tishchenko's avatar Maksym Tishchenko
Browse files

Filtering by inventory group

parent eff7a5f3
......@@ -47,6 +47,7 @@ class AccessionFilter {
public taxonomySpecies?: TaxonomySpeciesFilter;
public inventories?: InventoryFilter;
public sources?: AccessionSourceFilter;
public accessionInvGroup?: number[];
public accessionActions?: AccessionActionFilter;
public accessionIprs?: AccessionIprFilter;
public _text?: string;
......
import CooperatorFilter from '@gringlobal-ce/client/model/gringlobal/CooperatorFilter';
import DateFilter from '@gringlobal-ce/client/model/gringlobal/DateFilter';
import StringFilter from "@gringlobal-ce/client/model/common/StringFilter";
/**
* AccessionInvGroupFilter
......@@ -14,6 +15,7 @@ class AccessionInvGroupFilter {
public createdBy?: number[];
public createdDate?: DateFilter;
public modifiedBy?: number[];
public groupName?: StringFilter;
public modifiedDate?: DateFilter;
public ownedBy?: CooperatorFilter;
public ownedDate?: DateFilter;
......
......@@ -50,6 +50,8 @@ interface IAutocompleteProps {
required?: boolean;
withDetails?: (details: any) => React.ReactNode;
autoFocus?: boolean;
onKeyPress?: (event) => void;
onBlur?: (event) => void;
}
interface IAutocompleteState {
......@@ -163,7 +165,7 @@ class Autocomplete extends React.Component<IAutocompleteProps & WithStyles & Wit
};
public render() {
const { label, classes, helperText, renderOption, getOptionLabel, noOptionsText, autoFocus, placeholder, t, required, meta, withDetails } = this.props;
const { label, classes, helperText, onKeyPress, onBlur, renderOption, getOptionLabel, noOptionsText, autoFocus, placeholder, t, required, meta, withDetails } = this.props;
const { error, touched, dirty } = meta;
const { loading, open, options, inputValue } = this.state;
......@@ -180,6 +182,8 @@ class Autocomplete extends React.Component<IAutocompleteProps & WithStyles & Wit
getOptionLabel={ getOptionLabel }
onChange={ this.onChange }
onInputChange={ this.onInputChange }
onKeyPress={ onKeyPress }
onBlur={ onBlur }
options={ options }
loading={ loading }
classes={ { input: classes.input, option: classes.option, popper: classes.popper } }
......
......@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
// UI
import { withFilters, TextFilter, StringArrFilter, NumberFilter, DateFilter, BooleanFilter, CVFilter , SiteFilter } from 'ui/common/filter';
import AccessionInvGroupFilter from "common/AccessionInvGroupFilter";
export function AccessionFiltersEmbedded({ prefix = '', withFullText = true }: { prefix?: string, withFullText?: boolean }) {
const { t } = useTranslation();
......@@ -77,6 +78,10 @@ export function AccessionFiltersEmbedded({ prefix = '', withFullText = true }: {
label={ t([ 'client:model.Accession.initialReceivedDate', 'client:model._.initialReceivedDate' ]) }
name={ `${prefix}initialReceivedDate` }
/>
<AccessionInvGroupFilter
label={ t([ 'client:model.AccessionInvGroup.groupName', 'client:model._.groupName' ]) }
name={ `${prefix}accessionInvGroup` }
/>
<BooleanFilter
label={ t(['client:model.AccessionSource.isOrigin', 'client:model._.isOrigin']) }
name={ `${prefix}sources.origin` }
......
import * as React from 'react';
import { createUseStyles } from 'react-jss';
import { HighlightOff as Clear } from '@material-ui/icons';
import { Field, FieldProps, useForm } from 'react-final-form';
import { FieldArray } from 'react-final-form-arrays';
// ui
import { WithStyles } from '@material-ui/core/styles';
import { InventoryService } from "@gringlobal-ce/client/service";
import {
AccessionInvGroup, AccessionInvGroupFilteredPage,
} from "@gringlobal-ce/client/model/gringlobal";
import Autocomplete from "@gringlobal-ce/client/ui/common/form/Autocomplete";
const useStyles = createUseStyles((theme) => ({
stringArrElement: {
margin: '.2rem 0',
padding: '.2rem 1rem',
backgroundColor: '#e8e5e1',
color: '#202222',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
stringArrElementText: {
display: 'inline-block',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
removeIcon: {
cursor: 'pointer',
color: '#6f6f6f',
fontSize: '20px',
},
}));
interface IAccessionInvGroupFilter extends FieldProps<any, any> {
name: string;
label: string;
placeholder?: string;
}
const StringArrElement = ({ arrElem, remove, classes }: { arrElem: string, remove: () => void } & WithStyles) => {
return <div>
<div className={ classes.stringArrElement }>
<span className={ classes.stringArrElementText }>
{arrElem}
</span>
<div className="font-bold float-right" onClick={ remove }><Clear className={ classes.removeIcon }/></div>
</div>
</div>
};
const AccessionInvGroupFilter = ({ name, label, ...other }: IAccessionInvGroupFilter) => {
// util
const classes = useStyles();
const { mutators, change, getFieldState } = useForm();
const [ groups, setGroups ] = React.useState([])
// effect
React.useEffect(() => {
const fieldValue = getFieldState(name)?.value
if (!fieldValue) {
return
}
InventoryService.filterGroups({ id: fieldValue }, { size: 500 }).then((page) => {
setGroups(page.content);
}).catch((err) => {
console.log('Could not fetch groups list', err);
});
}, []);
// callback
const maybeAdd = React.useCallback((val: AccessionInvGroup) => {
if (!val) {
return;
}
if (getFieldState(name)?.value?.length > 0 && getFieldState(name)?.value.includes(val.id)) {
change('temp', undefined);
return;
}
setGroups([ ...groups, val ])
mutators.push(name, val.id);
change('temp', undefined);
}, [ mutators, groups, setGroups, name ]);
const maybeRemove = React.useCallback((index) => {
mutators.remove(name, index);
}, [ groups, setGroups ])
const handleKeyPress = React.useCallback((event: KeyboardEvent) => {
if (event.key === 'Enter') {
if (getFieldState(`temp.${name}`).value) {
event.preventDefault();
maybeAdd(getFieldState(`temp.${name}`).value);
}
}
}, [ name, maybeAdd ]);
const renderOption = (invGroup: AccessionInvGroup, state?: any) => <>{ invGroup.groupName } </>
const optionLabel = (invGroup: AccessionInvGroup): string => invGroup.groupName;
const mapOptions = (page: AccessionInvGroupFilteredPage) => page.content;
const autocomplete = (text: string) => {
return InventoryService.filterGroups({ groupName: { contains: text } }, { page: 0, size: 20 });
};
const transformOption = (option: AccessionInvGroup | number[]): any => {
return typeof option[0] === 'number' ? {id: option[0]} : option;
}
return (
<>
<Field
name={ `temp.${name}` }
component={ Autocomplete }
label={ label }
autocomplete={ autocomplete }
renderOption={ renderOption }
mapOptions={ mapOptions }
transformOption={ transformOption }
getOptionLabel={ optionLabel }
onBlur={ () => maybeAdd(getFieldState(`temp.${name}`).value) }
onKeyPress={ handleKeyPress }
{ ...other }
/>
<FieldArray name={ name }>
{({ fields }) => (
<>
{fields?.value && fields.value.map((field, index) => {
return <StringArrElement
arrElem={ groups.find((group) => +group.id === +field)?.groupName ?? field }
remove={ () => maybeRemove(index) }
classes={ classes }
key={ `${field}-${index}` }
/>
})}
</>
)}
</FieldArray>
</>
);
}
export default AccessionInvGroupFilter;
......@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
import { Inventory } from '@gringlobal-ce/client/model/gringlobal';
// UI
import { withFilters, TextFilter, StringArrFilter, NumberFilter, DateFilter, BooleanFilter, CVFilter, SiteFilter, IMPFilter } from 'ui/common/filter';
import AccessionInvGroupFilter from "common/AccessionInvGroupFilter";
export function InventoryFiltersEmbedded({ prefix = '', allowSystem = true, withFullText = true }: { prefix?: string, allowSystem?: boolean, withFullText?: boolean }) {
const { t } = useTranslation();
......@@ -46,6 +47,10 @@ export function InventoryFiltersEmbedded({ prefix = '', allowSystem = true, with
label={ t([ 'client:model.Accession.accessionNumber', 'client:model._.accessionNumber' ]) }
name={ `${prefix}accession.accessionNumber` }
/>
<AccessionInvGroupFilter
label={ t([ 'client:model.AccessionInvGroup.groupName', 'client:model._.groupName' ]) }
name={ `${prefix}accessionInvGroup` }
/>
<NumberFilter
label={ t([ 'client:model.Inventory.quantityOnHand' ]) }
name={ `${prefix}quantityOnHand` }
......
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