TP-62632 | Address screen refactor
This commit is contained in:
@@ -7,10 +7,13 @@ import axiosInstance, { ApiKeys, API_STATUS_CODE, getApiUrl } from '../component
|
||||
import { AppDispatch } from '../store/store';
|
||||
import { logError } from '../components/utlis/errorUtils';
|
||||
import { IAddAddressPayload, IAddressGeolocationPayload } from '../types/addressGeolocation.types';
|
||||
import { UnifiedCaseDetailsTypes, initialUrlParams } from './caseApiActions';
|
||||
import { UnifiedCaseDetailsTypes } from './caseApiActions';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
import { IUngroupedAddressWithFeedbacks } from '@screens/addressGeolocation';
|
||||
import {
|
||||
setUngroupedAddresses,
|
||||
setUngroupedAddressesLoading,
|
||||
} from '@reducers/ungroupedAddressesSlice';
|
||||
|
||||
export const getAddressesGeolocation =
|
||||
(payload: IAddressGeolocationPayload) => (dispatch: AppDispatch) => {
|
||||
@@ -60,34 +63,35 @@ export const addAddress =
|
||||
});
|
||||
};
|
||||
|
||||
export const getUngroupedAddress = (
|
||||
loanAccountNumber: string,
|
||||
infoToGet: UnifiedCaseDetailsTypes[]
|
||||
) => {
|
||||
const queryParams = { ...initialUrlParams };
|
||||
for (const key of infoToGet) {
|
||||
queryParams[key] = true;
|
||||
}
|
||||
export const getUngroupedAddress = (loanAccountNumber: string) => (dispatch: AppDispatch) => {
|
||||
const queryParams = {
|
||||
[UnifiedCaseDetailsTypes.UNGROUPED_ADDRESSES]: true,
|
||||
[UnifiedCaseDetailsTypes.INCLUDE_ADDRESS_FEEDBACK]: true,
|
||||
};
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_UNIFIED_ENTITY_REQUESTED, {
|
||||
lan: loanAccountNumber || '',
|
||||
requestedEntities: JSON.stringify(infoToGet || []) || '',
|
||||
requestedEntities: JSON.stringify([
|
||||
UnifiedCaseDetailsTypes.UNGROUPED_ADDRESSES,
|
||||
UnifiedCaseDetailsTypes.INCLUDE_ADDRESS_FEEDBACK,
|
||||
]),
|
||||
});
|
||||
dispatch(setUngroupedAddressesLoading({ loanAccountNumber, isLoading: true }));
|
||||
const url = getApiUrl(ApiKeys.CASE_UNIFIED_DETAILS_V4, { loanAccountNumber }, queryParams);
|
||||
return axiosInstance
|
||||
axiosInstance
|
||||
.get(url)
|
||||
.then((response) => {
|
||||
if (response.status === API_STATUS_CODE.OK) {
|
||||
const ungroupedAddressesWithFeedbacks: IUngroupedAddressWithFeedbacks = {
|
||||
const ungroupedAddressesWithFeedbacks = {
|
||||
ungroupedAddresses: response?.data?.ungroupedAddresses || [],
|
||||
ungroupedAddressFeedbacks: response?.data?.addressFeedbacks || [],
|
||||
loanAccountNumber,
|
||||
};
|
||||
return ungroupedAddressesWithFeedbacks;
|
||||
dispatch(setUngroupedAddresses(ungroupedAddressesWithFeedbacks));
|
||||
}
|
||||
throw response;
|
||||
})
|
||||
.catch((err) => {
|
||||
logError(err);
|
||||
throw new Error(err);
|
||||
dispatch(setUngroupedAddressesLoading({ loanAccountNumber, isLoading: false }));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -10,12 +10,7 @@ import CosmosForegroundService, {
|
||||
type IForegroundTask,
|
||||
} from '../services/foregroundServices/foreground.service';
|
||||
import useIsOnline from '../hooks/useIsOnline';
|
||||
import {
|
||||
IGeolocationPayload,
|
||||
getSyncTime,
|
||||
sendCurrentGeolocationAndBuffer,
|
||||
sendLocationAndActivenessToServer,
|
||||
} from '../hooks/capturingApi';
|
||||
import { getSyncTime, sendCurrentGeolocationAndBuffer } from '../hooks/capturingApi';
|
||||
import { isTimeDifferenceWithinRange } from '../components/utlis/commonFunctions';
|
||||
import { setIsTimeSynced } from '../reducer/foregroundServiceSlice';
|
||||
import { logError } from '../components/utlis/errorUtils';
|
||||
@@ -49,12 +44,6 @@ import { GlobalImageMap } from './CachedImage';
|
||||
import { addClickstreamEvent } from '../services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from './Constants';
|
||||
import useResyncFirebase from '@hooks/useResyncFirebase';
|
||||
import { CaptureGeolocation } from '@components/form/services/geoLocation.service';
|
||||
import getLitmusExperimentResult, {
|
||||
LitmusExperimentName,
|
||||
LitmusExperimentNameMap,
|
||||
} from '@services/litmusExperiments.service';
|
||||
import { setLitmusExperimentResult } from '@reducers/litmusExperimentSlice';
|
||||
|
||||
export enum FOREGROUND_TASKS {
|
||||
GEOLOCATION = 'GEOLOCATION',
|
||||
@@ -82,59 +71,17 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
const {
|
||||
isTeamLead,
|
||||
caseSyncLock,
|
||||
isTrackingComponentV2Enabled,
|
||||
referenceId,
|
||||
pendingList = [],
|
||||
pinnedList = [],
|
||||
geolocations = [],
|
||||
deviceId,
|
||||
userId,
|
||||
} = useAppSelector((state) => ({
|
||||
isTeamLead: state.user.isTeamLead,
|
||||
caseSyncLock: state?.user?.caseSyncLock,
|
||||
isTrackingComponentV2Enabled: state.litmusExperiment?.isTrackingComponentV2Enabled,
|
||||
referenceId: state.user.user?.referenceId!,
|
||||
pendingList: state.allCases.pendingList,
|
||||
pinnedList: state.allCases.pinnedList,
|
||||
geolocations: state.foregroundService.deviceGeolocationsBuffer,
|
||||
deviceId: state?.user?.deviceId,
|
||||
userId: state?.user?.user?.referenceId,
|
||||
}));
|
||||
|
||||
const handleSendGeolocation = async () => {
|
||||
try {
|
||||
const location = await CaptureGeolocation.fetchLocation(`${Date.now()}`, 0, appState.current);
|
||||
if (!location) {
|
||||
return;
|
||||
}
|
||||
const isActiveOnApp: string | boolean = (await getItem(StorageKeys.IS_USER_ACTIVE)) || false;
|
||||
const userActivityonApp: string =
|
||||
(await getItem(StorageKeys.USER_ACTIVITY_ON_APP)) || AgentActivity.LOW;
|
||||
|
||||
const geolocation: IGeolocationPayload = {
|
||||
latitude: location.latitude,
|
||||
longitude: location.longitude,
|
||||
accuracy: location.accuracy,
|
||||
timestamp: Date.now(),
|
||||
isActiveOnApp: Boolean(isActiveOnApp),
|
||||
userActivityOnApp: String(userActivityonApp),
|
||||
deviceId: deviceId,
|
||||
};
|
||||
dispatch(sendLocationAndActivenessToServer([geolocation]));
|
||||
} catch (e: any) {
|
||||
logError(e, 'Error during background location sending.');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOnline || isTrackingComponentV2Enabled) {
|
||||
return;
|
||||
}
|
||||
if (geolocations.length > 0) {
|
||||
dispatch(sendLocationAndActivenessToServer(geolocations, true));
|
||||
}
|
||||
}, [geolocations, isOnline, isTrackingComponentV2Enabled]);
|
||||
|
||||
const handleTimeSync = async () => {
|
||||
try {
|
||||
if (!isOnline) {
|
||||
@@ -296,9 +243,7 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
},
|
||||
{
|
||||
taskId: FOREGROUND_TASKS.GEOLOCATION,
|
||||
task: isTrackingComponentV2Enabled
|
||||
? () => dispatch(sendCurrentGeolocationAndBuffer(appState.current))
|
||||
: handleSendGeolocation,
|
||||
task: () => dispatch(sendCurrentGeolocationAndBuffer(appState.current)),
|
||||
delay: 3 * MILLISECONDS_IN_A_MINUTE, // 3 minutes
|
||||
onLoop: true,
|
||||
},
|
||||
@@ -364,32 +309,6 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleGeolocationTaskUpdate = async () => {
|
||||
const trackingComponentExperimentName = LitmusExperimentName.COSMOS_TRACKING_COMPONENT_V2;
|
||||
const trackingComponentExperiment = await getLitmusExperimentResult(
|
||||
trackingComponentExperimentName,
|
||||
{ 'x-customer-id': userId }
|
||||
);
|
||||
// If the tracking component experiment is changed, update to new geolocation task
|
||||
if (trackingComponentExperiment !== isTrackingComponentV2Enabled) {
|
||||
const updatedGeolocationTask = {
|
||||
taskId: FOREGROUND_TASKS.GEOLOCATION,
|
||||
task: trackingComponentExperiment
|
||||
? () => dispatch(sendCurrentGeolocationAndBuffer(appState.current))
|
||||
: handleSendGeolocation,
|
||||
delay: 3 * MILLISECONDS_IN_A_MINUTE,
|
||||
onLoop: true,
|
||||
};
|
||||
CosmosForegroundService.updateTask(updatedGeolocationTask);
|
||||
dispatch(
|
||||
setLitmusExperimentResult({
|
||||
experimentName: trackingComponentExperimentName,
|
||||
result: trackingComponentExperiment,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAppStateChange = async (nextAppState: AppStateStatus) => {
|
||||
// App comes to foreground from background
|
||||
const now = dayJs().toString();
|
||||
@@ -401,7 +320,6 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
dispatch(getConfigData());
|
||||
CosmosForegroundService.start(tasks);
|
||||
resyncFirebase();
|
||||
handleGeolocationTaskUpdate();
|
||||
}
|
||||
if (nextAppState === AppStates.BACKGROUND) {
|
||||
await setItem(StorageKeys.APP_BACKGROUND_TIMESTAMP, now);
|
||||
@@ -433,7 +351,6 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
let appStateSubscription: NativeEventSubscription;
|
||||
appStateSubscription = AppState.addEventListener('change', handleAppStateChange);
|
||||
CosmosForegroundService.start(tasks);
|
||||
handleGeolocationTaskUpdate();
|
||||
return () => {
|
||||
appStateSubscription?.remove();
|
||||
};
|
||||
|
||||
@@ -31,7 +31,13 @@ export const sendLocationAndActivenessToServerV2 =
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
dispatch(clearDeviceGeolocationsBuffer());
|
||||
// clear the buffer after sending the location to the server
|
||||
dispatch(
|
||||
setDeviceGeolocationsBuffer({
|
||||
deviceGeolocationBuffer: [],
|
||||
deviceGeolocationCoordinate: geolocationBuffer[0],
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
dispatch(
|
||||
@@ -44,33 +50,6 @@ export const sendLocationAndActivenessToServerV2 =
|
||||
});
|
||||
};
|
||||
|
||||
export const sendLocationAndActivenessToServer =
|
||||
(geolocationBuffer: IGeolocationPayload[], clearBuffer?: boolean) =>
|
||||
(dispatch: AppDispatch, getState: AppGetState) => {
|
||||
axiosInstance
|
||||
.post(getApiUrl(ApiKeys.SEND_LOCATION), geolocationBuffer, {
|
||||
headers: {
|
||||
donotHandleError: 'true',
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
clearBuffer && dispatch(clearDeviceGeolocationsBuffer());
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!clearBuffer) {
|
||||
const { foregroundService } = getState();
|
||||
const latestGeolocationsBuffer = foregroundService.deviceGeolocationsBuffer || [];
|
||||
dispatch(
|
||||
setDeviceGeolocationsBuffer({
|
||||
deviceGeolocationBuffer: [...geolocationBuffer, ...latestGeolocationsBuffer],
|
||||
deviceGeolocationCoordinate: geolocationBuffer[0],
|
||||
})
|
||||
);
|
||||
}
|
||||
logError(error, 'Error while sending location to server');
|
||||
});
|
||||
};
|
||||
|
||||
export const sendCurrentGeolocationAndBuffer =
|
||||
(appState: AppStateStatus) => async (dispatch: AppDispatch, getState: AppGetState) => {
|
||||
try {
|
||||
|
||||
43
src/hooks/useAddresses.tsx
Normal file
43
src/hooks/useAddresses.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { useAppSelector } from '@hooks';
|
||||
import { MAXIMUM_ALLOWED_DISTANCE_FOR_GROUPED_ADDRESSES } from '@screens/addressGeolocation/constants';
|
||||
import filterMetaAddressesByDistance from '@screens/addressGeolocation/utils/FilterFarAwayMetaAddresses';
|
||||
import { RootState } from '@store';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
const useAddresses = (loanAccountNumber: string) => {
|
||||
const {
|
||||
addressGeolocation,
|
||||
currentGeolocationCoordinates,
|
||||
ungroupedAddresses = [],
|
||||
} = useAppSelector((state: RootState) => ({
|
||||
addressGeolocation: state.address?.[loanAccountNumber]?.addressesAndGeoLocations || {},
|
||||
isLoading: state.address?.[loanAccountNumber]?.isLoading || false,
|
||||
currentGeolocationCoordinates: state.foregroundService?.deviceGeolocationCoordinate || {},
|
||||
addressFeedbacks: state.address?.[loanAccountNumber]?.addressFeedbacks || [],
|
||||
ungroupedAddresses: state.ungroupedAddresses[loanAccountNumber]?.ungroupedAddresses,
|
||||
}));
|
||||
|
||||
const { farAwayAddresses = [], nearByAddresses = [] } = useMemo(() => {
|
||||
const { farAwayAddresses = [], nearByAddresses } = filterMetaAddressesByDistance({
|
||||
metaAddresses: addressGeolocation?.groupedAddresses,
|
||||
maximumDistance: MAXIMUM_ALLOWED_DISTANCE_FOR_GROUPED_ADDRESSES,
|
||||
currentLocationCoordinates: currentGeolocationCoordinates,
|
||||
});
|
||||
return { farAwayAddresses, nearByAddresses };
|
||||
}, [addressGeolocation, currentGeolocationCoordinates]);
|
||||
|
||||
const { additionalAddresses = [] } = useMemo(() => {
|
||||
const metaAddresses = farAwayAddresses.map((farAwayAddress) => {
|
||||
return {
|
||||
...farAwayAddress?.metaAddress,
|
||||
similarAddresses: farAwayAddress?.similarAddresses,
|
||||
};
|
||||
});
|
||||
const additionalAddresses = [...metaAddresses, ...ungroupedAddresses];
|
||||
return { additionalAddresses, farAwayAddresses, nearByAddresses };
|
||||
}, [farAwayAddresses, ungroupedAddresses]);
|
||||
|
||||
return { farAwayAddresses, nearByAddresses, additionalAddresses };
|
||||
};
|
||||
|
||||
export default useAddresses;
|
||||
45
src/reducer/ungroupedAddressesSlice.ts
Normal file
45
src/reducer/ungroupedAddressesSlice.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { IAddress } from '../types/addressGeolocation.types';
|
||||
import { IAddressFeedback } from './addressSlice';
|
||||
|
||||
type IUngroupedAddressesSlice = Record<
|
||||
string,
|
||||
{
|
||||
ungroupedAddresses: IAddress[];
|
||||
ungroupedAddressFeedbacks: IAddressFeedback[];
|
||||
isLoading: boolean;
|
||||
}
|
||||
>;
|
||||
|
||||
const initialState: IUngroupedAddressesSlice = {};
|
||||
|
||||
const UngroupedAddressesSlice = createSlice({
|
||||
name: 'ungroupedAddressesSlice',
|
||||
initialState,
|
||||
reducers: {
|
||||
setUngroupedAddresses: (state, action) => {
|
||||
const {
|
||||
loanAccountNumber,
|
||||
ungroupedAddresses = [],
|
||||
ungroupedAddressFeedbacks = [],
|
||||
} = action.payload;
|
||||
state[loanAccountNumber] = {
|
||||
ungroupedAddresses,
|
||||
ungroupedAddressFeedbacks,
|
||||
isLoading: false,
|
||||
};
|
||||
},
|
||||
setUngroupedAddressesLoading: (state, action) => {
|
||||
const { loanAccountNumber, isLoading } = action.payload;
|
||||
state[loanAccountNumber] = {
|
||||
...(state?.[loanAccountNumber] || {}),
|
||||
isLoading,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setUngroupedAddresses, setUngroupedAddressesLoading } =
|
||||
UngroupedAddressesSlice.actions;
|
||||
|
||||
export default UngroupedAddressesSlice.reducer;
|
||||
@@ -1,34 +1,29 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import Accordion from '../../../RN-UI-LIB/src/components/accordian/Accordian';
|
||||
import Text from '../../../RN-UI-LIB/src/components/Text';
|
||||
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import { type IAddress, type IGroupedAddressesItem } from '../../types/addressGeolocation.types';
|
||||
import { type IAddress } from '../../types/addressGeolocation.types';
|
||||
import AddressItem from './AddressItem';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
|
||||
import { type GenericFunctionArgs } from '../../common/GenericTypes';
|
||||
import SimilarAddressItem from './SimilarAddressItem';
|
||||
import filterNearMetaAddresses from './utils/FilterNearMetaAddresses';
|
||||
import { useAppSelector } from '../../hooks';
|
||||
import { MAXIMUM_ALLOWED_DISTANCE_FOR_GROUPED_ADDRESSES } from './constants';
|
||||
import { CaptureGeolocation, DeviceLocation } from '@components/form/services/geoLocation.service';
|
||||
import { setDeviceGeolocation } from '@reducers/foregroundServiceSlice';
|
||||
import { useAppDispatch } from '../../hooks';
|
||||
import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack';
|
||||
import useAddresses from '@hooks/useAddresses';
|
||||
|
||||
interface IAddressContainer {
|
||||
groupedAddressList?: IGroupedAddressesItem[];
|
||||
caseId?: string;
|
||||
caseId: string;
|
||||
handlePageRouting?: GenericFunctionArgs;
|
||||
addressFeedbacks?: any;
|
||||
}
|
||||
|
||||
function SeparatorBorderComponent() {
|
||||
return <View style={[styles.borderLine, GenericStyles.mv16]} />;
|
||||
}
|
||||
|
||||
const getAllAddressIds = (groupedAddress: IGroupedAddressesItem) => {
|
||||
const getAllAddressIds = (groupedAddress: IAddress) => {
|
||||
// Set for unique address IDs
|
||||
const addressIds = new Set();
|
||||
|
||||
@@ -50,18 +45,18 @@ const getAllAddressIds = (groupedAddress: IGroupedAddressesItem) => {
|
||||
return Array.from(addressIds);
|
||||
};
|
||||
|
||||
const AddressContainer: React.FC<IAddressContainer> = ({
|
||||
groupedAddressList,
|
||||
caseId,
|
||||
handlePageRouting,
|
||||
addressFeedbacks,
|
||||
}) => {
|
||||
const { currentGeolocationCoordinates } = useAppSelector((state) => ({
|
||||
currentGeolocationCoordinates: state.foregroundService?.deviceGeolocationCoordinate,
|
||||
}));
|
||||
const AddressContainer: React.FC<IAddressContainer> = ({ caseId, handlePageRouting }) => {
|
||||
const { loanAccountNumber, addressFeedbacks } = useAppSelector((state) => {
|
||||
const loanAccountNumber = state.allCases?.caseDetails?.[caseId]?.loanAccountNumber;
|
||||
return {
|
||||
loanAccountNumber,
|
||||
addressFeedbacks: state.address?.[loanAccountNumber]?.addressFeedbacks || [],
|
||||
};
|
||||
});
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleOpenOldFeedbacks = (groupedAddress: IGroupedAddressesItem) => {
|
||||
const { nearByAddresses } = useAddresses(loanAccountNumber);
|
||||
|
||||
const handleOpenOldFeedbacks = (groupedAddress: IAddress) => {
|
||||
const addressIds = getAllAddressIds(groupedAddress);
|
||||
const commonParams = {
|
||||
addressText: groupedAddress?.metaAddress?.addressText,
|
||||
@@ -78,19 +73,9 @@ const AddressContainer: React.FC<IAddressContainer> = ({
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
CaptureGeolocation.watchLocation((location: DeviceLocation) => {
|
||||
return dispatch(setDeviceGeolocation(location));
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View>
|
||||
{filterNearMetaAddresses({
|
||||
metaAddresses: groupedAddressList,
|
||||
maximumDistance: MAXIMUM_ALLOWED_DISTANCE_FOR_GROUPED_ADDRESSES,
|
||||
currentLocationCoordinates: currentGeolocationCoordinates,
|
||||
})?.map((groupedAddress: IGroupedAddressesItem, index: number) => {
|
||||
{nearByAddresses?.map((groupedAddress: IAddress, index: number) => {
|
||||
const lastFeedbackForAddress = addressFeedbacks.find(
|
||||
(addressFeedback) =>
|
||||
addressFeedback?.addressReferenceId === groupedAddress?.metaAddress?.id
|
||||
@@ -99,7 +84,7 @@ const AddressContainer: React.FC<IAddressContainer> = ({
|
||||
<View key={groupedAddress?.metaAddress?.id}>
|
||||
<Accordion
|
||||
accordionStyle={[GenericStyles.pv24, GenericStyles.ph16]}
|
||||
isExpansionDisabled={!groupedAddress?.similarAddresses.length}
|
||||
isExpansionDisabled={!groupedAddress?.similarAddresses?.length}
|
||||
touchableOpacity={0.8}
|
||||
touchableDelay={50}
|
||||
accordionHeader={
|
||||
@@ -127,7 +112,7 @@ const AddressContainer: React.FC<IAddressContainer> = ({
|
||||
handleAccordionExpand(isExpanded, groupedAddress?.metaAddress?.id);
|
||||
}}
|
||||
>
|
||||
{groupedAddress?.similarAddresses.length ? (
|
||||
{groupedAddress?.similarAddresses?.length ? (
|
||||
<View>
|
||||
<SeparatorBorderComponent />
|
||||
<Text
|
||||
@@ -136,9 +121,8 @@ const AddressContainer: React.FC<IAddressContainer> = ({
|
||||
Similar addresses
|
||||
</Text>
|
||||
{groupedAddress.similarAddresses.map((similarAddress: IAddress) => (
|
||||
<View style={{ flex: 1 }}>
|
||||
<View style={GenericStyles.fill} key={similarAddress?.id}>
|
||||
<SimilarAddressItem
|
||||
key={similarAddress?.id}
|
||||
addressItem={similarAddress}
|
||||
containerStyle={styles.card}
|
||||
showSource
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Dimensions, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Dimensions, RefreshControl, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import NavigationHeader from '../../../RN-UI-LIB/src/components/NavigationHeader';
|
||||
import { goBack, navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import useIsOnline from '../../hooks/useIsOnline';
|
||||
@@ -16,10 +16,11 @@ import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import SimilarAddressItem from './SimilarAddressItem';
|
||||
import Accordion from '../../../RN-UI-LIB/src/components/accordian/Accordian';
|
||||
import { IAddressFeedback } from '@reducers/addressSlice';
|
||||
import getAddressIdsArrayForFarAddresses from './utils/getAddressIdsArrayForFarAddresses';
|
||||
import { IUngroupedAddressWithFeedbacks } from '.';
|
||||
import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack';
|
||||
import { useAppDispatch, useAppSelector } from '@hooks';
|
||||
import { getUngroupedAddress } from '@actions/addressGeolocationAction';
|
||||
import useAddresses from '@hooks/useAddresses';
|
||||
|
||||
const PAGE_TITLE = 'Additional addresses';
|
||||
|
||||
@@ -29,7 +30,6 @@ interface IUngroupedAddress {
|
||||
loanAccountNumber: string;
|
||||
caseId: string;
|
||||
customerReferenceId: string;
|
||||
fetchUngroupedAddress: (loanAccountNumber: string) => Promise<IUngroupedAddressWithFeedbacks>;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -48,38 +48,28 @@ const handleAccordionExpand = (isExpanded: boolean, addressId: string) => {
|
||||
|
||||
const UngroupedAddressContainer: React.FC<IUngroupedAddress> = ({ route: routeParams }) => {
|
||||
const {
|
||||
params: { loanAccountNumber, caseId, customerReferenceId, fetchUngroupedAddress },
|
||||
params: { loanAccountNumber, caseId, customerReferenceId },
|
||||
} = routeParams;
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [retryBtnToggle, setRetryBtnToggle] = useState(false);
|
||||
const [ungroupedAddressList, setUngroupedAddressList] = useState<IAddress[]>([]);
|
||||
const [addressFeedbacksList, setAddressFeedbacksList] = useState<IAddressFeedback[]>([]);
|
||||
const { ungroupedAddressFeedbacks = [], isLoading = false } =
|
||||
useAppSelector((state) => state.ungroupedAddresses?.[loanAccountNumber]) || {};
|
||||
const { additionalAddresses = [] } = useAddresses(loanAccountNumber);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const isOnline = useIsOnline();
|
||||
|
||||
const handleRetryEvent = () => {
|
||||
setRetryBtnToggle((retryBtnToggle) => !retryBtnToggle);
|
||||
const fetchUngroupedAddress = () => {
|
||||
// fetch ungrouped address
|
||||
dispatch(getUngroupedAddress(loanAccountNumber));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isOnline) {
|
||||
setLoading(true);
|
||||
fetchUngroupedAddress(loanAccountNumber)
|
||||
.then((res: IUngroupedAddressWithFeedbacks) => {
|
||||
setUngroupedAddressList(res?.ungroupedAddresses || []);
|
||||
setAddressFeedbacksList(res?.ungroupedAddressFeedbacks || []);
|
||||
setLoading(true);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
}, [retryBtnToggle, isOnline]);
|
||||
|
||||
if (!isOnline) {
|
||||
return (
|
||||
<OfflineScreen handleRetryEvent={handleRetryEvent} goBack={goBack} pageTitle={PAGE_TITLE} />
|
||||
<OfflineScreen
|
||||
handleRetryEvent={fetchUngroupedAddress}
|
||||
goBack={goBack}
|
||||
pageTitle={PAGE_TITLE}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -105,13 +95,21 @@ const UngroupedAddressContainer: React.FC<IUngroupedAddress> = ({ route: routePa
|
||||
return (
|
||||
<View style={GenericStyles.fill}>
|
||||
<NavigationHeader title={PAGE_TITLE} onBack={goBack} />
|
||||
<ScrollView>
|
||||
<ScrollView
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={false}
|
||||
onRefresh={fetchUngroupedAddress}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<SuspenseLoader
|
||||
loading={loading}
|
||||
loading={isLoading}
|
||||
fallBack={
|
||||
<>
|
||||
{[...Array(8).keys()].map(() => (
|
||||
{[...Array(8).keys()].map((_, index) => (
|
||||
<LineLoader
|
||||
key={index}
|
||||
width="100%"
|
||||
height={75}
|
||||
style={[GenericStyles.br6, { marginBottom: 20 }]}
|
||||
@@ -120,13 +118,16 @@ const UngroupedAddressContainer: React.FC<IUngroupedAddress> = ({ route: routePa
|
||||
</>
|
||||
}
|
||||
>
|
||||
{ungroupedAddressList?.length ? (
|
||||
{additionalAddresses?.length ? (
|
||||
<View>
|
||||
{ungroupedAddressList.map((ungroupedAddressItem: IAddress, index: number) => {
|
||||
const lastFeedbackForAddress = addressFeedbacksList?.find((addressFeedback)=>{
|
||||
return (addressFeedback?.addressReferenceId === ungroupedAddressItem?.id)});
|
||||
{additionalAddresses.map((ungroupedAddressItem: IAddress, index: number) => {
|
||||
const lastFeedbackForAddress = ungroupedAddressFeedbacks?.find(
|
||||
(addressFeedback) => {
|
||||
return addressFeedback?.addressReferenceId === ungroupedAddressItem?.id;
|
||||
}
|
||||
);
|
||||
return (
|
||||
<View>
|
||||
<View key={ungroupedAddressItem?.id}>
|
||||
{!ungroupedAddressItem?.similarAddresses ? (
|
||||
<AddressItem
|
||||
caseId={caseId}
|
||||
@@ -162,7 +163,10 @@ const UngroupedAddressContainer: React.FC<IUngroupedAddress> = ({ route: routePa
|
||||
handleOpenOldFeedbacks(ungroupedAddressItem);
|
||||
}}
|
||||
handleCloseRouting={() => {
|
||||
navigateToScreen(CaseDetailStackEnum.ADDITIONAL_ADDRESSES, commonParams);
|
||||
navigateToScreen(
|
||||
CaseDetailStackEnum.ADDITIONAL_ADDRESSES,
|
||||
commonParams
|
||||
);
|
||||
}}
|
||||
showSource
|
||||
lastFeedbackForAddress={lastFeedbackForAddress}
|
||||
@@ -184,7 +188,8 @@ const UngroupedAddressContainer: React.FC<IUngroupedAddress> = ({ route: routePa
|
||||
>
|
||||
Similar addresses
|
||||
</Text>
|
||||
{ungroupedAddressItem.similarAddresses.map((similarAddress: IAddress) => (
|
||||
{ungroupedAddressItem.similarAddresses.map(
|
||||
(similarAddress: IAddress) => (
|
||||
<View style={GenericStyles.fill} key={similarAddress?.id}>
|
||||
<SimilarAddressItem
|
||||
addressItem={similarAddress}
|
||||
@@ -192,13 +197,14 @@ const UngroupedAddressContainer: React.FC<IUngroupedAddress> = ({ route: routePa
|
||||
showSource
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
) : null}
|
||||
</Accordion>
|
||||
)}
|
||||
</View> );
|
||||
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
) : (
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import {
|
||||
AppState,
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import NavigationHeader from '../../../RN-UI-LIB/src/components/NavigationHeader';
|
||||
import { goBack, navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import { useAppDispatch } from '../../hooks';
|
||||
import { useAppDispatch, useAppSelector } from '../../hooks';
|
||||
import { type RootState } from '../../store/store';
|
||||
import GeolocationContainer from './GeolocationContainer';
|
||||
import AddressContainer from './AddressContainer';
|
||||
@@ -17,50 +23,34 @@ import { getCaseUnifiedData, UnifiedCaseDetailsTypes } from '../../action/caseAp
|
||||
import { CLICKSTREAM_EVENT_NAMES, HIT_SLOP } from '../../common/Constants';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import HomeIconSmall from '../../assets/icons/HomeIconSmall';
|
||||
import { IAddressFeedback, setAddressLoading } from '../../reducer/addressSlice';
|
||||
import { setAddressLoading } from '../../reducer/addressSlice';
|
||||
import SuspenseLoader from '../../../RN-UI-LIB/src/components/suspense_loader/SuspenseLoader';
|
||||
import LineLoader from '../../../RN-UI-LIB/src/components/suspense_loader/LineLoader';
|
||||
import { CaptureGeolocation } from '../../components/form/services/geoLocation.service';
|
||||
import { setDeviceGeolocation } from '../../reducer/foregroundServiceSlice';
|
||||
import Layout from '../layout/Layout';
|
||||
import { getUngroupedAddress } from '../../action/addressGeolocationAction';
|
||||
import { type IAddress } from '../../types/addressGeolocation.types';
|
||||
import filterFarAwayMetaAddresses from './utils/FilterFarAwayMetaAddresses';
|
||||
import { MAXIMUM_ALLOWED_DISTANCE_FOR_GROUPED_ADDRESSES } from './constants';
|
||||
import CustomTabs from '@rn-ui-lib/components/customTabs/CustomTabs';
|
||||
import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack';
|
||||
import { ADDRESSES_TABS, AddressGeolocationTabEnum, IAddressGeolocation } from './constant';
|
||||
import useAddresses from '@hooks/useAddresses';
|
||||
import { sendCurrentGeolocationAndBuffer } from '@hooks/capturingApi';
|
||||
import Text from '@rn-ui-lib/components/Text';
|
||||
|
||||
const PAGE_TITLE = 'All addresses';
|
||||
|
||||
export interface IUngroupedAddressWithFeedbacks {
|
||||
ungroupedAddresses: IAddress[];
|
||||
ungroupedAddressFeedbacks: IAddressFeedback[];
|
||||
}
|
||||
|
||||
const AddressGeolocation: React.FC<IAddressGeolocation> = ({ route: routeParams }) => {
|
||||
const {
|
||||
params: { loanAccountNumber, customerReferenceId, caseId },
|
||||
} = routeParams;
|
||||
|
||||
const [ungroupedAddress, setUngroupedAddress] = useState<IAddress[]>([]);
|
||||
const { addressGeolocation, isLoading } = useAppSelector((state: RootState) => ({
|
||||
addressGeolocation: state.address?.[loanAccountNumber]?.addressesAndGeoLocations || {},
|
||||
isLoading: state.address?.[loanAccountNumber]?.isLoading || false,
|
||||
}));
|
||||
const [selectedTab, setSelectedTab] = useState<string>(AddressGeolocationTabEnum.ADDRESS);
|
||||
const [ungroupedAddressFeedbacks, setUngroupedAddressFeedbacks] = useState<IAddressFeedback[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
const [retryBtnToggle, setRetryBtnToggle] = useState(false);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const isOnline = useIsOnline();
|
||||
|
||||
const { addressGeolocation, isLoading, currentGeolocationCoordinates, addressFeedbacks } =
|
||||
useSelector((state: RootState) => ({
|
||||
addressGeolocation: state.address?.[loanAccountNumber]?.addressesAndGeoLocations || {},
|
||||
isLoading: state.address?.[loanAccountNumber]?.isLoading || false,
|
||||
currentGeolocationCoordinates: state.foregroundService?.deviceGeolocationCoordinate || {},
|
||||
addressFeedbacks: state.address?.[loanAccountNumber]?.addressFeedbacks || [],
|
||||
}));
|
||||
const { additionalAddresses } = useAddresses(loanAccountNumber);
|
||||
|
||||
const commonParams = {
|
||||
loanAccountNumber,
|
||||
@@ -81,75 +71,29 @@ const AddressGeolocation: React.FC<IAddressGeolocation> = ({ route: routeParams
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ADD_NEW_ADDRESS_CLICKED, commonParams);
|
||||
};
|
||||
|
||||
const fetchUngroupedAddress = async (loanAccountNumber: string) =>
|
||||
await getUngroupedAddress(loanAccountNumber, [
|
||||
UnifiedCaseDetailsTypes.UNGROUPED_ADDRESSES,
|
||||
UnifiedCaseDetailsTypes.INCLUDE_ADDRESS_FEEDBACK,
|
||||
]).then((res: IUngroupedAddressWithFeedbacks) => {
|
||||
const farAwayAddresses = filterFarAwayMetaAddresses({
|
||||
metaAddresses: addressGeolocation?.groupedAddresses,
|
||||
maximumDistance: MAXIMUM_ALLOWED_DISTANCE_FOR_GROUPED_ADDRESSES,
|
||||
currentLocationCoordinates: currentGeolocationCoordinates,
|
||||
});
|
||||
const metaAddresses = farAwayAddresses.map((farAwayAddress) => {
|
||||
return {
|
||||
...farAwayAddress?.metaAddress,
|
||||
similarAddresses: farAwayAddress?.similarAddresses,
|
||||
};
|
||||
});
|
||||
const ungroupedAddresses = [...metaAddresses, ...(res?.ungroupedAddresses || [])];
|
||||
const ungroupedAddressFeedbacks = [
|
||||
...addressFeedbacks,
|
||||
...(res?.ungroupedAddressFeedbacks || []),
|
||||
];
|
||||
setUngroupedAddress(ungroupedAddresses);
|
||||
setUngroupedAddressFeedbacks(ungroupedAddressFeedbacks);
|
||||
const ungroupedAddressesWithFeedbacks: IUngroupedAddressWithFeedbacks = {
|
||||
ungroupedAddresses,
|
||||
ungroupedAddressFeedbacks,
|
||||
};
|
||||
return ungroupedAddressesWithFeedbacks;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
async function getUngroupedAddress() {
|
||||
await fetchUngroupedAddress(loanAccountNumber);
|
||||
return null;
|
||||
}
|
||||
getUngroupedAddress();
|
||||
}, [addressGeolocation?.groupedAddresses, isLoading]);
|
||||
const getGroupedAddresses = () => {
|
||||
dispatch(setAddressLoading({ loanAccountNumbers: [loanAccountNumber], isLoading: true }));
|
||||
dispatch(
|
||||
getCaseUnifiedData(
|
||||
[loanAccountNumber],
|
||||
[
|
||||
UnifiedCaseDetailsTypes.ADDRESS_AND_GEOLOCATIONS,
|
||||
UnifiedCaseDetailsTypes.INCLUDE_ADDRESS_FEEDBACK,
|
||||
]
|
||||
)
|
||||
);
|
||||
dispatch(sendCurrentGeolocationAndBuffer(AppState.currentState));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isOnline) {
|
||||
dispatch(setAddressLoading({ loanAccountNumbers: [loanAccountNumber], isLoading: true }));
|
||||
dispatch(
|
||||
getCaseUnifiedData(
|
||||
[loanAccountNumber],
|
||||
[
|
||||
UnifiedCaseDetailsTypes.ADDRESS_AND_GEOLOCATIONS,
|
||||
UnifiedCaseDetailsTypes.INCLUDE_ADDRESS_FEEDBACK,
|
||||
]
|
||||
)
|
||||
);
|
||||
getGroupedAddresses();
|
||||
}
|
||||
}, [retryBtnToggle, isOnline]);
|
||||
|
||||
useEffect(() => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ALL_ADDRESSES_LANDED, commonParams);
|
||||
|
||||
(async () => {
|
||||
const location = await CaptureGeolocation.fetchLocation(`${Date.now()}`, 0);
|
||||
|
||||
if (location != null) {
|
||||
dispatch(
|
||||
setDeviceGeolocation({
|
||||
latitude: location.latitude,
|
||||
longitude: location.longitude,
|
||||
timestamp: Date.now(),
|
||||
})
|
||||
);
|
||||
}
|
||||
})();
|
||||
dispatch(getUngroupedAddress(loanAccountNumber));
|
||||
}, []);
|
||||
|
||||
const handleTabChange = (tab: string) => {
|
||||
@@ -180,7 +124,9 @@ const AddressGeolocation: React.FC<IAddressGeolocation> = ({ route: routeParams
|
||||
onTabChange={handleTabChange}
|
||||
containerStyle={[GenericStyles.ml16, GenericStyles.pt16, GenericStyles.silverBackground]}
|
||||
/>
|
||||
<ScrollView>
|
||||
<ScrollView
|
||||
refreshControl={<RefreshControl refreshing={false} onRefresh={getGroupedAddresses} />}
|
||||
>
|
||||
<SuspenseLoader
|
||||
loading={isLoading}
|
||||
fallBack={
|
||||
@@ -198,13 +144,8 @@ const AddressGeolocation: React.FC<IAddressGeolocation> = ({ route: routeParams
|
||||
>
|
||||
<View style={GenericStyles.fill}>
|
||||
<View style={selectedTab === AddressGeolocationTabEnum.ADDRESS ? {} : styles.hidden}>
|
||||
<AddressContainer
|
||||
groupedAddressList={addressGeolocation.groupedAddresses}
|
||||
addressFeedbacks={addressFeedbacks}
|
||||
caseId={caseId}
|
||||
handlePageRouting={handleRouting}
|
||||
/>
|
||||
{ungroupedAddress?.length > 0 ? (
|
||||
<AddressContainer caseId={caseId} handlePageRouting={handleRouting} />
|
||||
{additionalAddresses?.length > 0 ? (
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
@@ -224,12 +165,7 @@ const AddressGeolocation: React.FC<IAddressGeolocation> = ({ route: routeParams
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
hitSlop={HIT_SLOP}
|
||||
onPress={() =>
|
||||
handleRouting(CaseDetailStackEnum.ADDITIONAL_ADDRESSES, {
|
||||
fetchUngroupedAddress,
|
||||
ungroupedAddressFeedbacks,
|
||||
})
|
||||
}
|
||||
onPress={() => handleRouting(CaseDetailStackEnum.ADDITIONAL_ADDRESSES)}
|
||||
>
|
||||
<Text style={styles.actionBtn}>View all addresses</Text>
|
||||
</TouchableOpacity>
|
||||
@@ -294,11 +230,6 @@ const styles = StyleSheet.create({
|
||||
lineHeight: 20,
|
||||
color: COLORS.TEXT.BLUE,
|
||||
},
|
||||
textContainer: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
color: COLORS.TEXT.BLACK_24,
|
||||
},
|
||||
hidden: {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
import { IAddress } from '@interfaces/addressGeolocation.types';
|
||||
import { getDistanceFromLatLonInKm } from '../../../components/utlis/commonFunctions';
|
||||
|
||||
interface FilterFarAwayMetaAddressesParams {
|
||||
interface filterMetaAddressesByDistance {
|
||||
metaAddresses: any[];
|
||||
maximumDistance: number;
|
||||
currentLocationCoordinates: { latitude: number; longitude: number };
|
||||
}
|
||||
const filterFarAwayMetaAddresses = ({
|
||||
const filterMetaAddressesByDistance = ({
|
||||
metaAddresses = [],
|
||||
maximumDistance,
|
||||
currentLocationCoordinates,
|
||||
}: FilterFarAwayMetaAddressesParams) =>
|
||||
metaAddresses?.filter((metaAddress) => {
|
||||
}: filterMetaAddressesByDistance) => {
|
||||
const farAwayAddresses: IAddress[] = [];
|
||||
const nearByAddresses: IAddress[] = [];
|
||||
metaAddresses?.forEach((metaAddress) => {
|
||||
const distance = getDistanceFromLatLonInKm(currentLocationCoordinates, {
|
||||
latitude: metaAddress?.metaAddress?.latitude,
|
||||
longitude: metaAddress?.metaAddress?.longitude,
|
||||
});
|
||||
if (distance >= maximumDistance || isNaN(distance)) {
|
||||
return true;
|
||||
return farAwayAddresses.push(metaAddress);
|
||||
}
|
||||
return false;
|
||||
return nearByAddresses.push(metaAddress);
|
||||
});
|
||||
return { nearByAddresses, farAwayAddresses };
|
||||
};
|
||||
|
||||
export default filterFarAwayMetaAddresses;
|
||||
export default filterMetaAddressesByDistance;
|
||||
|
||||
@@ -38,6 +38,7 @@ import agentPerformanceSlice from '../reducer/agentPerformanceSlice';
|
||||
import telephoneNumbersSlice from '../reducer/telephoneNumbersSlice';
|
||||
import { getStorageEngine } from '../PersistStorageEngine';
|
||||
import litmusExperimentSlice from '@reducers/litmusExperimentSlice';
|
||||
import ungroupedAddressesSlice from '@reducers/ungroupedAddressesSlice';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
case: caseReducer,
|
||||
@@ -64,6 +65,7 @@ const rootReducer = combineReducers({
|
||||
agentPerformance: agentPerformanceSlice,
|
||||
telephoneNumbers: telephoneNumbersSlice,
|
||||
litmusExperiment: litmusExperimentSlice,
|
||||
ungroupedAddresses: ungroupedAddressesSlice,
|
||||
});
|
||||
|
||||
const persistConfig = {
|
||||
@@ -88,7 +90,7 @@ const persistConfig = {
|
||||
'feedbackFilters',
|
||||
'litmusExperiment',
|
||||
],
|
||||
blackList: ['case', 'filters', 'reportees', 'agentPerformance'],
|
||||
blackList: ['case', 'filters', 'reportees', 'agentPerformance', 'ungroupedAddresses'],
|
||||
};
|
||||
|
||||
const persistedReducer = persistReducer(persistConfig, rootReducer);
|
||||
|
||||
@@ -51,6 +51,8 @@ export interface IAddress {
|
||||
metaAddressReferences: IMetaAddress[];
|
||||
similarAddresses?: IAddress[];
|
||||
createdAt?: string;
|
||||
metaAddress: IAddress;
|
||||
contactabilityStatus: string;
|
||||
}
|
||||
|
||||
export interface IGroupedAddressesItem {
|
||||
|
||||
Reference in New Issue
Block a user