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

Bug fix: Allow for captures without locations on the Manage Animals page #1318

Merged
merged 7 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { mdiAlertRhombusOutline } from '@mdi/js';
import Icon from '@mdi/react';
import Box from '@mui/material/Box';
import { orange } from '@mui/material/colors';
import Skeleton from '@mui/material/Skeleton';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { SkeletonHorizontalStack } from 'components/loading/SkeletonLoaders';
import { AnimalCaptureCardContainer } from 'features/surveys/animals/profile/captures/components/AnimalCaptureCardContainer';
import { AnimalCapturesToolbar } from 'features/surveys/animals/profile/captures/components/AnimalCapturesToolbar';
Expand Down Expand Up @@ -100,6 +105,8 @@ export const AnimalCaptureContainer = () => {
animalPageContext.critterDataLoader.refresh(critterbase_critter_id);
};

const capturesWithLocation = captures.filter((capture) => capture.capture_location);

return (
<>
<AnimalCapturesToolbar
Expand All @@ -110,7 +117,16 @@ export const AnimalCaptureContainer = () => {
);
}}
/>
{captures.length > 0 && <AnimalCapturesMap captures={captures} isLoading={false} />}
{capturesWithLocation.length < captures.length && (
<Stack gap={1} direction="row" alignItems="center" display="flex" px={3} py={2} bgcolor={orange[50]}>
<Icon path={mdiAlertRhombusOutline} size={1} color={orange[800]} />
<Typography color={orange[800]} variant="body2">
Not all captures are visible on the map due to missing location data. Please update these captures with
location information.
</Typography>
</Stack>
)}
{captures.length > 0 && <AnimalCapturesMap captures={capturesWithLocation} isLoading={false} />}
<AnimalCaptureCardContainer captures={captures} selectedAnimal={selectedAnimal} handleDelete={handleDelete} />
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export interface ICaptureLocationMapControlProps {
/**
* Capture location map control component.
*
* This component can be used to record a Point location on a map, for either a capture or release location.
*
* @param {ICaptureLocationMapControlProps} props
* @return {*}
*/
Expand Down Expand Up @@ -83,8 +85,10 @@ export const CaptureLocationMapControl = <FormikValuesType extends ICreateCaptur
const coordinates = captureLocationGeoJson && getCoordinatesFromGeoJson(captureLocationGeoJson);

// Initialize state based on formik context for the edit page
const [latitudeInput, setLatitudeInput] = useState<string>(coordinates ? String(coordinates.latitude) : '');
const [longitudeInput, setLongitudeInput] = useState<string>(coordinates ? String(coordinates.longitude) : '');
const [latitudeInput, setLatitudeInput] = useState<string>(coordinates?.latitude ? String(coordinates.latitude) : '');
const [longitudeInput, setLongitudeInput] = useState<string>(
coordinates?.longitude ? String(coordinates.longitude) : ''
);

const [updatedBounds, setUpdatedBounds] = useState<LatLngBoundsExpression | undefined>(undefined);

Expand Down Expand Up @@ -120,9 +124,10 @@ export const CaptureLocationMapControl = <FormikValuesType extends ICreateCaptur
return;
}

drawControlsRef.current?.clearLayers();

