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

PSP-8094, PSP-8095 Warn user that property is part of an existing acquisition/research file #3931

Closed
wants to merge 6 commits into from
Closed
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
Expand Up @@ -16,6 +16,7 @@ import { useMapStateMachine } from '@/components/common/mapFSM/MapStateMachineCo
import { FileTypes } from '@/constants/index';
import { InventoryTabNames } from '@/features/mapSideBar/property/InventoryTabs';
import { useAcquisitionProvider } from '@/hooks/repositories/useAcquisitionProvider';
import { usePropertyAssociations } from '@/hooks/repositories/usePropertyAssociations';
import { useQuery } from '@/hooks/use-query';
import useApiUserOverride from '@/hooks/useApiUserOverride';
import { getCancelModalProps, useModalContext } from '@/hooks/useModalContext';
Expand Down Expand Up @@ -84,6 +85,7 @@ export const AcquisitionContainer: React.FunctionComponent<IAcquisitionContainer
} = useAcquisitionProvider();

const { setModalContent, setDisplayModal } = useModalContext();
const { execute: getPropertyAssociations } = usePropertyAssociations();
const mapMachine = useMapStateMachine();

const formikRef = useRef<FormikProps<any>>(null);
Expand Down Expand Up @@ -263,6 +265,19 @@ export const AcquisitionContainer: React.FunctionComponent<IAcquisitionContainer
return true;
};

// Warn user that property is part of an existing acquisition file
const confirmBeforeAdd = useCallback(
async (propertyId: number) => {
const response = await getPropertyAssociations(propertyId);
const acquisitionAssociations = response?.acquisitionAssociations ?? [];
const otherAcqFiles = acquisitionAssociations.filter(
a => exists(a.id) && a.id !== acquisitionFileId,
);
return otherAcqFiles.length > 0;
},
[getPropertyAssociations, acquisitionFileId],
);

