Skip to content

Commit

Permalink
Merge pull request #3527 from LiteFarmOrg/LF-4481/Add_Location_to_ani…
Browse files Browse the repository at this point in the history
…mal_inventory_UI

LF-4481:  Add Location to animal inventory UI
  • Loading branch information
antsgar authored Dec 2, 2024
2 parents c269228 + 566f31e commit e54dfd3
Show file tree
Hide file tree
Showing 15 changed files with 140 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ export type AnimalSingleViewHeaderProps = {
onEdit: () => void;
onRemove: () => void;
onBack: () => void;
animalOrBatch: (Animal | AnimalBatch) & { location?: string }; // TODO: LF-4481
animalOrBatch: Animal | AnimalBatch;
locationText?: string;
defaultTypes: DefaultAnimalType[];
customTypes: CustomAnimalType[];
defaultBreeds: DefaultAnimalBreed[];
Expand All @@ -140,6 +141,7 @@ const AnimalSingleViewHeader = ({
onRemove,
onBack,
animalOrBatch,
locationText,
defaultTypes,
customTypes,
defaultBreeds,
Expand All @@ -163,7 +165,7 @@ const AnimalSingleViewHeader = ({
<AnimalImageWithCount photoUrl={photo_url} count={count} isCompactView={isCompactView} />
);
const age = <Age birthDate={birth_date} t={t} />;
const location = <Location location={animalOrBatch.location} t={t} />;
const location = <Location location={locationText} t={t} />;

const menuOptions = [
{ label: <MenuItem iconName="EDIT" text={t('ADD_ANIMAL.EDIT_BASIC_INFO')} />, onClick: onEdit },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const HoverPillOverFlow = ({ items, noneText = '' }: HoverPillOverflowProps) =>
return (
<div className={clsx(styles.text)}>
{items.length === 0 ? (
<span className={styles.italics}>{noneText}</span>
<span>{noneText}</span>
) : (
<span className={styles.marginRight8px}>{items[0]}</span>
)}
Expand Down
10 changes: 3 additions & 7 deletions packages/webapp/src/components/Table/Cell/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
font-weight: 400;
}

.overflowText, .overflowText div {
.overflowText,
.overflowText div {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
Expand Down Expand Up @@ -45,7 +46,6 @@
width: 32px;
padding: 4px;
@include svgColorFill(var(--Colors-Primary-Primary-teal-300));

}

.withSubtextText {
Expand Down Expand Up @@ -73,7 +73,7 @@
padding: 4px;
border-radius: 2px;
background: var(--Colors-Accent---singles-Purple-light);
box-shadow: 1px 1px 0px 0px #FFF;
box-shadow: 1px 1px 0px 0px #fff;
color: var(--Colors-Accent---singles-Purple-full);
font-size: 12px;
font-weight: 700;
Expand All @@ -84,7 +84,3 @@
align-items: center;
gap: 8px;
}

.italics {
font-style: italic;
}
26 changes: 25 additions & 1 deletion packages/webapp/src/containers/Animals/Inventory/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import useExpandable from '../../../components/Expandable/useExpandableItem';
import clsx from 'clsx';
import AnimalsBetaSpotlight from './AnimalsBetaSpotlight';
import { sumObjectValues } from '../../../util';
import Icon from '../../../components/Icons';

const HEIGHTS = {
filterAndSearch: 64,
Expand Down Expand Up @@ -321,6 +322,22 @@ export default function AnimalInventory({
),
sortable: false,
},
{
id: isDesktop ? 'location' : null,
label: t('ANIMAL.ANIMAL_LOCATIONS').toLocaleUpperCase(),
format: (d: AnimalInventoryType) => (
<div className={clsx(styles.location, !d.location && styles.unknown)}>
{d.location ? (
<>
<Icon iconName="LOCATION" className={styles.locationIcon} />
<span className={styles.locationText}>{d.location}</span>
</>
) : (
t('common:UNKNOWN')
)}
</div>
),
},
{
id: showLinks ? 'path' : null,
label: '',
Expand All @@ -337,7 +354,14 @@ export default function AnimalInventory({
);

const makeAnimalsSearchableString = (animal: AnimalInventoryType) => {
return [animal.identification, animal.type, animal.breed, ...animal.groups, animal.count]
return [
animal.identification,
animal.type,
animal.breed,
...animal.groups,
animal.count,
animal.location,
]
.filter(Boolean)
.join(' ');
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
height: auto;
overflow-y: auto;
}

.taskViewMaxHeight {
max-height: calc(100vh - var(--global-navbar-height) - var(--global-multi-step-task-layout-aggregated-height));
max-height: calc(
100vh - var(--global-navbar-height) - var(--global-multi-step-task-layout-aggregated-height)
);
}

.summaryViewHeight {
Expand Down Expand Up @@ -105,4 +107,28 @@

.disableHover {
pointer-events: none;
}
}

.location {
display: flex;
align-items: center;
gap: 4px;
font-size: 14px;
font-weight: 600;
color: var(--Colors-Neutral-Neutral-600);

&.unknown {
font-weight: normal;
}
}

.locationIcon {
padding: 0;
min-width: 16px;
background-color: transparent;
}

.locationText {
width: calc(100% - 20px); // icon width 16px + gap 4px
@include truncateText();
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ import { AnimalOrBatchKeys } from '../types';
import { generateInventoryId } from '../../../util/animal';
import { AnimalTypeIconKey, isAnimalTypeIconKey } from '../../../components/Icons/icons';
import { createSingleAnimalViewURL } from '../../../util/siteMapConstants';
import { useSelector } from 'react-redux';
import { locationsSelector } from '../../locationSlice';
import { Location } from '../../../types';

export type AnimalInventory = {
id: string;
Expand All @@ -51,12 +54,14 @@ export type AnimalInventory = {
count: number;
batch: boolean;
group_ids: number[];
location: string;
sex_id?: number;
sex_detail?: { sex_id: number; count: number }[];
custom_type_id: number | null;
default_type_id: number | null;
custom_breed_id: number | null;
default_breed_id: number | null;
location_id?: string | null;
};

const { t } = i18n;
Expand Down Expand Up @@ -139,6 +144,7 @@ const formatAnimalsData = (
customAnimalTypes: CustomAnimalType[],
defaultAnimalBreeds: DefaultAnimalBreed[],
defaultAnimalTypes: DefaultAnimalType[],
locationsMap: { [key: string]: string },
): AnimalInventory[] => {
return animals
.filter(
Expand All @@ -157,13 +163,15 @@ const formatAnimalsData = (
path: createSingleAnimalViewURL(animal.internal_identifier),
count: 1,
batch: false,
location: animal.location_id ? locationsMap[animal.location_id] : '',
// preserve some untransformed data for filtering
group_ids: animal.group_ids,
sex_id: animal.sex_id,
custom_type_id: animal.custom_type_id,
default_type_id: animal.default_type_id,
custom_breed_id: animal.custom_breed_id,
default_breed_id: animal.default_breed_id,
location_id: animal.location_id,
};
});
};
Expand All @@ -175,6 +183,7 @@ const formatAnimalBatchesData = (
customAnimalTypes: CustomAnimalType[],
defaultAnimalBreeds: DefaultAnimalBreed[],
defaultAnimalTypes: DefaultAnimalType[],
locationsMap: { [key: string]: string },
): AnimalInventory[] => {
return animalBatches
.filter(
Expand All @@ -193,13 +202,15 @@ const formatAnimalBatchesData = (
path: createSingleAnimalViewURL(batch.internal_identifier),
count: batch.count,
batch: true,
location: batch.location_id ? locationsMap[batch.location_id] : '',
// preserve some untransformed data for filtering
group_ids: batch.group_ids,
sex_detail: batch.sex_detail,
custom_type_id: batch.custom_type_id,
default_type_id: batch.default_type_id,
custom_breed_id: batch.custom_breed_id,
default_breed_id: batch.default_breed_id,
location_id: batch.location_id,
};
});
};
Expand All @@ -212,6 +223,7 @@ interface BuildInventoryArgs {
customAnimalTypes: CustomAnimalType[];
defaultAnimalBreeds: DefaultAnimalBreed[];
defaultAnimalTypes: DefaultAnimalType[];
locationsMap: { [key: string]: string };
}

export const buildInventory = ({
Expand All @@ -222,6 +234,7 @@ export const buildInventory = ({
customAnimalTypes,
defaultAnimalBreeds,
defaultAnimalTypes,
locationsMap,
}: BuildInventoryArgs) => {
const inventory = [
...formatAnimalsData(
Expand All @@ -231,6 +244,7 @@ export const buildInventory = ({
customAnimalTypes,
defaultAnimalBreeds,
defaultAnimalTypes,
locationsMap,
),
...formatAnimalBatchesData(
animalBatches,
Expand All @@ -239,6 +253,7 @@ export const buildInventory = ({
customAnimalTypes,
defaultAnimalBreeds,
defaultAnimalTypes,
locationsMap,
),
];

Expand Down Expand Up @@ -269,6 +284,12 @@ const useAnimalInventory = () => {
defaultAnimalTypes,
} = data;

const locations: Location[] = useSelector(locationsSelector);
const locationsMap = locations?.reduce(
(map, { location_id, name }) => ({ ...map, [location_id]: name }),
{},
);

const inventory = useMemo(() => {
if (isLoading) {
return [];
Expand All @@ -280,7 +301,8 @@ const useAnimalInventory = () => {
customAnimalBreeds &&
customAnimalTypes &&
defaultAnimalBreeds &&
defaultAnimalTypes
defaultAnimalTypes &&
locationsMap
) {
return buildInventory({
animals,
Expand All @@ -290,6 +312,7 @@ const useAnimalInventory = () => {
customAnimalTypes,
defaultAnimalBreeds,
defaultAnimalTypes,
locationsMap,
});
}
return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ export const useFilteredInventory = (
isInactive(groupsFilter) ||
entity.group_ids.some((groupId) => groupsFilter[groupId]?.active);

// *** Location is not yet implemented as a property on animal or batch ***
const locationMatches = isInactive(locationsFilter);
// const locationMatches =
// isInactive(locationsFilter) || locationsFilter[entity.location]?.active;
const locationMatches =
isInactive(locationsFilter) ||
(entity.location_id && locationsFilter[entity.location_id]?.active);

return (
animalOrBatchMatches &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
useGetDefaultAnimalBreedsQuery,
useGetDefaultAnimalTypesQuery,
} from '../../../store/api/apiSlice';
import { locationsSelector } from '../../locationSlice';
import {
formatAnimalDetailsToDBStructure,
formatBatchDetailsToDBStructure,
Expand All @@ -44,7 +45,7 @@ import { AnimalDetailsFormFields } from '../AddAnimals/types';
import RemoveAnimalsModal, { FormFields } from '../../../components/Animals/RemoveAnimalsModal';
import useAnimalOrBatchRemoval from '../Inventory/useAnimalOrBatchRemoval';
import { generateInventoryId } from '../../../util/animal';
import { CustomRouteComponentProps } from '../../../types';
import { CustomRouteComponentProps, Location } from '../../../types';
import { isAdminSelector } from '../../userFarmSlice';

export const STEPS = {
Expand Down Expand Up @@ -90,6 +91,7 @@ function SingleAnimalView({ isCompactSideMenu, history, match, location }: AddAn

// Form setup
const dispatch = useDispatch();
const locations: Location[] = useSelector(locationsSelector);

const getFormSteps = () => [
{
Expand All @@ -106,6 +108,9 @@ function SingleAnimalView({ isCompactSideMenu, history, match, location }: AddAn
});

const isRemoved = !!defaultFormValues?.animal_removal_reason_id;
const locationText = locations?.find(
({ location_id }) => location_id === defaultFormValues?.location_id,
)?.name;

useEffect(() => {
if (!isFetchingAnimalsOrBatches && !selectedAnimal && !selectedBatch) {
Expand Down Expand Up @@ -201,6 +206,7 @@ function SingleAnimalView({ isCompactSideMenu, history, match, location }: AddAn
onBack={history.back}
/* @ts-ignore */
animalOrBatch={defaultFormValues}
locationText={locationText}
defaultBreeds={defaultAnimalBreeds}
defaultTypes={defaultAnimalTypes}
customBreeds={customAnimalBreeds}
Expand Down
3 changes: 2 additions & 1 deletion packages/webapp/src/containers/Filter/Animals/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import FilterGroup from '../../../components/Filter/FilterGroup';
import type { ReduxFilterEntity, ContainerOnChangeCallback, Location, FilterState } from '../types';
import type { ReduxFilterEntity, ContainerOnChangeCallback, FilterState } from '../types';
import { Location } from '../../../types';
import { FilterType, type ComponentFilter } from '../../../components/Filter/types';
import {
useGetDefaultAnimalTypesQuery,
Expand Down
8 changes: 0 additions & 8 deletions packages/webapp/src/containers/Filter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,3 @@ export interface FilterState {
}

export type ContainerOnChangeCallback = (filterKey: string, filterState: FilterState) => void;

// Only typing the properties relevant to filtering on location; there are no .ts files yet related to this entity
export interface Location {
name: string;
location_id: string;

[key: string]: any;
}
2 changes: 2 additions & 0 deletions packages/webapp/src/store/api/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface Animal {
animal_removal_reason_id: number | null;
removal_explanation: string | null;
removal_date: string | null;
location_id: string | null;
type_name?: string; // request only
breed_name?: string; // request only
}
Expand Down Expand Up @@ -92,6 +93,7 @@ export interface AnimalBatch {
animal_removal_reason_id: number | null;
removal_explanation: string | null;
removal_date: string | null;
location_id: string | null;
type_name?: string; // request only
breed_name?: string; // request only
}
Expand Down
Loading

0 comments on commit e54dfd3

Please sign in to comment.