// If coordinates are invalid, reset the map to show nothing
if (!isValidCoordinates(lat, lon)) {
drawControlsRef.current?.clearLayers();
setUpdatedBounds(calculateUpdatedMapBounds([ALL_OF_BC_BOUNDARY]));
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import { CaptureLocationMapControl } from './CaptureLocationMapControl';
* @return {*}
*/
export const ReleaseLocationForm = <FormikValuesType extends ICreateCaptureRequest | IEditCaptureRequest>() => {
const { values } = useFormikContext<FormikValuesType>();
const { values, setFieldValue } = useFormikContext<FormikValuesType>();

// Determine if the release location is the same as the capture location
// If the release location is the same as the capture location, we don't need to show the release location map control
const [isReleaseSameAsCapture, setIsReleaseSameAsCapture] = useState<boolean>(
!(values.capture.release_location && values.capture.capture_location) ||
booleanEqual(values.capture.release_location, values.capture.capture_location)
Expand All @@ -30,7 +32,13 @@ export const ReleaseLocationForm = <FormikValuesType extends ICreateCaptureReque
<RadioGroup
aria-label="release-location"
value={isReleaseSameAsCapture}
onChange={(event) => setIsReleaseSameAsCapture(event.target.value === 'true')}>
onChange={(event) => {
if (event.target.value) {
// Clear the release location if it is the same as the capture location
setFieldValue('capture.release_location', null);
}
setIsReleaseSameAsCapture(event.target.value === 'true');
}}>
<FormControlLabel value="true" control={<Radio color="primary" />} label="Yes" />
<FormControlLabel value="false" control={<Radio color="primary" />} label="No" />
</RadioGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export const EditCapturePage = () => {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [capture.capture_location.longitude ?? 0, capture.capture_location.latitude ?? 0]
coordinates: [capture?.capture_location?.longitude ?? 0, capture?.capture_location?.latitude ?? 0]
},
properties: {}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,11 @@ export const AnimalCaptureCardContainer = (props: IAnimalCaptureCardContainer) =
)}
&nbsp;
</Typography>
{capture.capture_location.latitude && capture.capture_location.longitude && (
{capture.capture_location?.latitude && capture.capture_location?.longitude && (
<Box>
<Typography color="textSecondary" variant="body2">
{capture.capture_location.latitude},&nbsp;
{capture.capture_location.longitude}
{capture.capture_location?.latitude},&nbsp;
{capture.capture_location?.longitude}
</Typography>
</Box>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,26 @@ interface IAnimalCapturesMapProps {
export const AnimalCapturesMap = (props: IAnimalCapturesMapProps) => {
const { captures, isLoading } = props;

const captureMapFeatures = captures
.filter((capture) => isDefined(capture.capture_location?.latitude) && isDefined(capture.capture_location.longitude))
.map((capture) => ({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [capture.capture_location.longitude, capture.capture_location.latitude]
},
properties: { captureId: capture.capture_id, date: capture.capture_date }
})) as Feature[];
// Only include captures with valid locations
const captureMapFeatures: Feature[] = [];

for (const capture of captures) {
if (
capture.capture_location &&
isDefined(capture.capture_location.latitude) &&
isDefined(capture.capture_location.longitude)
) {
const feature: Feature = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [capture.capture_location.longitude, capture.capture_location.latitude]
},
properties: { captureId: capture.capture_id, date: capture.capture_date }
};
captureMapFeatures.push(feature);
}
}

const staticLayers: IStaticLayer[] = captureMapFeatures.map((feature, index) => ({
layerName: 'Captures',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ export const CaptureDetails = (props: ICaptureDetailsProps) => {
const captureLocation = capture.capture_location;
const captureComment = capture.capture_comment;

if (!captureDate && (!captureLocation.latitude || !captureLocation.longitude) && !captureComment) {
return null;
}

return (
<Stack gap={2}>
<Stack direction="row" spacing={3}>
Expand All @@ -52,9 +48,11 @@ export const CaptureDetails = (props: ICaptureDetailsProps) => {
sx={{ textTransform: 'uppercase', mb: 0.5 }}>
Capture location
</Typography>
<Typography color="textSecondary" variant="body2">
{captureLocation.latitude},&nbsp;{captureLocation.longitude}
</Typography>
{captureLocation && (
<Typography color="textSecondary" variant="body2">
{captureLocation.latitude},&nbsp;{captureLocation.longitude}
</Typography>
)}
</Box>
</Stack>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ export const ReleaseDetails = (props: IReleaseDetailsProps) => {
const releaseLocation = capture.release_location;
const releaseComment = capture.release_comment;

if (!releaseDate && !releaseLocation && !releaseComment) {
return null;
}

return (
<Stack gap={2}>
<Stack direction="row" spacing={3}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ export const MeasurementDetails = (props: IMeasurementDetailsProps) => {
<Box maxHeight="300px" sx={{ overflow: 'auto', pr: 1 }}>
{allMeasurements.map((measurement) => (
<Paper variant="outlined" sx={{ px: 3, py: 2, bgcolor: grey[100], mt: 1 }} key={v4()}>
<Typography fontWeight={700}>
{startCase(measurement.measurement_name)}: <Typography component="span">{measurement.value}</Typography>
<Typography fontWeight={700} variant="body2">
{startCase(measurement.measurement_name)}:{' '}
<Typography component="span" variant="body2">
{measurement.value}
</Typography>
</Typography>
</Paper>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const AnimalAttributeItem = (props: IAnimalAttributeItemProps) => {
return (
<Box display="flex" alignItems="center">
{props.startIcon && <Icon path={props.startIcon} size={0.8} color={grey[500]} />}
<Typography variant="body1" color="textSecondary" sx={{ ml: 0.5 }}>
<Typography variant="body2" color="textSecondary" sx={{ ml: 0.5 }}>
{props.text}
</Typography>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mdiCheckboxMultipleBlankOutline, mdiInformationOutline, mdiPlusBoxOutline } from '@mdi/js';
import { mdiCheckboxMultipleBlankOutline, mdiInformationOutline } from '@mdi/js';
import { Icon } from '@mdi/react';
import Box from '@mui/material/Box';
import green from '@mui/material/colors/green';
Expand Down Expand Up @@ -60,7 +60,6 @@ export const AnimalProfileHeader = (props: IAnimalProfileHeaderProps) => {
}
startIcon={mdiInformationOutline}
/>
{critter.wlh_id && <AnimalAttributeItem text={critter.wlh_id} startIcon={mdiPlusBoxOutline} />}
<Box mt={1}>
<ColouredRectangleChip
label={critter.mortality.length ? 'Deceased' : 'Alive'}
Expand Down Expand Up @@ -92,14 +91,16 @@ export const AnimalProfileHeader = (props: IAnimalProfileHeaderProps) => {
</Box>
<Divider sx={{ my: 2 }} />
<Stack direction="row" gap={3} flex="1 1 auto">
<Box>
<Typography component="dt" variant="body2" fontWeight={500} color="textSecondary">
Sex
</Typography>
<Typography component="dd" variant="body2">
{critter.sex}
</Typography>
</Box>
{critter.wlh_id && (
<Box>
<Typography component="dt" variant="body2" fontWeight={500} color="textSecondary">
Wildlife Health ID
</Typography>
<Typography component="dd" variant="body2">
{critter.wlh_id}
</Typography>
</Box>
)}
{critter.collection_units.map((unit, index) => (
<Box key={`${unit.collection_category_id}-${index}`}>
<Typography component="dt" variant="body2" fontWeight={500} color="textSecondary">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const MarkingCard = (props: IMarkingCardProps) => {
return (
<Paper variant="outlined" sx={{ px: 3, py: 2, bgcolor: grey[100] }}>
<Box position="relative" display="flex">
<Typography component="dd" fontWeight={700}>
<Typography component="dd" fontWeight={700} variant="body2">
{startCase(marking_type_label)}
</Typography>
{editable && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const CaptureAnimalForm = (props: AnimalFormProps<ICaptureResponse>) => {
capture_id: props?.formObject?.capture_id,
critter_id: props.critter.critter_id,
capture_location: {
location_id: props?.formObject?.capture_location.location_id,
location_id: props?.formObject?.capture_location?.location_id,
latitude: props?.formObject?.capture_location?.latitude ?? ('' as unknown as number),
longitude: props?.formObject?.capture_location?.longitude ?? ('' as unknown as number),
coordinate_uncertainty:
Expand Down
4 changes: 2 additions & 2 deletions app/src/interfaces/useCritterApi.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ export type ICaptureResponse = {
release_time: string | null;
capture_comment: string | null;
release_comment: string | null;
capture_location: ILocationResponse;
release_location: ILocationResponse | null | undefined;
capture_location?: ILocationResponse | null;
release_location?: ILocationResponse | null;
};

export type IMarkingResponse = {
Expand Down