const onUpdateProperties = (
file: ApiGen_Concepts_File,
): Promise<ApiGen_Concepts_File | undefined> => {
Expand Down Expand Up @@ -321,6 +336,7 @@ export const AcquisitionContainer: React.FunctionComponent<IAcquisitionContainer
onCancelConfirm={handleCancelConfirm}
onUpdateProperties={onUpdateProperties}
onSuccess={onSuccess}
confirmBeforeAdd={confirmBeforeAdd}
canRemove={canRemove}
formikRef={formikRef}
isFormValid={isValid}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import { mockNotesResponse } from '@/mocks/noteResponses.mock';
import { getUserMock } from '@/mocks/user.mock';
import { lookupCodesSlice } from '@/store/slices/lookupCodes';
import { prettyFormatUTCDate } from '@/utils';
import { act, render, RenderOptions, userEvent, waitFor } from '@/utils/test-utils';
import { RenderOptions, act, render, userEvent, waitFor } from '@/utils/test-utils';

import { getMockApiTakes } from '@/mocks/takes.mock';
import { SideBarContextProvider } from '../context/sidebarContext';
import { FileTabType } from '../shared/detail/FileTabs';
import AcquisitionView, { IAcquisitionViewProps } from './AcquisitionView';
import { getMockApiTakes } from '@/mocks/takes.mock';

// mock auth library
jest.mock('@react-keycloak/web');
Expand All @@ -50,6 +50,7 @@ const onMenuChange = jest.fn();
const onSuccess = jest.fn();
const onCancelConfirm = jest.fn();
const onUpdateProperties = jest.fn();
const confirmBeforeAdd = jest.fn();
const canRemove = jest.fn();
const setContainerState = jest.fn();
const setIsEditing = jest.fn();
Expand All @@ -73,6 +74,7 @@ const DEFAULT_PROPS: IAcquisitionViewProps = {
onSuccess,
onCancelConfirm,
onUpdateProperties,
confirmBeforeAdd,
canRemove,
isEditing: false,
setIsEditing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface IAcquisitionViewProps {
onSuccess: () => void;
onCancelConfirm: () => void;
onUpdateProperties: (file: ApiGen_Concepts_File) => Promise<ApiGen_Concepts_File | undefined>;
confirmBeforeAdd: (propertyId: number) => Promise<boolean>;
canRemove: (propertyId: number) => Promise<boolean>;
isEditing: boolean;
setIsEditing: (value: boolean) => void;
Expand All @@ -61,6 +62,7 @@ export const AcquisitionView: React.FunctionComponent<IAcquisitionViewProps> = (
onShowPropertySelector,
onSuccess,
onUpdateProperties,
confirmBeforeAdd,
canRemove,
isEditing,
setIsEditing,
Expand Down Expand Up @@ -112,6 +114,13 @@ export const AcquisitionView: React.FunctionComponent<IAcquisitionViewProps> = (
setIsShowingPropertySelector={closePropertySelector}
onSuccess={onSuccess}
updateFileProperties={onUpdateProperties}
confirmBeforeAdd={confirmBeforeAdd}
confirmBeforeAddMessage={
<>
<p>This property has already been added to one or more acquisition files.</p>
<p>Do you want to acknowledge and proceed?</p>
</>
}
canRemove={canRemove}
formikRef={formikRef}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,22 @@ jest.mock('@/components/common/mapFSM/MapStateMachineContext');

describe('AcquisitionProperties component', () => {
// render component under test
const setup = (props: { initialForm: AcquisitionForm }, renderOptions: RenderOptions = {}) => {
const setup = (
props: {
initialForm: AcquisitionForm;
confirmBeforeAdd?: (propertyId: number) => Promise<boolean>;
},
renderOptions: RenderOptions = {},
) => {
const utils = render(
<>
<Formik initialValues={props.initialForm} onSubmit={noop}>
{formikProps => <AcquisitionPropertiesSubForm formikProps={formikProps} />}
{formikProps => (
<AcquisitionPropertiesSubForm
formikProps={formikProps}
confirmBeforeAdd={props.confirmBeforeAdd ?? jest.fn()}
/>
)}
</Formik>
</>,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@ import { IMapProperty } from '@/components/propertySelector/models';
import SelectedPropertyHeaderRow from '@/components/propertySelector/selectedPropertyList/SelectedPropertyHeaderRow';
import SelectedPropertyRow from '@/components/propertySelector/selectedPropertyList/SelectedPropertyRow';
import { useBcaAddress } from '@/features/properties/map/hooks/useBcaAddress';
import { useModalContext } from '@/hooks/useModalContext';

import { AddressForm, PropertyForm } from '../../shared/models';
import { AcquisitionForm } from './models';

interface AcquisitionPropertiesProp {
export interface IAcquisitionPropertiesProps {
formikProps: FormikProps<AcquisitionForm>;
confirmBeforeAdd: (propertyId: number) => Promise<boolean>;
}

export const AcquisitionPropertiesSubForm: React.FunctionComponent<
React.PropsWithChildren<AcquisitionPropertiesProp>
> = ({ formikProps }) => {
export const AcquisitionPropertiesSubForm: React.FunctionComponent<IAcquisitionPropertiesProps> = ({
formikProps,
confirmBeforeAdd,
}) => {
const { values } = formikProps;
const { getPrimaryAddressByPid, bcaLoading } = useBcaAddress();
const { setModalContent, setDisplayModal } = useModalContext();

return (
<>
Expand All @@ -46,14 +50,48 @@ export const AcquisitionPropertiesSubForm: React.FunctionComponent<
? AddressForm.fromBcaAddress(bcaSummary?.address)
: undefined;
}
if (
values.properties?.length === 0 &&
index === 0 &&
formProperty.regionName !== 'Cannot determine'
) {
formikProps.setFieldValue(`region`, formProperty.region);

if (formProperty.apiId && (await confirmBeforeAdd(formProperty.apiId))) {
// Require user confirmation before adding property to file
setModalContent({
variant: 'warning',
title: 'User Override Required',
message: (
<>
<p>
This property has already been added to one or more acquisition
files.
</p>
<p>Do you want to acknowledge and proceed?</p>
</>
),
okButtonText: 'Yes',
cancelButtonText: 'No',
handleOk: () => {
if (
values.properties?.length === 0 &&
index === 0 &&
formProperty.regionName !== 'Cannot determine'
) {
formikProps.setFieldValue(`region`, formProperty.region);
}
push(formProperty);
setDisplayModal(false);
},
handleCancel: () => setDisplayModal(false),
});
setDisplayModal(true);
} else {
// No confirmation needed - just add the property to the file
if (
values.properties?.length === 0 &&
index === 0 &&
formProperty.regionName !== 'Cannot determine'
) {
formikProps.setFieldValue(`region`, formProperty.region);
}
push(formProperty);
}
push(formProperty);
});
}, Promise.resolve());
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FormikProps } from 'formik';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { toast } from 'react-toastify';
import styled from 'styled-components';
Expand All @@ -8,8 +8,10 @@ import RealEstateAgent from '@/assets/images/real-estate-agent.svg?react';
import LoadingBackdrop from '@/components/common/LoadingBackdrop';
import { useMapStateMachine } from '@/components/common/mapFSM/MapStateMachineContext';
import MapSideBarLayout from '@/features/mapSideBar/layout/MapSideBarLayout';
import { usePropertyAssociations } from '@/hooks/repositories/usePropertyAssociations';
import { getCancelModalProps, useModalContext } from '@/hooks/useModalContext';
import { ApiGen_Concepts_AcquisitionFile } from '@/models/api/generated/ApiGen_Concepts_AcquisitionFile';
import { exists } from '@/utils';
import { featuresetToMapProperty } from '@/utils/mapPropertyUtils';

import { PropertyForm } from '../../shared/models';
Expand All @@ -32,6 +34,20 @@ export const AddAcquisitionContainer: React.FC<IAddAcquisitionContainerProps> =
const mapMachine = useMapStateMachine();
const selectedFeatureDataset = mapMachine.selectedFeatureDataset;

const { execute: getPropertyAssociations } = usePropertyAssociations();
const [needsUserConfirmation, setNeedsUserConfirmation] = useState<boolean>(true);

// Warn user that property is part of an existing research file
const confirmBeforeAdd = useCallback(
async (propertyId: number) => {
const response = await getPropertyAssociations(propertyId);
const acquisitionAssociations = response?.acquisitionAssociations ?? [];
const otherAcqFiles = acquisitionAssociations.filter(a => exists(a.id));
return otherAcqFiles.length > 0;
},
[getPropertyAssociations],
);

const initialForm = useMemo(() => {
const acquisitionForm = new AcquisitionForm();
if (selectedFeatureDataset !== null) {
Expand All @@ -45,15 +61,6 @@ export const AddAcquisitionContainer: React.FC<IAddAcquisitionContainerProps> =
return acquisitionForm;
}, [selectedFeatureDataset]);

useEffect(() => {
if (!!selectedFeatureDataset && !!formikRef.current) {
formikRef.current.resetForm();
formikRef.current?.setFieldValue('properties', [
PropertyForm.fromMapProperty(featuresetToMapProperty(selectedFeatureDataset)),
]);
}
}, [initialForm, selectedFeatureDataset]);

const handleSave = async () => {
// Sets the formik field `isValid` to false at start
await formikRef?.current?.validateForm();
Expand Down Expand Up @@ -104,6 +111,60 @@ export const AddAcquisitionContainer: React.FC<IAddAcquisitionContainerProps> =
}
};

const { initialValues } = helper;
// Require user confirmation before adding a property to file
// This is the flow for Map Marker -> right-click -> create Acquisition File
useEffect(() => {
const runAsync = async () => {
if (exists(initialValues) && exists(formikRef.current) && needsUserConfirmation) {
if (initialValues.properties.length > 0) {
const formProperty = initialValues.properties[0];
if (formProperty.apiId && (await confirmBeforeAdd(formProperty.apiId))) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same issue here.

setModalContent({
variant: 'warning',
title: 'User Override Required',
message: (
<>
<p>This property has already been added to one or more acquisition files.</p>
<p>Do you want to acknowledge and proceed?</p>
</>
),
okButtonText: 'Yes',
cancelButtonText: 'No',
handleOk: () => {
// allow the property to be added to the file being created
formikRef.current.resetForm();
formikRef.current.setFieldValue('properties', initialValues.properties);
setDisplayModal(false);
// show the user confirmation modal only once when creating a file
setNeedsUserConfirmation(false);
},
handleCancel: () => {
// clear out the properties array as the user did not agree to the popup
initialValues.properties.splice(0, initialValues.properties.length);
formikRef.current.resetForm();
formikRef.current.setFieldValue('properties', initialValues.properties);
setDisplayModal(false);
// show the user confirmation modal only once when creating a file
setNeedsUserConfirmation(false);
},
});
setDisplayModal(true);
}
}
}
};

runAsync();
}, [
confirmBeforeAdd,
initialValues,
needsUserConfirmation,
selectedFeatureDataset,
setDisplayModal,
setModalContent,
]);

return (
<MapSideBarLayout
showCloseButton
Expand Down Expand Up @@ -134,6 +195,7 @@ export const AddAcquisitionContainer: React.FC<IAddAcquisitionContainerProps> =
initialValues={helper.initialValues}
onSubmit={helper.handleSubmit}
validationSchema={helper.validationSchema}
confirmBeforeAdd={confirmBeforeAdd}
/>
</StyledFormWrapper>
</MapSideBarLayout>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const history = createMemoryHistory();
const validationSchema = jest.fn().mockReturnValue(AddAcquisitionFileYupSchema);
const onSubmit = jest.fn();

type TestProps = Pick<IAddAcquisitionFormProps, 'initialValues'>;
type TestProps = Partial<Pick<IAddAcquisitionFormProps, 'initialValues' | 'confirmBeforeAdd'>>;
jest.mock('@react-keycloak/web');

describe('AddAcquisitionForm component', () => {
Expand All @@ -26,7 +26,8 @@ describe('AddAcquisitionForm component', () => {
const utils = render(
<AddAcquisitionForm
ref={formikRef}
initialValues={props.initialValues}
initialValues={props.initialValues ?? new AcquisitionForm()}
confirmBeforeAdd={props.confirmBeforeAdd ?? jest.fn()}
validationSchema={validationSchema}
onSubmit={onSubmit}
/>,
Expand Down
Loading
Loading