Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIMSBIOHUB-486: Update Manage Animals Species and Observations Table Species Menus with Lastest Component #1224

Merged
merged 8 commits into from
Mar 1, 2024
2 changes: 1 addition & 1 deletion api/src/openapi/schemas/critter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const critterSchema: OpenAPIV3.SchemaObject = {
wlh_id: {
type: 'string'
},
taxon_id: {
itis_tsn: {
type: 'string'
},
sex: {
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/buttons/HelpButtonTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface HelpButtonTooltipProps {
* @param {HelpButtonTooltipProps}
* @return {*}
*/

//TODO: Update positioning of the tooltip to be more dynamic (Add Animal form)
const HelpButtonTooltip = ({ content, children, iconSx }: HelpButtonTooltipProps) => {
return (
<Box
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export interface IAsyncAutocompleteDataGridEditCell<
* @memberof IAsyncAutocompleteDataGridEditCell
*/
error?: boolean;

renderOption?: (renderProps: any, renderOption: IAutocompleteDataGridOption<ValueType>) => JSX.Element;
KjartanE marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand All @@ -65,7 +67,8 @@ const AsyncAutocompleteDataGridEditCell = <DataGridType extends GridValidRowMode
if (dataGridProps.hasFocus) {
ref.current?.focus();
}
}, [dataGridProps.hasFocus]);
}, [dataGridProps]);

// The current data grid value
const dataGridValue = dataGridProps.value;
// The input field value
Expand Down Expand Up @@ -200,11 +203,14 @@ const AsyncAutocompleteDataGridEditCell = <DataGridType extends GridValidRowMode
/>
)}
renderOption={(renderProps, renderOption) => {
return (
<Box component="li" {...renderProps}>
{renderOption.label}
</Box>
);
if (!props.renderOption) {
return (
<Box component="li" {...renderProps}>
KjartanE marked this conversation as resolved.
Show resolved Hide resolved
{renderOption.label}
</Box>
);
}
return props.renderOption(renderProps, renderOption);
}}
data-testid={dataGridProps.id}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
export interface IAutocompleteDataGridOption<ValueType extends string | number> {
value: ValueType;
label: string;
subtext?: string;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Box } from '@mui/system';
import { GridRenderEditCellParams, GridValidRowModel } from '@mui/x-data-grid';
import AsyncAutocompleteDataGridEditCell from 'components/data-grid/autocomplete/AsyncAutocompleteDataGridEditCell';
import { IAutocompleteDataGridOption } from 'components/data-grid/autocomplete/AutocompleteDataGrid.interface';
import SpeciesCard from 'components/species/components/SpeciesCard';
import { TaxonomyContext } from 'contexts/taxonomyContext';
import { useBiohubApi } from 'hooks/useBioHubApi';
import debounce from 'lodash-es/debounce';
Expand Down Expand Up @@ -67,7 +69,8 @@ const TaxonomyDataGridEditCell = <DataGridType extends GridValidRowModel, ValueT
const response = await biohubApi.taxonomy.searchSpeciesByTerms([searchTerm]);
const options = response.map((item) => ({
value: item.tsn as ValueType,
label: [item.commonName, `(${item.scientificName})`].filter(Boolean).join(' ')
label: [item.commonName, `(${item.scientificName})`].filter(Boolean).join(' '),
subtext: item.scientificName
}));
onSearchResults(options);
},
Expand All @@ -82,6 +85,13 @@ const TaxonomyDataGridEditCell = <DataGridType extends GridValidRowModel, ValueT
getCurrentOption={getCurrentOption}
getOptions={getOptions}
error={props.error}
renderOption={(renderProps, renderOption) => {
return (
<Box component="li" {...renderProps} key={renderOption.value}>
<SpeciesCard name={renderOption.label} subtext={String(renderOption.subtext)} />
</Box>
);
}}
/>
);
};
Expand Down
1 change: 1 addition & 0 deletions app/src/components/species/AncillarySpeciesComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const AncillarySpeciesComponent = () => {
label={'Ancillary Species'}
required={false}
handleAddSpecies={handleAddSpecies}
clearOnSelect={true}
/>
<SelectedSpecies selectedSpecies={selectedSpecies} handleRemoveSpecies={handleRemoveSpecies} />
{errors && get(errors, 'species.ancillary_species') && (
Expand Down
1 change: 1 addition & 0 deletions app/src/components/species/FocalSpeciesComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const FocalSpeciesComponent = () => {
label={'Focal Species'}
required={true}
handleAddSpecies={handleAddSpecies}
clearOnSelect={true}
/>
<SelectedSpecies selectedSpecies={selectedSpecies} handleRemoveSpecies={handleRemoveSpecies} />
{submitCount > 0 && errors && get(errors, 'species.focal_species') && (
Expand Down
61 changes: 37 additions & 24 deletions app/src/components/species/components/SpeciesAutocompleteField.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { mdiMagnify } from '@mdi/js';
import Icon from '@mdi/react';
import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
import Autocomplete from '@mui/material/Autocomplete';
import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
import TextField from '@mui/material/TextField';
import SpeciesCard from 'components/species/components/SpeciesCard';
import { useFormikContext } from 'formik';
import { useBiohubApi } from 'hooks/useBioHubApi';
import { ITaxonomy } from 'interfaces/useTaxonomyApi.interface';
import { debounce } from 'lodash-es';
Expand All @@ -15,16 +15,25 @@ export interface ISpeciesAutocompleteFieldProps {
label: string;
required?: boolean;
handleAddSpecies: (species: ITaxonomy) => void;
value?: string;
/**
* Clear the input value after a selection is made
* Defaults to false
*
* @type {boolean}
* @memberof ISpeciesAutocompleteFieldProps
*/
clearOnSelect?: boolean;
}

const SpeciesAutocompleteField = (props: ISpeciesAutocompleteFieldProps) => {
const { formikFieldName, label, required, handleAddSpecies } = props;
const biohubApi = useBiohubApi();

const { values } = useFormikContext<ITaxonomy[]>();

const [inputValue, setInputValue] = useState('');
const [options, setOptions] = useState<ITaxonomy[]>([]);
// Is control loading (search in progress)
const [isLoading, setIsLoading] = useState(false);

const handleSearch = useMemo(
() =>
Expand All @@ -38,21 +47,28 @@ const SpeciesAutocompleteField = (props: ISpeciesAutocompleteFieldProps) => {
[biohubApi.taxonomy]
);

const searchSpecies = async () => {
useEffect(() => {
let mounted = true;

if (!inputValue) {
setOptions([]);
handleSearch.cancel();
} else {
setIsLoading(true);
handleSearch(inputValue, (newOptions) => {
if (!mounted) {
return;
}

setOptions(newOptions);
setIsLoading(false);
});
}
};

useEffect(() => {
searchSpecies();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [inputValue]);
return () => {
mounted = false;
};
}, [handleSearch, inputValue]);

return (
<Autocomplete
Expand All @@ -65,24 +81,15 @@ const SpeciesAutocompleteField = (props: ISpeciesAutocompleteFieldProps) => {
isOptionEqualToValue={(option, value) => {
return option.tsn === value.tsn;
}}
filterOptions={(options, state) => {
const searchFilter = createFilterOptions<ITaxonomy>({ ignoreCase: true });
if (!values?.length) {
return options;
}

const unselectedOptions = options.filter((item) => {
return !values.some((existing) => existing.tsn === item.tsn);
});
return searchFilter(unselectedOptions, state);
}}
filterOptions={(item) => item}
inputValue={inputValue}
onInputChange={(_, value, reason) => {
if (reason === 'reset') {
if (props.clearOnSelect && reason === 'reset') {
setInputValue('');
} else {
setInputValue(value);
return;
}

setInputValue(value);
}}
onChange={(_, option) => {
if (option) {
Expand Down Expand Up @@ -114,6 +121,12 @@ const SpeciesAutocompleteField = (props: ISpeciesAutocompleteFieldProps) => {
<Box mx={1} mt="6px">
<Icon path={mdiMagnify} size={1}></Icon>
</Box>
),
endAdornment: (
<>
{isLoading ? <CircularProgress color="inherit" size={20} /> : null}
{params.InputProps.endAdornment}
</>
)
}}
/>
Expand Down
4 changes: 2 additions & 2 deletions app/src/features/surveys/telemetry/ManualTelemetryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ const ManualTelemetryList = () => {
{item.animal_id}
</Typography>
<Typography variant="subtitle2" color="textSecondary">
{item.taxon} - {item.sex}
{item.itis_scientific_name} - {item.sex}
</Typography>
</Box>
</MenuItem>
Expand Down Expand Up @@ -565,7 +565,7 @@ const ManualTelemetryList = () => {
key={`${item.deployment.device_id}:${item.deployment.device_make}:${item.deployment.attachment_start}`}
device_id={item.deployment.device_id}
device_make={item.deployment.device_make}
name={String(item.critter.animal_id ?? item.critter.taxon)}
name={String(item.critter.animal_id ?? item.critter.itis_scientific_name)}
start_date={item.deployment.attachment_start}
end_date={item.deployment.attachment_end}
onMenu={(event, id) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const mockCritterbaseApi = useCritterbaseApi as jest.Mock;

const mockValues: IAnimal = {
general: {
taxon_id: 'taxon',
taxon_name: 'taxon_name',
itis_tsn: 'itis_tsn',
itis_scientific_name: 'itis_scientific_name',
animal_id: 'alias',
critter_id: 'critter',
sex: AnimalSex.UNKNOWN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ export const AddEditAnimal = (props: IAddEditAnimalProps) => {
}, [critterData]);

useEffect(() => {
refreshMeasurements(values.general.taxon_id);
refreshMeasurements(values.general.itis_tsn);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [values.general.taxon_id]);
}, [values.general.itis_tsn]);

useEffect(() => {
if (!status?.success && status?.msg) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const AnimalList = (props: IAnimalListProps) => {
{critter.animal_id}
</Typography>
<Typography variant="body2" color="textSecondary">
{critter.taxon} | {critter.sex}
{critter.itis_scientific_name} | {critter.sex}
</Typography>
</Box>
</AccordionSummary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const AnimalSectionDataCards = (props: IAnimalSectionDataCardsProps) => {
{
header: `General: ${initialValues.general.animal_id}`,
subHeader: formatSubHeader({
Taxon: initialValues.general.taxon_name,
Taxon: initialValues.general.itis_scientific_name,
Sex: initialValues.general.sex,
'WLH ID': initialValues.general.wlh_id
}),
Expand Down Expand Up @@ -141,7 +141,7 @@ export const AnimalSectionDataCards = (props: IAnimalSectionDataCardsProps) => {
return sectionData[section];
}, [
initialValues.general.animal_id,
initialValues.general.taxon_name,
initialValues.general.itis_scientific_name,
initialValues.general.sex,
initialValues.markings,
initialValues.measurements,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const GeneralAnimalSummary = (props: GeneralAnimalSummaryProps) => {

const animalGeneralDetails: Array<IGeneralDetail> = [
{ title: 'Alias', value: general.animal_id },
{ title: 'Taxon', value: general.taxon_name },
{ title: 'Taxon', value: general.itis_scientific_name },
{ title: 'Sex', value: general.sex },
{ title: 'Wildlife Health ID', value: general.wlh_id },
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ export const SurveyAnimalsPage = () => {

const defaultFormValues: IAnimal = useMemo(() => {
return {
general: { wlh_id: '', taxon_id: '', taxon_name: '', animal_id: '', sex: AnimalSex.UNKNOWN, critter_id: '' },
general: {
wlh_id: '',
itis_tsn: '',
itis_scientific_name: '',
animal_id: '',
sex: AnimalSex.UNKNOWN,
critter_id: ''
},
captures: [],
markings: [],
mortality: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface ISurveyAnimalsTableEntry {
survey_critter_id: number;
critter_id: string;
animal_id: string | null;
taxon: string;
itis_scientific_name: string;
deployments?: IAnimalDeployment[];
}

Expand Down Expand Up @@ -46,7 +46,7 @@ export const SurveyAnimalsTable = ({

const columns: GridColDef<ISurveyAnimalsTableEntry>[] = [
{
field: 'taxon',
field: 'itis_scientific_name',
headerName: 'Species',
flex: 1
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('animal form helpers', () => {
const detailedResponse: IDetailedCritterWithInternalId = {
survey_critter_id: 1,
critter_id: 'c8601a4a-3946-4d1a-8c3f-a07088112284',
taxon_id: '93ced109-d806-4851-90d7-064951cfc4f5',
itis_tsn: '93ced109-d806-4851-90d7-064951cfc4f5',
wlh_id: 'abc',
animal_id: 'def',
sex: 'Male',
Expand All @@ -26,7 +26,7 @@ describe('animal form helpers', () => {
create_timestamp: '2022-02-02',
update_timestamp: '2022-02-02',
critter_comment: '',
taxon: 'Caribou',
itis_scientific_name: 'Caribou',
responsible_region: 'Montana',
mortality_timestamp: null,
collection_units: [
Expand Down Expand Up @@ -175,7 +175,7 @@ describe('animal form helpers', () => {
expect(result.general.critter_id).toBe('c8601a4a-3946-4d1a-8c3f-a07088112284');
expect(result.general.sex).toBe('Male');
expect(result.general.animal_id).toBe('def');
expect(result.general.taxon_id).toBe('93ced109-d806-4851-90d7-064951cfc4f5');
expect(result.general.itis_tsn).toBe('93ced109-d806-4851-90d7-064951cfc4f5');
expect(result.captures.length).toBe(1);
expect(result.markings.length).toBe(1);
expect(result.mortality.length).toBe(1);
Expand Down Expand Up @@ -267,9 +267,9 @@ describe('animal form helpers', () => {
const initialFormValues: IAnimal = {
general: {
wlh_id: 'wlh-a',
taxon_id: '',
itis_tsn: '',
animal_id: '',
taxon_name: undefined,
itis_scientific_name: undefined,
sex: undefined,
critter_id: undefined
},
Expand All @@ -288,9 +288,9 @@ describe('animal form helpers', () => {

const currentFormValues: IAnimal = {
general: {
taxon_id: '',
itis_tsn: '',
animal_id: '',
taxon_name: undefined,
itis_scientific_name: undefined,
wlh_id: 'wlh-b',
sex: undefined,
critter_id: undefined
Expand Down
Loading
Loading