diff --git a/.github/workflows/newBuild.yml b/.github/workflows/newBuild.yml
index a8c6a098..ddc37bbe 100644
--- a/.github/workflows/newBuild.yml
+++ b/.github/workflows/newBuild.yml
@@ -1,4 +1,4 @@
-name: Create-Apk-QA
+name: generate-apk
on:
workflow_dispatch:
@@ -8,23 +8,31 @@ on:
required: true
type: choice
options:
- - qa
- - dev
+ - QA
+ - Prod
+ flavor:
+ description: Choose build flavour
+ required: true
+ type: choice
+ options:
+ - fieldAgents
+ - callingAgents
type:
description: Choose build type
required: true
type: choice
options:
- - debug
- release
version_code:
description: Enter app version code (example, 292)
- required: false
+ required: true
type: string
+ default: "292"
version_name:
description: Enter app version name (example, 3.2.1)
- required: false
+ required: true
type: string
+ default: "3.2.1"
jobs:
generate:
runs-on: [ default ]
@@ -69,21 +77,21 @@ jobs:
run: chmod +x android/gradlew
- name: Create local.properties
run: cd android && touch local.properties && echo "sdk.dir = /home/USERNAME/Android/Sdk" > local.properties
- - name: Assemble with Stacktrace - QA debug
- if: ((github.event.inputs.environment == 'qa' || inputs.environment == 'qa') && (github.event.inputs.type == 'debug' || inputs.type == 'debug'))
- run: yarn move:qa && yarn debug && cd android && ./gradlew assembleDebug
- - name: Assemble with Stacktrace - Dev debug
- if: ((github.event.inputs.environment == 'dev' || inputs.environment == 'dev') && (github.event.inputs.type == 'debug' || inputs.type == 'debug'))
- run: yarn move:dev && yarn debug && cd android && ./gradlew assembleDebug
- - name: Assemble with Stacktrace - QA release
- if: ((github.event.inputs.environment == 'qa' || inputs.environment == 'qa') && (github.event.inputs.type == 'release' || inputs.type == 'release'))
- run: yarn move:qa && cd android && ./gradlew assembleRelease
- - name: Assemble with Stacktrace - Dev release
- if: ((github.event.inputs.environment == 'dev' || inputs.environment == 'dev') && (github.event.inputs.type == 'debug' || inputs.type == 'release'))
- run: yarn move:dev && cd android && ./gradlew assembleRelease
+ - name: Assemble with Stacktrace - Field QA release
+ if: ((github.event.inputs.environment == 'QA' || inputs.environment == 'QA') && (github.event.flavor.type == 'fieldAgents' || inputs.flavor == 'fieldAgents'))
+ run: yarn move:qa && cd android && ./gradlew assemblefieldAgentsQARelease
+ - name: Assemble with Stacktrace - Field PROD release
+ if: ((github.event.inputs.environment == 'Prod' || inputs.environment == 'Prod') && (github.event.flavor.type == 'fieldAgents' || inputs.flavor == 'fieldAgents'))
+ run: yarn move:prod && cd android && ./gradlew assemblefieldAgentsProdRelease
+ - name: Assemble with Stacktrace - Calling QA release
+ if: ((github.event.inputs.environment == 'QA' || inputs.environment == 'QA') && (github.event.flavor.type == 'callingAgents' || inputs.flavor == 'callingAgents'))
+ run: yarn move:qa && cd android && ./gradlew assemblefieldAgentsQARelease
+ - name: Assemble with Stacktrace - Calling PROD release
+ if: ((github.event.inputs.environment == 'Prod' || inputs.environment == 'Prod') && (github.event.flavor.type == 'callingAgents' || inputs.flavor == 'callingAgents'))
+ run: yarn move:prod && cd android && ./gradlew assemblefieldAgentsProdRelease
- name: Upload APK as Artifact
uses: actions/upload-artifact@v3
with:
- name: app-${{ github.event.inputs.type || inputs.type }}
- path: android/app/build/outputs/apk/${{ github.event.inputs.type || inputs.type }}/
+ name: app-${{ github.event.inputs.type || inputs.type }}-v${{ github.event.inputs.version_code || inputs.version_code }}-name-${{github.event.inputs.version_name || inputs.version_name}}
+ path: android/app/build/outputs/apk/${{ github.event.inputs.flavor || inputs.flavor }}${{github.event.inputs.environment || inputs.environment}}/${{github.event.inputs.type || inputs.type}}
retention-days: 30
diff --git a/android/app/build.gradle b/android/app/build.gradle
index a36fe82e..0b4a2be9 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -313,7 +313,7 @@ dependencies {
implementation "com.github.anrwatchdog:anrwatchdog:1.4.0"
- implementation 'com.navi.medici:alfred:v1.0.1'
+ implementation 'com.navi.medici:alfred:v1.0.2'
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index b6c2906d..463e9f52 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -35,6 +35,7 @@
+
{
+ Log.d("Alfred", "setBottomSheetView nativeViewHierarchyManager:" + nativeViewHierarchyManager);
+ View view = nativeViewHierarchyManager.resolveView(refID);
+ Log.d("Alfred", "setBottomSheetView view:" + view);
+ AlfredManager.INSTANCE.setBottomSheetView(view);
+ });
+ } catch (Exception error) {
+ Log.d("Alfred", "setBottomSheetView error:" + error);
+ }
+ }
}
- return;
}
+
+ @ReactMethod
+ public void clearBottomSheet() {
+ AlfredManager.INSTANCE.clearBottomSheetView();
+ }
+
private static File convertBase64ToFile(Context context,String base64Data) {
try {
byte[] decodedBytes = Base64.decode(base64Data, Base64.DEFAULT);
@@ -207,50 +229,87 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
}
}
- public boolean isWhatsAppInstalled() {
+ public ArrayList isWhatsAppInstalled() {
PackageManager packageManager = RNContext.getPackageManager();
List packages = packageManager.getInstalledPackages(PackageManager.GET_META_DATA);
+ ArrayList appsInstalled = new ArrayList();
for (PackageInfo packageInfo : packages) {
String packageName = packageInfo.packageName;
- if(packageName.equals("com.whatsapp")){
- return true;
+ if(packageName.equals("com.whatsapp") || packageName.equals("com.whatsapp.w4b")){
+ appsInstalled.add(packageName);
}
}
- return false;
+ return appsInstalled;
+ }
+
+ public Intent getWhatsappShareIntent(String message, String imageUrl, String mimeType, String packageName) {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, message);
+
+ if (imageUrl.equals("")) {
+ sendIntent.setType("text/plain");
+ sendIntent.setPackage(packageName);
+ } else {
+ sendIntent.setType(mimeType);
+ sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ imageFile = convertBase64ToFile(getReactApplicationContext(), imageUrl);
+ Uri fileUri = FileProvider.getUriForFile(getReactApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
+ imageFile.getName()
+ )
+ );
+ sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
+ sendIntent.setPackage(packageName);
+ }
+
+ return sendIntent;
}
@ReactMethod
public void sendFeedbackToWhatsapp(String message, String imageUrl, String mimeType, Promise promise) {
try{
- if(!isWhatsAppInstalled()){
+ ArrayList appsInstalled = isWhatsAppInstalled();
+ int numberOfAppsInstalled = appsInstalled.size();
+
+ if(numberOfAppsInstalled == 0){
promise.reject("errorCode", "1");
return;
}
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, message);
-
- if(imageUrl.equals("")) {
- sendIntent.setType("text/plain");
- sendIntent.setPackage("com.whatsapp");
- getCurrentActivity().startActivity(sendIntent);
- } else {
- sendIntent.setType(mimeType);
- sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- imageFile = convertBase64ToFile(getReactApplicationContext(), imageUrl);
- Uri fileUri = FileProvider.getUriForFile(getReactApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", new File(
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
- imageFile.getName()
- )
- );
- sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
- sendIntent.setPackage("com.whatsapp");
- getCurrentActivity().startActivityForResult(sendIntent, WHATSAPP_SHARE_REQUEST_CODE);
+ else if(numberOfAppsInstalled == 1) {
+ String packageName = appsInstalled.get(0);
+ Intent sendIntent = getWhatsappShareIntent(message, imageUrl, mimeType, packageName);
+ if(getCurrentActivity()!=null) {
+ getCurrentActivity().startActivityForResult(sendIntent, WHATSAPP_SHARE_REQUEST_CODE);
+ }
+ promise.resolve(true);
+ return;
+ }
+
+ else {
+ String packageName1 = appsInstalled.get(0);
+ String packageName2 = appsInstalled.get(1);
+ //Firing two intents, one for WhatsApp, another for WhatsApp business
+ Intent sendIntent1 = getWhatsappShareIntent(message, imageUrl, mimeType, packageName1);
+ Intent sendIntent2 = getWhatsappShareIntent(message, imageUrl, mimeType, packageName2);
+ ArrayList appIntents = new ArrayList<>();
+ appIntents.add(sendIntent1);
+ appIntents.add(sendIntent2);
+
+ Intent defaultIntent = new Intent(android.content.Intent.ACTION_SEND);
+ defaultIntent.setType("text/plain");
+ defaultIntent.putExtra(android.content.Intent.EXTRA_TEXT, "Sharing to WhatsApp");
+
+ Intent chooserIntent = Intent.createChooser(defaultIntent, "Share via");
+ chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, appIntents.toArray(new Parcelable[appIntents.size()]));
+ if (getCurrentActivity() != null) {
+ getCurrentActivity().startActivityForResult(chooserIntent, WHATSAPP_SHARE_REQUEST_CODE);
+ }
+ promise.resolve(true);
+ return;
}
- promise.resolve(true);
- return;
} catch (Error e){
promise.reject("errorCode","2");
diff --git a/src/action/feedbackActions.ts b/src/action/feedbackActions.ts
index 27c0f7be..00f89844 100644
--- a/src/action/feedbackActions.ts
+++ b/src/action/feedbackActions.ts
@@ -9,6 +9,13 @@ interface IPastFeedbacksPayload {
addressReferenceIds?: string; // required to fetch past feedback on addresses
}
+export interface IFilterPayload {
+ filters: Array<{
+ filterName: string;
+ selectedValue: string[];
+ }>;
+}
+
interface IPastFeedbacksOnAddressesPayload {
loanAccountNumber: string;
pageNo?: number;
@@ -16,12 +23,13 @@ interface IPastFeedbacksOnAddressesPayload {
addressReferenceIds?: string;
}
-export const getPastFeedbacks = (queryParamsPayload: IPastFeedbacksPayload) => {
- const url = getApiUrl(ApiKeys.PAST_FEEDBACK);
+export const getPastFeedbacks = (
+ queryParamsPayload: IPastFeedbacksPayload,
+ filterPayload: IFilterPayload
+) => {
+ const url = getApiUrl(ApiKeys.PAST_FEEDBACK, {}, queryParamsPayload);
return axiosInstance
- .get(url, {
- params: queryParamsPayload,
- })
+ .post(url, filterPayload)
.then((response) => {
if (response?.data) {
return {
diff --git a/src/assets/icons/FilterIcon.tsx b/src/assets/icons/FilterIcon.tsx
index 825c24eb..95b472d0 100644
--- a/src/assets/icons/FilterIcon.tsx
+++ b/src/assets/icons/FilterIcon.tsx
@@ -1,10 +1,13 @@
import * as React from 'react';
import Svg, { Path } from 'react-native-svg';
-const FilterIcon = () => (
+import { IconProps } from '../../../RN-UI-LIB/src/Icons/types';
+import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
+
+const FilterIcon: React.FC = ({ fillColor = COLORS.BACKGROUND.LIGHT }) => (
);
diff --git a/src/common/BottomSheetWrapper.tsx b/src/common/BottomSheetWrapper.tsx
index 01d9561d..15ecb14c 100644
--- a/src/common/BottomSheetWrapper.tsx
+++ b/src/common/BottomSheetWrapper.tsx
@@ -2,24 +2,28 @@ import React from 'react';
import BottomSheet, {
BottomSheetProps,
} from '../../RN-UI-LIB/src/components/bottom_sheet/BottomSheet';
-import { sendBottomSheetOpenSignal } from '../components/utlis/DeviceUtils';
-import { NativeSyntheticEvent } from 'react-native';
+import { clearBottomSheet, setBottomSheetView } from '../components/utlis/DeviceUtils';
interface IBottomSheetWrapperProps extends BottomSheetProps {}
const BottomSheetWrapper: React.FC = (props) => {
const { children, onShow, onSwipeDownClose, onClose, ...restProps } = props;
const onCloseHandler = () => {
- sendBottomSheetOpenSignal(false);
+ clearBottomSheet();
if (typeof onClose === 'function') onClose();
};
- const onShowHandler = (event: NativeSyntheticEvent) => {
- sendBottomSheetOpenSignal(true);
- if (typeof onShow === 'function') onShow(event);
+ const onAnimationEndHandler = (id: number | null) => {
+ if (!id) return;
+ setBottomSheetView(id);
};
return (
-
+
{children}
);
diff --git a/src/common/Constants.ts b/src/common/Constants.ts
index 4f347b30..1441ae6c 100644
--- a/src/common/Constants.ts
+++ b/src/common/Constants.ts
@@ -747,6 +747,7 @@ export const REQUEST_TO_UNBLOCK_FOR_IMPERSONATION = [
getApiUrl(ApiKeys.GET_SIGNED_URL),
getApiUrl(ApiKeys.GET_SIGNED_URL_FOR_REPORTEE),
getApiUrl(ApiKeys.LOGOUT),
+ getApiUrl(ApiKeys.PAST_FEEDBACK),
];
export const NAVI_AGENCY_CODE = '1000';
diff --git a/src/common/DropDownWrapper.tsx b/src/common/DropDownWrapper.tsx
index e3ba6f4b..97f20582 100644
--- a/src/common/DropDownWrapper.tsx
+++ b/src/common/DropDownWrapper.tsx
@@ -1,19 +1,27 @@
import React from 'react';
import Dropdown, { IDropdown } from '../../RN-UI-LIB/src/components/dropdown/Dropdown';
-import { sendBottomSheetOpenSignal } from '../components/utlis/DeviceUtils';
+import {
+ clearBottomSheet,
+ sendBottomSheetOpenSignal,
+ setBottomSheetView,
+} from '../components/utlis/DeviceUtils';
const DropDownWrapper: React.FC = (props) => {
- const { onShow, onClose, children, ...remainingProps } = props;
- const onShowHandler = () => {
- if (typeof onShow === 'function') onShow();
- sendBottomSheetOpenSignal(true);
- };
+ const { onShow, onClose, onAnimationEnd, children, ...remainingProps } = props;
+
const onCloseHandler = () => {
if (typeof onClose === 'function') onClose();
- sendBottomSheetOpenSignal(false);
+ clearBottomSheet();
};
+
+ const onAnimationEndHandler = (id: number | null) => {
+ if (!id) return;
+ console.log('dropdown opened', id);
+ setBottomSheetView(id);
+ };
+
return (
-
+
{children}
);
diff --git a/src/common/ModalWrapperForAlfredV2.tsx b/src/common/ModalWrapperForAlfredV2.tsx
new file mode 100644
index 00000000..4577809f
--- /dev/null
+++ b/src/common/ModalWrapperForAlfredV2.tsx
@@ -0,0 +1,45 @@
+import React, { useEffect } from 'react';
+import { Modal, NativeSyntheticEvent, View, findNodeHandle } from 'react-native';
+import { IModalWrapper } from '../../RN-UI-LIB/src/components/modalWrapper/ModalWrapper';
+import {
+ clearBottomSheet,
+ sendBottomSheetOpenSignal,
+ setBottomSheetView,
+} from '../components/utlis/DeviceUtils';
+import { GenericStyles } from '@rn-ui-lib/styles';
+
+const ModalWrapperForAlfredV2: React.FC = ({ children, ...props }) => {
+ const { onRequestClose, onShow, visible } = props;
+ const modalRef = React.useRef(null);
+ const lastSent = React.useRef(visible);
+ const onRequestCloseHandler = (event: NativeSyntheticEvent) => {
+ if (typeof onRequestClose === 'function') onRequestClose(event);
+ clearBottomSheet();
+ };
+ const onShowHandler = (event: NativeSyntheticEvent) => {
+ if (typeof onShow === 'function') onShow(event);
+ const nodeId = findNodeHandle(modalRef.current);
+ lastSent.current = true;
+ setBottomSheetView(nodeId);
+ };
+
+ return (
+
+
+ {children}
+
+
+ );
+};
+
+export default ModalWrapperForAlfredV2;
diff --git a/src/components/filters/FilterOptions.tsx b/src/components/filters/FilterOptions.tsx
new file mode 100644
index 00000000..fc0955e2
--- /dev/null
+++ b/src/components/filters/FilterOptions.tsx
@@ -0,0 +1,122 @@
+import { View } from 'react-native';
+import React from 'react';
+import { IFilters, TFilterOptions } from './Filters';
+import RadioGroup from '../../../RN-UI-LIB/src/components/radio_button/RadioGroup';
+import RNRadioButton from '../../../RN-UI-LIB/src/components/radio_button/RadioButton';
+import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
+import Checkbox from '../../../RN-UI-LIB/src/components/chechbox/Checkbox';
+
+interface IFilterOptions {
+ filters: IFilters;
+ selectedFilter: string;
+ selectedFilterOptions: TFilterOptions;
+ setSelectedFilterOptions: React.Dispatch>;
+}
+
+enum FilterTypes {
+ RADIO_GROUP = 'RADIO_GROUP',
+ CHECKBOX_GROUP = 'CHECKBOX_GROUP',
+}
+
+const FilterOptions: React.FC = ({
+ filters,
+ selectedFilter,
+ selectedFilterOptions,
+ setSelectedFilterOptions,
+}) => {
+ if (!selectedFilter || !filters?.[selectedFilter]) {
+ return null;
+ }
+
+ const handleSingleOptionChange = (value: string) => {
+ if (selectedFilter) {
+ const selectedOption = !!selectedFilterOptions?.[selectedFilter]?.filters?.[value];
+ const currentSelectedFilterCount =
+ selectedFilterOptions?.[selectedFilter]?.selectedFilterCount || 0;
+ if (selectedOption && currentSelectedFilterCount === 1) {
+ // delete the selected filter if we are unchecking the only selected option
+ setSelectedFilterOptions((prev) => {
+ const updatedOptions = { ...prev };
+ delete updatedOptions[selectedFilter];
+ return updatedOptions;
+ });
+ return;
+ }
+ setSelectedFilterOptions({
+ ...selectedFilterOptions,
+ [selectedFilter]: {
+ filters: {
+ [value]: !selectedOption,
+ },
+ selectedFilterCount: selectedOption ? 0 : 1,
+ },
+ });
+ }
+ };
+
+ const handleMultiOptionChange = (isChecked: boolean, values: string) => {
+ if (selectedFilter) {
+ const currentFilters = selectedFilterOptions?.[selectedFilter]?.filters || {};
+ const currentSelectedFilterCount =
+ selectedFilterOptions?.[selectedFilter]?.selectedFilterCount || 0;
+ if (!isChecked && currentSelectedFilterCount === 1) {
+ // delete the selected filter if we are unchecking the only selected option
+ setSelectedFilterOptions((prev) => {
+ const updatedOptions = { ...prev };
+ delete updatedOptions[selectedFilter];
+ return updatedOptions;
+ });
+ return;
+ }
+ setSelectedFilterOptions({
+ ...selectedFilterOptions,
+ [selectedFilter]: {
+ filters: {
+ ...currentFilters,
+ [values]: isChecked,
+ },
+ selectedFilterCount: isChecked
+ ? currentSelectedFilterCount + 1
+ : currentSelectedFilterCount - 1,
+ },
+ });
+ }
+ };
+
+ const filter = filters[selectedFilter];
+ const selectionType = filter.filterType;
+ const selectedOptions = selectedFilterOptions[selectedFilter]?.filters || {};
+
+ if (selectionType === FilterTypes.RADIO_GROUP) {
+ const selectedOption = Object.keys(selectedOptions)[0];
+ return (
+ handleSingleOptionChange(value)}
+ orientation="vertical"
+ >
+ {filter.options.map((option) => (
+
+ ))}
+
+ );
+ } else if (selectionType === FilterTypes.CHECKBOX_GROUP) {
+ return (
+ <>
+ {filter.options.map((option) => (
+
+ handleMultiOptionChange(value, option.value)}
+ />
+
+ ))}
+ >
+ );
+ }
+
+ return null;
+};
+
+export default FilterOptions;
diff --git a/src/components/filters/Filters.tsx b/src/components/filters/Filters.tsx
new file mode 100644
index 00000000..892284ee
--- /dev/null
+++ b/src/components/filters/Filters.tsx
@@ -0,0 +1,196 @@
+import React, { useEffect, useState } from 'react';
+import { View, ScrollView, StyleSheet, PixelRatio, Pressable } from 'react-native';
+import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
+import Text from '../../../RN-UI-LIB/src/components/Text';
+import Button from '../../../RN-UI-LIB/src/components/Button';
+import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
+import { _map } from '../../../RN-UI-LIB/src/utlis/common';
+import FilterOptions from './FilterOptions';
+import NavigationHeader from '../../../RN-UI-LIB/src/components/NavigationHeader';
+
+interface FilterOption {
+ value: string;
+ label: string;
+}
+
+interface FilterData {
+ filterType: string;
+ displayText: string;
+ options: FilterOption[];
+ name: string;
+}
+
+export interface IFilters {
+ [key: string]: FilterData;
+}
+
+interface IFilterOptions {
+ filters: Record;
+ selectedFilterCount: number;
+}
+
+export type TFilterOptions = Record;
+
+const Filters: React.FC<{
+ header: string;
+ filters: IFilters;
+ defaultSelectedFilters: TFilterOptions;
+ closeFilterModal: () => void;
+ onFilterChange: (selectedFilters: TFilterOptions) => void;
+}> = ({ header, filters, defaultSelectedFilters, closeFilterModal, onFilterChange }) => {
+ // Default select first filter
+ const [selectedFilter, setSelectedFilter] = useState(Object.keys(filters)[0]);
+ const [selectedFilterOptions, setSelectedFilterOptions] =
+ useState(defaultSelectedFilters);
+
+ useEffect(() => {
+ if (defaultSelectedFilters) {
+ setSelectedFilterOptions(defaultSelectedFilters);
+ }
+ }, [defaultSelectedFilters]);
+
+ const handleFilterChange = (filterName: string) => {
+ setSelectedFilter(filterName);
+ };
+
+ const handleSubmit = () => {
+ onFilterChange(selectedFilterOptions);
+ closeFilterModal();
+ };
+
+ const handleClearAll = () => {
+ onFilterChange({});
+ setSelectedFilterOptions({});
+ };
+
+ return (
+
+
+
+
+ {/* @ts-expect-error */}
+ {_map(filters, (filterKey) => {
+ const filter = filters[filterKey];
+ const filterName = filter.name;
+
+ return (
+ handleFilterChange(filterName)}
+ style={[
+ GenericStyles.row,
+ GenericStyles.spaceBetween,
+ GenericStyles.ph12,
+ { paddingVertical: 8 },
+ GenericStyles.br6,
+ GenericStyles.mb8,
+ GenericStyles.alignCenter,
+ {
+ backgroundColor:
+ selectedFilter === filterName
+ ? COLORS.BACKGROUND.BLUE
+ : COLORS.BACKGROUND.PRIMARY,
+ },
+ ]}
+ >
+
+ {filter.displayText}
+
+ {selectedFilterOptions[filterName]?.selectedFilterCount ? (
+
+
+ {selectedFilterOptions[filterName]?.selectedFilterCount}
+
+
+ ) : (
+
+ )}
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ leftSection: {
+ width: '47%',
+ borderRightWidth: 1,
+ borderRightColor: COLORS.BORDER.PRIMARY,
+ paddingHorizontal: 8,
+ paddingVertical: 16,
+ },
+ rightSection: {
+ width: '53%',
+ paddingVertical: 16,
+ },
+ btn: {
+ margin: 0,
+ borderRadius: 0,
+ borderBottomWidth: 0,
+ borderTopWidth: 1,
+ borderTopColor: COLORS.BORDER.PRIMARY,
+ },
+ filterCountContainer: {
+ height: PixelRatio.roundToNearestPixel(25),
+ width: PixelRatio.roundToNearestPixel(25),
+ },
+ filterCount: {
+ backgroundColor: COLORS.BACKGROUND.SILVER,
+ borderColor: COLORS.BORDER.PRIMARY,
+ borderWidth: 1,
+ borderRadius: 20,
+ alignItems: 'center',
+ },
+ filterCountSelected: {
+ backgroundColor: COLORS.TEXT.BLUE,
+ borderRadius: 20,
+ alignItems: 'center',
+ },
+});
+
+export default Filters;
diff --git a/src/components/utlis/DeviceUtils.ts b/src/components/utlis/DeviceUtils.ts
index 28ad745d..dacfb45a 100644
--- a/src/components/utlis/DeviceUtils.ts
+++ b/src/components/utlis/DeviceUtils.ts
@@ -29,6 +29,10 @@ export const alfredSetUserId = (userId: string) => DeviceUtilsModule.setUserId(u
export const sendBottomSheetOpenSignal = (e: boolean) =>
DeviceUtilsModule.sendBottomSheetOpenSignal(e);
+export const setBottomSheetView = (id: number | null) => DeviceUtilsModule.setBottomSheetView(id);
+
+export const clearBottomSheet = () => DeviceUtilsModule.clearBottomSheet();
+
export const alfredSetEmailId = (emailId: string) => DeviceUtilsModule.setEmailId(emailId);
// sends feedback data to whatsapp.
diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts
index e8b5da23..ceb6be4c 100644
--- a/src/components/utlis/apiHelper.ts
+++ b/src/components/utlis/apiHelper.ts
@@ -79,7 +79,7 @@ API_URLS[ApiKeys.GET_SIGNED_URL] = '/cases/get-signed-urls';
API_URLS[ApiKeys.GET_SIGNED_URL_FOR_REPORTEE] = '/cases/get-signed-urls-for-reportee';
API_URLS[ApiKeys.CASE_UNIFIED_DETAILS] = '/v3/collection-cases/unified-details/{loanAccountNumber}';
API_URLS[ApiKeys.UNGROUPED_ADDRESSES] = '/addresses/ungrouped-v2/{loanAccountNumber}';
-API_URLS[ApiKeys.PAST_FEEDBACK] = '/feedback';
+API_URLS[ApiKeys.PAST_FEEDBACK] = '/feedback/filters';
API_URLS[ApiKeys.PAST_FEEDBACK_ON_ADDRESSES] = '/feedback/v2';
API_URLS[ApiKeys.NOTIFICATIONS] = '/notification/fetch';
API_URLS[ApiKeys.NOTIFICATION_ACTION] = '/notification/action';
diff --git a/src/components/utlis/commonFunctions.ts b/src/components/utlis/commonFunctions.ts
index 993f96cd..caf22894 100644
--- a/src/components/utlis/commonFunctions.ts
+++ b/src/components/utlis/commonFunctions.ts
@@ -368,10 +368,3 @@ export function getDistanceFromLatLonInKm(
const distance = 2 * Math.atan2(Math.sqrt(intermediateResult), Math.sqrt(1 - intermediateResult));
return EARTH_RADIUS * distance;
}
-
-export function insertCommasinAmount(amount: number | undefined) {
- const reversedAmount = amount?.toString().split('').reverse().join('');
- const groups = reversedAmount?.match(/.{1,3}/g);
- const result = groups?.join(',').split('').reverse().join('');
- return result;
-}
diff --git a/src/hooks/useFirestoreUpdates.ts b/src/hooks/useFirestoreUpdates.ts
index 9d004efa..bc69c563 100644
--- a/src/hooks/useFirestoreUpdates.ts
+++ b/src/hooks/useFirestoreUpdates.ts
@@ -15,6 +15,7 @@ import { ToastMessages } from '../screens/allCases/constants';
import { setForceUninstallData } from '../reducer/metadataSlice';
import { logError } from '../components/utlis/errorUtils';
import { GenericFunctionArgs } from '../common/GenericTypes';
+import { setFeedbackFilterTemplate } from '@reducers/feedbackFiltersSlice';
export interface CaseUpdates {
updateType: string;
@@ -53,6 +54,7 @@ const useFirestoreUpdates = () => {
let filterUnsubscribe: GenericFunctionArgs;
let forceUninstallUnsubscribe: GenericFunctionArgs;
let lockUnsubscribe: GenericFunctionArgs;
+ let feedbackFiltersUnsubscribe: GenericFunctionArgs;
const dispatch = useAppDispatch();
@@ -154,6 +156,13 @@ const useFirestoreUpdates = () => {
lockData && dispatch(setLockData(lockData));
};
+ const handleFeedbackFilters = (
+ snapshot: FirebaseFirestoreTypes.DocumentSnapshot
+ ) => {
+ const feedbackFilters = snapshot.data();
+ dispatch(setFeedbackFilterTemplate(feedbackFilters));
+ };
+
const handleError = (err: any, collectionPath?: string) => {
const errMsg = `Error while fetching fireStore snapshot: referenceId: ${user?.referenceId} collectionPath: ${collectionPath}`;
logError(err as Error, errMsg);
@@ -217,9 +226,15 @@ const useFirestoreUpdates = () => {
refId = selectedAgent?.referenceId;
}
const collectionPath = `filters/${refId}`;
+
return subscribeToDoc(handleFilterUpdate, collectionPath);
};
+ const subscribeToFeedbackFilters = () => {
+ const feedbackFiltersPath = `feedback-filters/v1`;
+ return subscribeToDoc(handleFeedbackFilters, feedbackFiltersPath);
+ };
+
const subscribeToUserConfig = () => {
const collectionPath = `config/${user?.referenceId}`;
return subscribeToDoc(handleConfigUpdate, collectionPath);
@@ -241,6 +256,7 @@ const useFirestoreUpdates = () => {
avTemplateUnSubscriber = subscribeToAvTemplate();
collectionTemplateUnsubscribe = subscribeToCollectionTemplate();
lockUnsubscribe = subscribeToLocks();
+ feedbackFiltersUnsubscribe = subscribeToFeedbackFilters();
}
useEffect(() => {
@@ -264,6 +280,7 @@ const useFirestoreUpdates = () => {
avTemplateUnSubscriber && avTemplateUnSubscriber();
collectionTemplateUnsubscribe && collectionTemplateUnsubscribe();
lockUnsubscribe && lockUnsubscribe();
+ feedbackFiltersUnsubscribe && feedbackFiltersUnsubscribe();
};
}, [isLoggedIn, user?.referenceId]);
diff --git a/src/reducer/feedbackFiltersSlice.ts b/src/reducer/feedbackFiltersSlice.ts
new file mode 100644
index 00000000..8866d4a0
--- /dev/null
+++ b/src/reducer/feedbackFiltersSlice.ts
@@ -0,0 +1,33 @@
+import { TFilterOptions } from '@components/filters/Filters';
+import { createSlice } from '@reduxjs/toolkit';
+import { IFilter } from '@screens/allCases/interface';
+
+interface IFeedbackFiltersSlice {
+ feedbackFiltersTemplate: IFilter | null;
+ selectedCaseFilters: Record;
+}
+
+const initialState: IFeedbackFiltersSlice = {
+ selectedCaseFilters: {},
+ feedbackFiltersTemplate: null,
+};
+
+const FeedbackFiltersSlice = createSlice({
+ name: 'feedbackFilters',
+ initialState,
+ reducers: {
+ setFeedbackFilterTemplate: (state, action) => {
+ if (action.payload) {
+ state.feedbackFiltersTemplate = action.payload;
+ }
+ },
+ setFeedbackFilters: (state, action) => {
+ const { loanAccountNumber, filters } = action.payload;
+ state.selectedCaseFilters[loanAccountNumber] = filters;
+ },
+ },
+});
+
+export const { setFeedbackFilterTemplate, setFeedbackFilters } = FeedbackFiltersSlice.actions;
+
+export default FeedbackFiltersSlice.reducer;
diff --git a/src/reducer/feedbackHistorySlice.ts b/src/reducer/feedbackHistorySlice.ts
index 4c58e4de..6b223140 100644
--- a/src/reducer/feedbackHistorySlice.ts
+++ b/src/reducer/feedbackHistorySlice.ts
@@ -1,5 +1,6 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { IFeedback } from '../types/feedback.types';
+import { TFilterOptions } from '../components/filters/Filters';
interface IFeedbackHistoryState {
[loanAccountNumber: string]: {
@@ -19,6 +20,7 @@ const FeedbackHistorySlice = createSlice({
setFeedbackHistory: (state, action) => {
const { loanAccountNumber, feedbacks, totalPages } = action.payload;
state[loanAccountNumber] = {
+ ...(state[loanAccountNumber] || {}),
data: feedbacks,
timestamp: new Date().toISOString(),
isLoading: false,
@@ -32,7 +34,7 @@ const FeedbackHistorySlice = createSlice({
const payloadData = action.payload;
payloadData.loanAccountNumbers.forEach((loanAccNumber) => {
state[loanAccNumber] = {
- ...(state?.[loanAccNumber] || []),
+ ...(state?.[loanAccNumber] || {}),
isLoading: payloadData.isLoading,
};
});
diff --git a/src/screens/allCases/CasesList.tsx b/src/screens/allCases/CasesList.tsx
index 502f938d..71f1f819 100644
--- a/src/screens/allCases/CasesList.tsx
+++ b/src/screens/allCases/CasesList.tsx
@@ -56,6 +56,7 @@ import BottomSheetWrapper from '../../common/BottomSheetWrapper';
import { toast } from '../../../RN-UI-LIB/src/components/toast';
import { setFilteredListToast } from '../../reducer/allCasesSlice';
import { getFilterCount, getSelectedFilters } from '../Dashboard/utils';
+import ModalWrapperForAlfredV2 from '@common/ModalWrapperForAlfredV2';
export const getItem = (item: Array, index: number) => item[index];
export const ESTIMATED_ITEM_SIZE = 250; // Average height of List item
@@ -354,7 +355,7 @@ const CasesList: React.FC = ({
{listEmptyComponent}
)}
- {
@@ -370,7 +371,7 @@ const CasesList: React.FC = ({
isVisitPlan={isVisitPlan}
isAgentDashboard={isAgentDashboard}
/>
-
+
(
diff --git a/src/screens/auth/ProtectedRouter.tsx b/src/screens/auth/ProtectedRouter.tsx
index bdf3125f..02aeb2b6 100644
--- a/src/screens/auth/ProtectedRouter.tsx
+++ b/src/screens/auth/ProtectedRouter.tsx
@@ -128,14 +128,13 @@ const ProtectedRouter = () => {
// Firestore listener hook
useFirestoreUpdates();
-
React.useEffect(() => {
// Watching Position for significant change
CaptureGeolocation.watchLocation((location: DeviceLocation) =>
- dispatch(setDeviceGeolocation(location))
+ dispatch(setDeviceGeolocation(location))
);
}, []);
-
+
if (isLoading) return ;
return (
diff --git a/src/screens/caseDetails/feedback/FeedbackDetailContainer.tsx b/src/screens/caseDetails/feedback/FeedbackDetailContainer.tsx
index 86578db4..5c5c8c85 100644
--- a/src/screens/caseDetails/feedback/FeedbackDetailContainer.tsx
+++ b/src/screens/caseDetails/feedback/FeedbackDetailContainer.tsx
@@ -1,11 +1,15 @@
-import React, { useCallback, useEffect, useState } from 'react';
-import { RefreshControl, ScrollView, StyleSheet, View } from 'react-native';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import { Modal, RefreshControl, ScrollView, StyleSheet, View } from 'react-native';
import Accordion from '../../../../RN-UI-LIB/src/components/accordian/Accordian';
import NavigationHeader from '../../../../RN-UI-LIB/src/components/NavigationHeader';
import Text from '../../../../RN-UI-LIB/src/components/Text';
import { GenericStyles, SCREEN_HEIGHT, getShadowStyle } from '../../../../RN-UI-LIB/src/styles';
import { COLORS } from '../../../../RN-UI-LIB/src/styles/colors';
-import { getPastFeedbacks, getPastFeedbacksOnAddresses } from '../../../action/feedbackActions';
+import {
+ IFilterPayload,
+ getPastFeedbacks,
+ getPastFeedbacksOnAddresses,
+} from '../../../action/feedbackActions';
import { GenericType } from '../../../common/GenericTypes';
import { logError } from '../../../components/utlis/errorUtils';
import { goBack } from '../../../components/utlis/navigationUtlis';
@@ -25,6 +29,10 @@ import { setFeedbackHistoryLoading } from '../../../reducer/feedbackHistorySlice
import SuspenseLoader from '../../../../RN-UI-LIB/src/components/suspense_loader/SuspenseLoader';
import LineLoader from '../../../../RN-UI-LIB/src/components/suspense_loader/LineLoader';
import NoPastFeedbackIcon from '../../../assets/icons/NoPastFeedbackIcon';
+import Button from '../../../../RN-UI-LIB/src/components/Button';
+import FilterIcon from '../../../assets/icons/FilterIcon';
+import Filters, { TFilterOptions } from '../../../components/filters/Filters';
+import { _map } from '../../../../RN-UI-LIB/src/utlis/common';
import ChevronDown from '../../../assets/icons/ChevronDown';
import ChevronUp from '../../../assets/icons/ChevronUp';
@@ -46,6 +54,21 @@ interface IFeedbackDetailContainer {
};
}
+const getFiltersPayload = (selectedFilters: TFilterOptions) => {
+ const payload: IFilterPayload = { filters: [] };
+ _map(selectedFilters, (filterName: string) => {
+ const filter = selectedFilters[filterName];
+ if (!filter || !filter.filters) {
+ return;
+ }
+ payload.filters.push({
+ filterName,
+ selectedValue: Object.keys(filter.filters),
+ });
+ });
+ return payload;
+};
+
const FeedbackDetailContainer: React.FC = ({ route: routeParams }) => {
const {
params: {
@@ -60,13 +83,17 @@ const FeedbackDetailContainer: React.FC = ({ route: ro
const isPastFeedbackOnAddress = addressText || addressReferenceIds?.length;
const [isExpanded, setIsExpanded] = useState(false);
+ const [feedbackFilters, setFeedbackFilters] = useState({});
const isOnline = useIsOnline();
const dispatch = useAppDispatch();
- const { feedbackListFromCache, feedbackTotalPages } = useAppSelector((state: RootState) => ({
- feedbackListFromCache: state.feedbackHistory?.[loanAccountNumber as string]?.data || [],
- feedbackTotalPages: state.feedbackHistory?.[loanAccountNumber as string]?.totalPages || 0,
- }));
+ const { feedbackListFromCache, feedbackTotalPages, feedbackFiltersTemplate } = useAppSelector(
+ (state: RootState) => ({
+ feedbackListFromCache: state.feedbackHistory?.[loanAccountNumber as string]?.data || [],
+ feedbackTotalPages: state.feedbackHistory?.[loanAccountNumber as string]?.totalPages || 0,
+ feedbackFiltersTemplate: state.feedbackFilters?.feedbackFiltersTemplate || {},
+ })
+ );
const [feedbackList, setFeedbackList] = useState(
!isPastFeedbackOnAddress ? feedbackListFromCache : []
@@ -75,53 +102,65 @@ const FeedbackDetailContainer: React.FC = ({ route: ro
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
+ const [dataSourceCord, setDataSourceCord] = useState(0);
+ const [ref, setRef] = useState();
+ const [showFilterModal, setShowFilterModal] = useState(false);
- const fetchFeedbacks = useCallback(() => {
- const getPastFeedbackApiFn = isPastFeedbackOnAddress
- ? getPastFeedbacksOnAddresses
- : getPastFeedbacks;
+ const fetchFeedbacks = useCallback(
+ (filtersPayload: IFilterPayload) => {
+ const getPastFeedbackApiFn = isPastFeedbackOnAddress
+ ? getPastFeedbacksOnAddresses
+ : getPastFeedbacks;
- if (isPastFeedbackOnAddress) {
- dispatch(
- setFeedbackHistoryLoading({ loanAccountNumbers: [loanAccountNumber], isLoading: true })
- );
- }
-
- getPastFeedbackApiFn({
- loan_account_number: loanAccountNumber,
- page_no: currentPage - 1,
- page_size: FEEDBACK_PER_PAGE,
- customerRecahble: false,
- addressReferenceIds,
- })
- .then((res: { data: IFeedback[]; totalPage: number } | GenericType) => {
- if (res?.data?.length) {
- setFeedbackList(res.data);
- setTotalPage(res.totalPage);
- return;
- }
- throw res;
- })
- .catch((err) => {
- addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_VIEW_PAST_FEEDBACK_NAVIGATION_PAGE_FAILED, {
- targetPageNumber: currentPage,
- lan: loanAccountNumber,
+ if (isPastFeedbackOnAddress) {
+ dispatch(
+ setFeedbackHistoryLoading({ loanAccountNumbers: [loanAccountNumber], isLoading: true })
+ );
+ }
+ getPastFeedbackApiFn(
+ {
+ loan_account_number: loanAccountNumber,
+ page_no: currentPage - 1,
+ page_size: FEEDBACK_PER_PAGE,
+ customerRecahble: false,
+ addressReferenceIds,
+ },
+ filtersPayload
+ )
+ .then((res: { data: IFeedback[]; totalPage: number } | GenericType) => {
+ if (res?.data) {
+ setFeedbackList(res.data || []);
+ setTotalPage(res.totalPage || 0);
+ return;
+ }
+ throw res;
+ })
+ .catch((err) => {
+ addClickstreamEvent(
+ CLICKSTREAM_EVENT_NAMES.FA_VIEW_PAST_FEEDBACK_NAVIGATION_PAGE_FAILED,
+ {
+ targetPageNumber: currentPage,
+ lan: loanAccountNumber,
+ }
+ );
+ logError(err);
+ })
+ .finally(() => {
+ setLoading(false);
});
- logError(err);
- })
- .finally(() => {
- setLoading(false);
- });
- }, [currentPage]);
+ },
+ [currentPage]
+ );
useEffect(() => {
if (!isPastFeedbackOnAddress && currentPage == 1) {
setFeedbackList(feedbackListFromCache);
} else {
- setLoading(true);
setFeedbackList([]);
}
- fetchFeedbacks();
+ setLoading(true);
+ const filtersPayload = getFiltersPayload(feedbackFilters);
+ fetchFeedbacks(filtersPayload);
}, [currentPage]);
useEffect(() => {
@@ -139,9 +178,6 @@ const FeedbackDetailContainer: React.FC = ({ route: ro
}
}, []);
- const [dataSourceCord, setDataSourceCord] = useState(0);
- const [ref, setRef] = useState();
-
useEffect(() => {
if (!ref || !dataSourceCord) {
return;
@@ -171,6 +207,19 @@ const FeedbackDetailContainer: React.FC = ({ route: ro
});
};
+ const handleFilterChange = (filters: TFilterOptions) => {
+ const filtersPayload = getFiltersPayload(filters);
+ if (currentPage === 1) {
+ fetchFeedbacks(filtersPayload);
+ setLoading(true);
+ } else {
+ setCurrentPage(1);
+ }
+ setFeedbackFilters(filters);
+ };
+
+ const feedbackFilterCount = Object.keys(feedbackFilters)?.length;
+
return (
@@ -178,9 +227,43 @@ const FeedbackDetailContainer: React.FC = ({ route: ro
title={!isPastFeedbackOnAddress ? FEEDBACK_PAGE_TITLE : ADDRESS_FEEDBACK_PAGE_TITLE}
onBack={goBack}
/>
+ {!isPastFeedbackOnAddress ? (
+
+
+ }
+ style={getShadowStyle(2)}
+ title="Filters"
+ buttonStyle={[GenericStyles.ph12, { paddingVertical: 8 }]}
+ onPress={() => setShowFilterModal(true)}
+ />
+ {feedbackFilterCount ? (
+
+
+ {feedbackFilterCount}
+
+
+ ) : null}
+
+ ) : null}
+
}
- style={[GenericStyles.ph16, GenericStyles.mt16]}
+ refreshControl={
+ {
+ const filtersPayload = getFiltersPayload(feedbackFilters);
+ fetchFeedbacks(filtersPayload);
+ }}
+ />
+ }
+ style={[GenericStyles.ph16, GenericStyles.fill]}
ref={(x) => setRef(x)}
>
= ({ route: ro
>
}
>
-
- {feedbackList?.length ? (
- <>
- {isPastFeedbackOnAddress ? (
-
-
- Feedback for
-
-
- {addressText}
-
-
- ) : null}
- {feedbackList.map((feedback: IFeedback, index) => (
- {
- const layout = event.nativeEvent.layout;
- if (!dataSourceCord && feedback.referenceId === activeFeedbackReferenceId) {
- setDataSourceCord(layout.y + SCROLL_LAYOUT_OFFSET);
- }
+ {feedbackList?.length ? (
+ <>
+ {isPastFeedbackOnAddress ? (
+
+
+ Feedback for
+
+
+ {addressText}
+
+
+ ) : null}
+ {feedbackList.map((feedback: IFeedback, index) => (
+ {
+ const layout = event.nativeEvent.layout;
+ if (!dataSourceCord && feedback.referenceId === activeFeedbackReferenceId) {
+ setDataSourceCord(layout.y + SCROLL_LAYOUT_OFFSET);
+ }
+ }}
+ >
+
+ }
+ customExpandUi={{
+ whenCollapsed: ,
+ whenExpanded: ,
+ }}
+ onExpanded={(value) => {
+ setIsExpanded(value);
}}
>
-
- }
- customExpandUi={{
- whenCollapsed: ,
- whenExpanded: ,
- }}
- onExpanded={(value) => {
- setIsExpanded(value);
- }}
- >
-
-
-
- ))}
-
- handlePageChangeClickstream(
- page,
- CLICKSTREAM_EVENT_NAMES.FA_VIEW_PAST_FEEDBACK_NEXT_PAGE_CLICKED
- )
- }
- onPrevPage={(page) =>
- handlePageChangeClickstream(
- page,
- CLICKSTREAM_EVENT_NAMES.FA_VIEW_PAST_FEEDBACK_PREV_PAGE_CLICKED
- )
- }
- onFirstPage={(page) =>
- handlePageChangeClickstream(
- page,
- CLICKSTREAM_EVENT_NAMES.FA_VIEW_PAST_FEEDBACK_FIRST_PAGE_CLICKED
- )
- }
- onLastPage={(page) =>
- handlePageChangeClickstream(
- page,
- CLICKSTREAM_EVENT_NAMES.FA_VIEW_PAST_FEEDBACK_LAST_PAGE_CLICKED
- )
- }
- />
- >
- ) : (
-
-
-
- No previous feedback found
-
-
- )}
-
+
+
+
+ ))}
+ >
+ ) : (
+
+
+
+ No previous feedback found
+
+
+ )}
+ {feedbackList?.length && totalPage > 1 && !loading ? (
+
+ handlePageChangeClickstream(
+ page,
+ CLICKSTREAM_EVENT_NAMES.FA_VIEW_PAST_FEEDBACK_NEXT_PAGE_CLICKED
+ )
+ }
+ onPrevPage={(page) =>
+ handlePageChangeClickstream(
+ page,
+ CLICKSTREAM_EVENT_NAMES.FA_VIEW_PAST_FEEDBACK_PREV_PAGE_CLICKED
+ )
+ }
+ onFirstPage={(page) =>
+ handlePageChangeClickstream(
+ page,
+ CLICKSTREAM_EVENT_NAMES.FA_VIEW_PAST_FEEDBACK_FIRST_PAGE_CLICKED
+ )
+ }
+ onLastPage={(page) =>
+ handlePageChangeClickstream(
+ page,
+ CLICKSTREAM_EVENT_NAMES.FA_VIEW_PAST_FEEDBACK_LAST_PAGE_CLICKED
+ )
+ }
+ />
+ ) : null}
+ {!isPastFeedbackOnAddress ? (
+ {
+ setShowFilterModal((prev) => !prev);
+ }}
+ visible={showFilterModal}
+ >
+ setShowFilterModal((prev) => !prev)}
+ filters={feedbackFiltersTemplate}
+ onFilterChange={handleFilterChange}
+ />
+
+ ) : null}
);
@@ -329,8 +431,7 @@ const styles = StyleSheet.create({
fontWeight: '500',
},
noFeedbackContainer: {
- height: SCREEN_HEIGHT - 56,
- backgroundColor: COLORS.BACKGROUND.PRIMARY,
+ marginTop: '50%',
},
accordionExpandBtn: {
fontSize: 13,
@@ -338,6 +439,17 @@ const styles = StyleSheet.create({
lineHeight: 20,
color: COLORS.TEXT.BLUE,
},
+ filterCount: {
+ backgroundColor: COLORS.TEXT.BLUE,
+ width: 18,
+ height: 18,
+ borderRadius: 9,
+ alignItems: 'center',
+ position: 'absolute',
+ opacity: 0.8,
+ top: 7,
+ right: 11,
+ },
accordianPadding: {
paddingTop: 16,
paddingBottom: 8,
diff --git a/src/screens/caseDetails/feedback/FeedbackDetailItem.tsx b/src/screens/caseDetails/feedback/FeedbackDetailItem.tsx
index 08854bee..033dd6e6 100644
--- a/src/screens/caseDetails/feedback/FeedbackDetailItem.tsx
+++ b/src/screens/caseDetails/feedback/FeedbackDetailItem.tsx
@@ -10,8 +10,8 @@ import {
} from '../../../../RN-UI-LIB/src/utlis/dates';
import { CaseDetail, Address as IAddress, IGeolocation, VisitType } from '../interface';
import {
+ debounce,
getGoogleMapUrl,
- insertCommasinAmount,
sanitizeString,
} from '../../../components/utlis/commonFunctions';
import {
@@ -33,6 +33,7 @@ import { useAppSelector } from '../../../hooks';
import { toast } from '../../../../RN-UI-LIB/src/components/toast';
import { ToastMessages } from '../../allCases/constants';
import { sendFeedbackToWhatsapp } from '../../../components/utlis/DeviceUtils';
+import { getSanitizedCommaAmount } from '@rn-ui-lib/utils/amount';
interface IFeedbackDetailItem {
feedbackItem: IFeedback;
@@ -71,122 +72,133 @@ function getLocationLink(latitude: string, longitude: string): string {
return link;
}
-const sendToWhatsappNative = (
- message: string,
- imageUrl: string,
- mimeType: string,
- caseDetails: CaseDetail,
- agentId: string
-) => {
- sendFeedbackToWhatsapp(message, imageUrl, mimeType)
- .then((res: boolean) => {
- addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SHARE_SUCCESSFUL, {
- caseId: caseDetails?.id,
- agentId: agentId,
- });
- })
- .catch((err: Error) => {
- if (err.message === '1') {
- toast({
- text1: ToastMessages.WHATSAPP_NOT_INSTALLED,
- type: 'error',
- });
- } else {
- toast({
- text1: ToastMessages.WHATSAPP_FEEDBACK_SHARE_FAILURE,
- type: 'error',
- });
- }
- });
-};
-
-const sendToWhatsapp = (feedbackItem: IFeedback, caseDetails: CaseDetail, agentId: string) => {
- addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SHARE_FEEDBACK_CLICKED, {
- caseId: caseDetails?.id,
- agentId: agentId,
- });
-
- var message = `*Visit Feedback* for ${sanitizeString(caseDetails?.customerName)}
-_${sanitizeString(dateFormat(new Date(feedbackItem?.createdAt), 'DD MMM, YYYY | HH:mm a.'))}_\n
-*LAN*: ${sanitizeString(caseDetails?.loanAccountNumber)}
-*DPD Bucket*: ${sanitizeString(caseDetails?.dpdBucket)}
-*EMI Amount*: ₹${insertCommasinAmount(caseDetails?.outstandingEmiDetails?.[0]?.emiAmount)}\n
-*Disposition*: ${sanitizeString(feedbackItem?.interactionStatus)}`;
-
- const ptpDate = feedbackItem?.answerViews.filter((answer) => answer.questionName === 'PTP Date');
- if (ptpDate.length > 0) {
- message +=
- ' for ' + sanitizeString(dateFormat(new Date(ptpDate[0].inputDate), 'DD MMM, YYYY')) + '\n\n';
- } else {
- message += '\n\n';
- }
-
- message += '*Remarks*: ';
- const answerList = feedbackItem?.answerViews?.filter(
- (answer) => answer.questionName === 'Comments'
- );
- if (answerList.length > 0) {
- message += sanitizeString(answerList[0]?.inputText) + '\n\n';
- } else {
- message += 'N.A.\n\n';
- }
-
- {
- feedbackItem?.metadata?.interactionLatitude &&
- feedbackItem?.metadata?.interactionLongitude &&
- FIELD_FEEDBACKS.includes(feedbackItem?.type)
- ? (message +=
- '*Location of feedback*: ' +
- sanitizeString(
- getLocationLink(
- feedbackItem?.metadata?.interactionLatitude,
- feedbackItem?.metadata?.interactionLongitude
- )
- ) +
- '\n\n')
- : null;
- }
-
- const imagesList = feedbackItem?.answerViews.filter(
- (answer) => answer.questionTag === OPTION_TAG.IMAGE_UPLOAD
- );
- var imageUrl = '';
- const mimeType = 'image/*';
- if (imagesList.length > 0) {
- var imageUri = '';
- imageUri = imagesList[0]?.inputText ? imagesList[0].inputText : '';
- let imagePath = '';
- ReactNativeBlobUtil.config({
- fileCache: true,
- })
- .fetch('GET', imageUri)
- .then((resp: any) => {
- if (resp.info().status !== 200) {
- return '';
- } else {
- imagePath = resp.path();
- return resp.readFile('base64');
- }
- })
- .then((base64Data: any) => {
- imageUrl = base64Data;
- sendToWhatsappNative(message, imageUrl, mimeType, caseDetails, agentId);
- ReactNativeBlobUtil.fs.unlink(imagePath);
- });
- } else {
- sendToWhatsappNative(message, imageUrl, mimeType, caseDetails, agentId);
- }
-};
-
const FeedbackDetailItem = ({
feedbackItem,
isExpanded,
caseId,
hideAddress,
}: IFeedbackDetailItem) => {
- const isGeolocation = feedbackItem?.source?.sourceType === VisitType.GEOLOCATION;
const caseDetails = useAppSelector((state) => state.allCases.caseDetails[caseId]);
const { agentId } = useAppSelector((state) => ({ agentId: state.user.user?.referenceId!! }));
+ const [isWhastappSendLoading, setIsWhatsappSendLoading] = useState(false);
+ const isGeolocation = feedbackItem?.source?.sourceType === VisitType.GEOLOCATION;
+
+ const sendToWhatsappNative = (
+ message: string,
+ imageUrl: string,
+ mimeType: string,
+ caseDetails: CaseDetail,
+ agentId: string
+ ) => {
+ setIsWhatsappSendLoading(true);
+ sendFeedbackToWhatsapp(message, imageUrl, mimeType)
+ .then((res: boolean) => {
+ addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SHARE_SUCCESSFUL, {
+ caseId: caseDetails?.id,
+ agentId: agentId,
+ });
+ setIsWhatsappSendLoading(false);
+ })
+ .catch((err: Error) => {
+ setIsWhatsappSendLoading(false);
+ if (err.message === '1') {
+ toast({
+ text1: ToastMessages.WHATSAPP_NOT_INSTALLED,
+ type: 'error',
+ });
+ } else {
+ toast({
+ text1: ToastMessages.WHATSAPP_FEEDBACK_SHARE_FAILURE,
+ type: 'error',
+ });
+ }
+ });
+ };
+
+ const sendToWhatsapp = (feedbackItem: IFeedback, caseDetails: CaseDetail, agentId: string) => {
+ setIsWhatsappSendLoading(true);
+ addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SHARE_FEEDBACK_CLICKED, {
+ caseId: caseDetails?.id,
+ agentId: agentId,
+ });
+
+ let message = `*Visit Feedback* for ${sanitizeString(caseDetails?.customerName)}
+_${sanitizeString(dateFormat(new Date(feedbackItem?.createdAt), 'DD MMM, YYYY | HH:mm a.'))}_\n
+*LAN*: ${sanitizeString(caseDetails?.loanAccountNumber)}
+*DPD Bucket*: ${sanitizeString(caseDetails?.dpdBucket)}
+*EMI Amount*: ₹${getSanitizedCommaAmount(caseDetails?.currentOutstandingEmi)}\n
+*Disposition*: ${sanitizeString(feedbackItem?.interactionStatus)}`;
+
+ const ptpDate = feedbackItem?.answerViews.filter(
+ (answer) => answer.questionName === 'PTP Date'
+ );
+ if (ptpDate.length > 0) {
+ message +=
+ ' for ' +
+ sanitizeString(dateFormat(new Date(ptpDate[0].inputDate), 'DD MMM, YYYY')) +
+ '\n\n';
+ } else {
+ message += '\n\n';
+ }
+
+ message += '*Remarks*: ';
+ const answerList = feedbackItem?.answerViews?.filter(
+ (answer) => answer.questionName === 'Comments'
+ );
+ if (answerList.length > 0) {
+ message += sanitizeString(answerList[0]?.inputText) + '\n\n';
+ } else {
+ message += 'N.A.\n\n';
+ }
+
+ {
+ feedbackItem?.metadata?.interactionLatitude &&
+ feedbackItem?.metadata?.interactionLongitude &&
+ FIELD_FEEDBACKS.includes(feedbackItem?.type)
+ ? (message +=
+ '*Location of feedback*: ' +
+ sanitizeString(
+ getLocationLink(
+ feedbackItem?.metadata?.interactionLatitude,
+ feedbackItem?.metadata?.interactionLongitude
+ )
+ ) +
+ '\n\n')
+ : null;
+ }
+
+ const imagesList = feedbackItem?.answerViews.filter(
+ (answer) => answer.questionTag === OPTION_TAG.IMAGE_UPLOAD
+ );
+ let imageUrl = '';
+ const mimeType = 'image/*';
+ if (imagesList.length > 0) {
+ let imageUri = '';
+ imageUri = imagesList[0]?.inputText ? imagesList[0].inputText : '';
+ let imagePath = '';
+ ReactNativeBlobUtil.config({
+ fileCache: true,
+ })
+ .fetch('GET', imageUri)
+ .then((resp: any) => {
+ if (resp.info().status !== 200) {
+ return '';
+ } else {
+ imagePath = resp.path();
+ return resp.readFile('base64');
+ }
+ })
+ .then((base64Data: any) => {
+ imageUrl = base64Data;
+ sendToWhatsappNative(message, imageUrl, mimeType, caseDetails, agentId);
+ ReactNativeBlobUtil.fs.unlink(imagePath);
+ });
+ } else {
+ sendToWhatsappNative(message, imageUrl, mimeType, caseDetails, agentId);
+ }
+ };
+
+ const throttledSendToWhatsapp = React.useRef(debounce(sendToWhatsapp, 500));
return (
@@ -273,8 +285,11 @@ const FeedbackDetailItem = ({
) : null}
sendToWhatsapp(feedbackItem, caseDetails, agentId)}
+ onPress={() => {
+ throttledSendToWhatsapp.current(feedbackItem, caseDetails, agentId);
+ }}
style={[GenericStyles.row, styles.BtnPadding]}
+ disabled={isWhastappSendLoading}
>