NTP-7914 | Feedback Section Revamp (#1009)
This commit is contained in:
Submodule RN-UI-LIB updated: 019bc50b01...348345f1bb
@@ -134,8 +134,8 @@ def reactNativeArchitectures() {
|
||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||
}
|
||||
|
||||
def VERSION_CODE = 213
|
||||
def VERSION_NAME = "2.14.13"
|
||||
def VERSION_CODE = 214
|
||||
def VERSION_NAME = "2.15.0"
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "AV_APP",
|
||||
"version": "2.14.13",
|
||||
"buildNumber": "213",
|
||||
"version": "2.15.0",
|
||||
"buildNumber": "214",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android:dev": "yarn move:dev && react-native run-android",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { AppDispatch } from '@store';
|
||||
import axiosInstance, { ApiKeys, getApiUrl } from '../components/utlis/apiHelper';
|
||||
import { logError } from '../components/utlis/errorUtils';
|
||||
import { setTopFeedbacks, setTopFeedbacksLoading } from '@reducers/topFeedbacksSlice';
|
||||
|
||||
interface IPastFeedbacksPayload {
|
||||
loan_account_number: string;
|
||||
@@ -70,3 +72,28 @@ export const getPastFeedbacksOnAddresses = (pastFeedbackPayload: IPastFeedbacksP
|
||||
logError(err);
|
||||
});
|
||||
};
|
||||
|
||||
export const getTopFeedbacks = (loanAccountNumber: string) => (dispatch: AppDispatch) => {
|
||||
// TODO: Change API Endpoint
|
||||
const url = getApiUrl(ApiKeys.PAST_FEEDBACK_ON_ADDRESSES);
|
||||
dispatch(setTopFeedbacksLoading({ loanAccountNumber, isLoading: true }));
|
||||
return axiosInstance
|
||||
.get(url, {
|
||||
params: { loanAccountNumber },
|
||||
})
|
||||
.then((response) => {
|
||||
dispatch(
|
||||
setTopFeedbacks({
|
||||
loanAccountNumber,
|
||||
feedbacks: [
|
||||
response?.data?.data?.currentMonthFeedbackStatus,
|
||||
response?.data?.data?.lastMonthFeedbackStatus,
|
||||
],
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
dispatch(setTopFeedbacksLoading({ loanAccountNumber, isLoading: false }));
|
||||
logError(err);
|
||||
});
|
||||
};
|
||||
|
||||
19
src/assets/icons/NotAttemptedIcon.tsx
Normal file
19
src/assets/icons/NotAttemptedIcon.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import Svg, { Path } from 'react-native-svg';
|
||||
|
||||
function NotAttemptedIcon() {
|
||||
return (
|
||||
<Svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<Path
|
||||
d="M14.2292 8.57982C12.0192 8.57982 10.2292 6.78982 10.2292 4.57982C10.2292 4.22982 10.2692 3.88982 10.3692 3.57982H5.10922C4.06922 3.57982 3.19922 4.42982 3.19922 5.48982V16.0998C3.19922 17.1598 4.06922 18.0098 5.10922 18.0098H13.4992C14.5592 18.0098 15.4092 17.1598 15.4092 16.0998V8.40982C15.0392 8.52982 14.6392 8.58982 14.2292 8.58982V8.57982ZM9.25922 13.4798H6.72922C6.44922 13.4798 6.19922 13.2398 6.19922 12.9498C6.19922 12.6598 6.43922 12.4198 6.72922 12.4198H9.25922C9.55922 12.4198 9.78922 12.6498 9.78922 12.9498C9.78922 13.2498 9.55922 13.4798 9.25922 13.4798ZM11.3692 10.3898H6.72922C6.44922 10.3898 6.19922 10.1498 6.19922 9.85982C6.19922 9.56982 6.43922 9.32982 6.72922 9.32982H11.3692C11.6492 9.32982 11.8992 9.55982 11.8992 9.85982C11.8992 10.1598 11.6592 10.3898 11.3692 10.3898ZM13.4992 3.56982H11.8492C11.7092 3.87982 11.6392 4.21982 11.6392 4.56982C11.6392 5.98982 12.7992 7.15982 14.2292 7.15982C14.6492 7.15982 15.0492 7.04982 15.4092 6.85982V5.47982C15.4092 4.41982 14.5592 3.56982 13.4992 3.56982Z"
|
||||
fill="#969696"
|
||||
/>
|
||||
<Path
|
||||
d="M16.7987 4.57977C16.7987 5.57977 16.2287 6.44977 15.4087 6.86977C15.0487 7.05977 14.6487 7.16977 14.2287 7.16977C12.8087 7.16977 11.6387 6.00977 11.6387 4.57977C11.6387 4.22977 11.7087 3.87977 11.8487 3.57977C12.2387 2.64977 13.1587 2.00977 14.2187 2.00977C15.6387 2.00977 16.7987 3.15977 16.7987 4.58977V4.57977Z"
|
||||
fill="#969696"
|
||||
/>
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default NotAttemptedIcon;
|
||||
20
src/assets/icons/RightChevronIcon.tsx
Normal file
20
src/assets/icons/RightChevronIcon.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { G, Mask, Path, Rect, Svg } from 'react-native-svg';
|
||||
|
||||
const RightChevronIcon = () => {
|
||||
return (
|
||||
<Svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<Mask id="mask0_3190_8548" maskUnits="userSpaceOnUse" x="0" y="0" width="20" height="20">
|
||||
<Rect width="20" height="20" fill="#D9D9D9" />
|
||||
</Mask>
|
||||
<G mask="url(#mask0_3190_8548)">
|
||||
<Path
|
||||
d="M7.25065 14.4166C7.09787 14.2638 7.02148 14.0694 7.02148 13.8333C7.02148 13.5972 7.09787 13.4027 7.25065 13.25L10.5007 9.99996L7.25065 6.74996C7.09787 6.59718 7.02148 6.40274 7.02148 6.16663C7.02148 5.93051 7.09787 5.73607 7.25065 5.58329C7.40343 5.43051 7.59787 5.35413 7.83398 5.35413C8.07009 5.35413 8.26454 5.43051 8.41732 5.58329L12.2507 9.41663C12.334 9.49996 12.3932 9.59024 12.4282 9.68746C12.4626 9.78468 12.4798 9.88885 12.4798 9.99996C12.4798 10.1111 12.4626 10.2152 12.4282 10.3125C12.3932 10.4097 12.334 10.5 12.2507 10.5833L8.41732 14.4166C8.26454 14.5694 8.07009 14.6458 7.83398 14.6458C7.59787 14.6458 7.40343 14.5694 7.25065 14.4166Z"
|
||||
fill="#0276FE"
|
||||
/>
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default RightChevronIcon;
|
||||
@@ -3,13 +3,13 @@ import Svg, { Mask, Path, G, Rect } from 'react-native-svg';
|
||||
|
||||
function SmsIcon() {
|
||||
return (
|
||||
<Svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<Mask id="mask0_16298_100611" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16">
|
||||
<Rect width="16" height="16" fill="#D9D9D9" />
|
||||
<Svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<Mask id="mask0_3186_34194" maskUnits="userSpaceOnUse" x="0" y="0" width="20" height="20">
|
||||
<Rect width="20" height="20" fill="white" />
|
||||
</Mask>
|
||||
<G mask="url(#mask0_16298_100611)">
|
||||
<G mask="url(#mask0_3186_34194)">
|
||||
<Path
|
||||
d="M4.69788 9.33008H8.68535C8.87286 9.33008 9.03328 9.26299 9.16662 9.12883C9.29995 8.99466 9.36662 8.82841 9.36662 8.63008C9.36662 8.44119 9.29717 8.2773 9.15828 8.13841C9.0194 7.99952 8.8562 7.93008 8.66868 7.93008H4.68122C4.49371 7.93008 4.33328 7.99716 4.19995 8.13133C4.06662 8.26549 3.99995 8.43174 3.99995 8.63008C3.99995 8.81897 4.06939 8.98286 4.20828 9.12174C4.34717 9.26063 4.51037 9.33008 4.69788 9.33008ZM4.69735 7.36341H11.3192C11.5064 7.36341 11.6666 7.29633 11.8 7.16216C11.9333 7.02799 12 6.86174 12 6.66341C12 6.47452 11.9305 6.31063 11.7916 6.17174C11.6527 6.03286 11.4897 5.96341 11.3026 5.96341H4.68068C4.49353 5.96341 4.33328 6.03049 4.19995 6.16466C4.06662 6.29883 3.99995 6.46508 3.99995 6.66341C3.99995 6.8523 4.06939 7.01619 4.20828 7.15508C4.34717 7.29397 4.51019 7.36341 4.69735 7.36341ZM4.69735 5.39674H11.3192C11.5064 5.39674 11.6666 5.32966 11.8 5.19549C11.9333 5.06133 12 4.89508 12 4.69674C12 4.50786 11.9305 4.34397 11.7916 4.20508C11.6527 4.06619 11.4897 3.99674 11.3026 3.99674H4.68068C4.49353 3.99674 4.33328 4.06383 4.19995 4.19799C4.06662 4.33216 3.99995 4.49841 3.99995 4.69674C3.99995 4.88563 4.06939 5.04952 4.20828 5.18841C4.34717 5.3273 4.51019 5.39674 4.69735 5.39674ZM1.33328 12.9801V2.73008C1.33328 2.34508 1.47037 2.01549 1.74453 1.74133C2.0187 1.46716 2.34828 1.33008 2.73328 1.33008H13.2666C13.6516 1.33008 13.9812 1.46716 14.2554 1.74133C14.5295 2.01549 14.6666 2.34508 14.6666 2.73008V10.5967C14.6666 10.9817 14.5295 11.3113 14.2554 11.5855C13.9812 11.8597 13.6516 11.9967 13.2666 11.9967H3.99995L2.53328 13.4634C2.31106 13.6856 2.0555 13.7372 1.76662 13.6181C1.47773 13.499 1.33328 13.2863 1.33328 12.9801Z"
|
||||
d="M5.87177 11.6648H10.8561C11.0905 11.6648 11.291 11.5809 11.4577 11.4132C11.6244 11.2455 11.7077 11.0377 11.7077 10.7898C11.7077 10.5537 11.6209 10.3488 11.4473 10.1752C11.2737 10.0016 11.0697 9.91479 10.8353 9.91479H5.85093C5.61654 9.91479 5.41602 9.99865 5.24935 10.1664C5.08268 10.3341 4.99935 10.5419 4.99935 10.7898C4.99935 11.0259 5.08615 11.2308 5.25977 11.4044C5.43338 11.578 5.63738 11.6648 5.87177 11.6648ZM5.8711 9.20646H14.1484C14.3824 9.20646 14.5827 9.12261 14.7494 8.9549C14.916 8.78719 14.9994 8.57938 14.9994 8.33146C14.9994 8.09535 14.9125 7.89049 14.7389 7.71688C14.5653 7.54327 14.3615 7.45646 14.1276 7.45646H5.85027C5.61632 7.45646 5.41602 7.54032 5.24935 7.70802C5.08268 7.87573 4.99935 8.08355 4.99935 8.33146C4.99935 8.56757 5.08615 8.77243 5.25977 8.94604C5.43338 9.11966 5.63716 9.20646 5.8711 9.20646ZM5.8711 6.74813H14.1484C14.3824 6.74813 14.5827 6.66427 14.7494 6.49657C14.916 6.32886 14.9994 6.12105 14.9994 5.87313C14.9994 5.63702 14.9125 5.43216 14.7389 5.25854C14.5653 5.08493 14.3615 4.99813 14.1276 4.99813H5.85027C5.61632 4.99813 5.41602 5.08198 5.24935 5.24969C5.08268 5.4174 4.99935 5.62521 4.99935 5.87313C4.99935 6.10924 5.08615 6.3141 5.25977 6.48771C5.43338 6.66132 5.63716 6.74813 5.8711 6.74813ZM1.66602 16.2273V3.41479C1.66602 2.93354 1.83737 2.52157 2.18008 2.17886C2.52279 1.83615 2.93477 1.66479 3.41602 1.66479H16.5827C17.0639 1.66479 17.4759 1.83615 17.8186 2.17886C18.1613 2.52157 18.3327 2.93354 18.3327 3.41479V13.2481C18.3327 13.7294 18.1613 14.1414 17.8186 14.4841C17.4759 14.8268 17.0639 14.9981 16.5827 14.9981H4.99935L3.16602 16.8315C2.88824 17.1092 2.56879 17.1737 2.20768 17.0248C1.84657 16.8759 1.66602 16.6101 1.66602 16.2273Z"
|
||||
fill="#969696"
|
||||
/>
|
||||
</G>
|
||||
|
||||
44
src/reducer/topFeedbacksSlice.ts
Normal file
44
src/reducer/topFeedbacksSlice.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
interface ITopFeedback {
|
||||
status: string;
|
||||
color: string;
|
||||
referenceId: string;
|
||||
offset: number;
|
||||
createdAt: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface ITopFeedbackState {
|
||||
[loanAccountNumber: string]: {
|
||||
feedbacks: ITopFeedback[];
|
||||
isLoading: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const initialState: ITopFeedbackState = {};
|
||||
|
||||
const TopFeedbacksSlice = createSlice({
|
||||
name: 'topFeedbacks',
|
||||
initialState,
|
||||
reducers: {
|
||||
setTopFeedbacks: (state, action) => {
|
||||
const { loanAccountNumber, feedbacks } = action.payload || {};
|
||||
state[loanAccountNumber] = {
|
||||
...(state[loanAccountNumber] || {}),
|
||||
feedbacks,
|
||||
};
|
||||
},
|
||||
setTopFeedbacksLoading: (state, action) => {
|
||||
const { loanAccountNumber, isLoading } = action.payload || {};
|
||||
state[loanAccountNumber] = {
|
||||
...(state?.[loanAccountNumber] || {}),
|
||||
isLoading: isLoading,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setTopFeedbacks, setTopFeedbacksLoading } = TopFeedbacksSlice.actions;
|
||||
|
||||
export default TopFeedbacksSlice.reducer;
|
||||
@@ -60,63 +60,45 @@ const FeedbackDetailsSection = ({ caseId }: IFeedbackDetailsSection) => {
|
||||
return notSyncedCases;
|
||||
};
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.whiteBackground,
|
||||
styles.secondSection,
|
||||
getShadowStyle(2),
|
||||
GenericStyles.mt16,
|
||||
// Change Here
|
||||
GenericStyles.pv24,
|
||||
]}
|
||||
>
|
||||
<View style={[GenericStyles.pb24, GenericStyles.w100]}>
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
GenericStyles.alignCenter,
|
||||
GenericStyles.ph24,
|
||||
GenericStyles.pb16,
|
||||
GenericStyles.justifyContentSpaceBetween,
|
||||
GenericStyles.ph8,
|
||||
GenericStyles.pv16,
|
||||
]}
|
||||
>
|
||||
<View style={GenericStyles.row}>
|
||||
<View style={GenericStyles.flex60}>
|
||||
<Text style={[styles.textContainer, GenericStyles.fontSize14]}>Feedbacks</Text>
|
||||
</View>
|
||||
{feedbackList?.length ? (
|
||||
<View style={GenericStyles.flex40}>
|
||||
<Pressable
|
||||
onTouchStart={touchStartHandler}
|
||||
onPress={openAllFeedbacksHandler}
|
||||
style={({ pressed }) => [GenericStyles.flex20, { opacity: pressed ? 0.7 : 1 }]}
|
||||
>
|
||||
<Text style={[styles.textContainer, styles.feedbackBtn]}>Open all feedbacks</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
<Text style={[styles.textContainer, GenericStyles.fontSize14]}>Recent feedbacks</Text>
|
||||
{feedbackList?.length ? (
|
||||
<Pressable
|
||||
onTouchStart={touchStartHandler}
|
||||
onPress={openAllFeedbacksHandler}
|
||||
style={({ pressed }) => [{ opacity: pressed ? 0.7 : 1 }]}
|
||||
>
|
||||
<Text style={[styles.textContainer, styles.feedbackBtn]}>View all</Text>
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
<View style={[GenericStyles.whiteBackground, styles.secondSection, getShadowStyle(2)]}>
|
||||
<FeedbackListContainer
|
||||
feedbackList={[...getUnSyncedFeedback(), ...feedbackList]?.splice(0, 5)}
|
||||
loanAccountNumber={loanAccountNumber}
|
||||
caseId={caseId}
|
||||
/>
|
||||
</View>
|
||||
<View style={GenericStyles.borderTop} />
|
||||
<FeedbackListContainer
|
||||
feedbackList={[...getUnSyncedFeedback(), ...feedbackList]?.splice(0, 5)}
|
||||
loanAccountNumber={loanAccountNumber}
|
||||
caseId={caseId}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
secondSection: {
|
||||
alignSelf: 'center',
|
||||
borderRadius: 16,
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
textContainer: {
|
||||
fontSize: 13,
|
||||
lineHeight: 18,
|
||||
color: COLORS.TEXT.LIGHT,
|
||||
color: COLORS.TEXT.BLACK,
|
||||
},
|
||||
feedbackBtn: {
|
||||
fontWeight: '500',
|
||||
|
||||
@@ -50,6 +50,7 @@ interface IFeedbackDetailContainer {
|
||||
addressText?: string;
|
||||
activeFeedbackReferenceId?: string;
|
||||
caseId: string;
|
||||
pageNo?: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -77,6 +78,7 @@ const FeedbackDetailContainer: React.FC<IFeedbackDetailContainer> = ({ route: ro
|
||||
addressReferenceIds,
|
||||
addressText,
|
||||
caseId,
|
||||
pageNo = 1,
|
||||
},
|
||||
} = routeParams;
|
||||
|
||||
@@ -101,7 +103,7 @@ const FeedbackDetailContainer: React.FC<IFeedbackDetailContainer> = ({ route: ro
|
||||
const [totalPage, setTotalPage] = useState(!isPastFeedbackOnAddress ? feedbackTotalPages : 0);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [currentPage, setCurrentPage] = useState(pageNo);
|
||||
const [dataSourceCord, setDataSourceCord] = useState(0);
|
||||
const [ref, setRef] = useState<GenericType>();
|
||||
const [showFilterModal, setShowFilterModal] = useState(false);
|
||||
@@ -169,6 +171,10 @@ const FeedbackDetailContainer: React.FC<IFeedbackDetailContainer> = ({ route: ro
|
||||
}
|
||||
}, [isOnline]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentPage(pageNo ?? 1);
|
||||
}, [pageNo])
|
||||
|
||||
useEffect(() => {
|
||||
if (isPastFeedbackOnAddress) {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ADDRESS_FEEDBACK_LANDED, {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, ReactNode, useEffect, useState } from 'react';
|
||||
import React, { ReactNode, useState } from 'react';
|
||||
import { View, StyleSheet, TouchableOpacity, Linking, Platform, NativeModules } from 'react-native';
|
||||
import Text from '../../../../RN-UI-LIB/src/components/Text';
|
||||
import { GenericStyles } from '../../../../RN-UI-LIB/src/styles';
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
BUSINESS_TIME_FORMAT,
|
||||
dateFormat,
|
||||
} from '../../../../RN-UI-LIB/src/utlis/dates';
|
||||
import { Address, CaseDetail, Address as IAddress, IGeolocation, VisitType } from '../interface';
|
||||
import { Address, Address as IAddress, IGeolocation, VisitType } from '../interface';
|
||||
import {
|
||||
debounce,
|
||||
getGoogleMapUrl,
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
FIELD_FEEDBACKS,
|
||||
ICallingFeedback,
|
||||
IFeedback,
|
||||
OPTION_TAG,
|
||||
} from '../../../types/feedback.types';
|
||||
import MapIcon from '../../../../RN-UI-LIB/src/Icons/MapIcon';
|
||||
import { FEEDBACK_TYPE } from '../../../types/feedback.types';
|
||||
@@ -31,7 +30,7 @@ import WhatsAppFeedbackShareIcon from '../../../assets/icons/WhatsAppIcon';
|
||||
import { useAppSelector } from '../../../hooks';
|
||||
import { shareToWhatsapp } from '../../../services/FeedbackWhatsApp';
|
||||
import SmsIcon from "@assets/icons/SmsIcon";
|
||||
import Button from '@rn-ui-lib/components/Button';
|
||||
import NotAttemptedIcon from '@assets/icons/NotAttemptedIcon';
|
||||
|
||||
interface IFeedbackDetailItem {
|
||||
feedbackItem: IFeedback;
|
||||
@@ -46,6 +45,7 @@ export const feedbackTypeIcon: Record<FEEDBACK_TYPE, ReactNode> = {
|
||||
SELF_CALL: <CallIcon fillColor={COLORS.TEXT.LIGHT} />,
|
||||
CALL_BRIDGE: <CallIcon fillColor={COLORS.TEXT.LIGHT} />,
|
||||
GEN_AI_BOT_FIELD: <SmsIcon />,
|
||||
NOT_ATTEMPTED: <NotAttemptedIcon />
|
||||
};
|
||||
|
||||
const getAddress = (address?: Address) => {
|
||||
|
||||
@@ -8,12 +8,13 @@ import { GenericFunctionArgs } from '../../../common/GenericTypes';
|
||||
import Heading from '../../../../RN-UI-LIB/src/components/Heading';
|
||||
import LoadingIcon from '../../../../RN-UI-LIB/src/Icons/LoadingIcon';
|
||||
import FeedbackListItem from './FeedbackListItem';
|
||||
import { useAppDispatch, useAppSelector } from '../../../hooks';
|
||||
import { useAppDispatch } from '../../../hooks';
|
||||
import useIsOnline from '../../../hooks/useIsOnline';
|
||||
import NoFeedbackIcon from '../../../assets/icons/NoFeedbackIcon';
|
||||
import { RootState } from '../../../store/store';
|
||||
import OfflineIcon from '../../../../RN-UI-LIB/src/Icons/OfflineIcon';
|
||||
import { getCaseUnifiedData, UnifiedCaseDetailsTypes } from '../../../action/caseApiActions';
|
||||
import SuspenseLoader from '@rn-ui-lib/components/suspense_loader/SuspenseLoader';
|
||||
import FeedbackLoading from './FeedbackLoading';
|
||||
|
||||
interface IFeedbackListContainer {
|
||||
loanAccountNumber: string;
|
||||
@@ -93,17 +94,19 @@ const FeedbackListContainer: React.FC<IFeedbackListContainer> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
{feedbackList.map((feedbackItem: IFeedback | IUnSyncedFeedbackItem, idx: number) => (
|
||||
<FeedbackListItem
|
||||
key={feedbackItem.createdAt}
|
||||
feedbackItem={feedbackItem}
|
||||
loanAccountNumber={loanAccountNumber}
|
||||
showHorizontalLine={++idx !== feedbackList.length}
|
||||
caseId={caseId}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
<SuspenseLoader loading={false} fallBack={<FeedbackLoading />}>
|
||||
<View>
|
||||
{feedbackList.map((feedbackItem: IFeedback | IUnSyncedFeedbackItem, idx: number) => (
|
||||
<FeedbackListItem
|
||||
key={feedbackItem.createdAt}
|
||||
feedbackItem={feedbackItem}
|
||||
loanAccountNumber={loanAccountNumber}
|
||||
showHorizontalLine={++idx !== feedbackList.length}
|
||||
caseId={caseId}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</SuspenseLoader>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import Text from '../../../../RN-UI-LIB/src/components/Text';
|
||||
import Chevron from '../../../../RN-UI-LIB/src/Icons/Chevron';
|
||||
import RightChevronIcon from '@assets/icons/RightChevronIcon';
|
||||
import UnsyncedIcon from '../../../../RN-UI-LIB/src/Icons/UnsyncedIcon';
|
||||
import { GenericStyles } from '../../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../../RN-UI-LIB/src/styles/colors';
|
||||
@@ -15,12 +15,14 @@ import { useAppSelector } from '@hooks';
|
||||
import { shareToWhatsapp } from '../../../services/FeedbackWhatsApp';
|
||||
import { CaseDetailStackEnum } from '../CaseDetailStack';
|
||||
import { feedbackTypeIcon } from '@screens/caseDetails/feedback/FeedbackDetailItem';
|
||||
import Tag, { TagVariant } from '@rn-ui-lib/components/Tag';
|
||||
|
||||
interface IFeedbackListItem {
|
||||
feedbackItem: IFeedback | IUnSyncedFeedbackItem;
|
||||
showHorizontalLine?: boolean;
|
||||
loanAccountNumber: string;
|
||||
caseId: string;
|
||||
isTopFeedbackItem?: boolean;
|
||||
}
|
||||
|
||||
const FeedbackListItem: React.FC<IFeedbackListItem> = ({
|
||||
@@ -28,15 +30,19 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
|
||||
loanAccountNumber,
|
||||
caseId,
|
||||
showHorizontalLine = true,
|
||||
isTopFeedbackItem = false,
|
||||
}) => {
|
||||
const handleRouting = (route: CaseDetailStackEnum, params: object | undefined = undefined) => {
|
||||
const handleRouting = () => {
|
||||
if (!(feedbackItem as IFeedback).referenceId) return;
|
||||
const commonParams = {
|
||||
loanAccountNumber,
|
||||
activeFeedbackReferenceId: (feedbackItem as IFeedback).referenceId,
|
||||
caseId: caseId,
|
||||
};
|
||||
navigateToScreen(route, { ...params, ...commonParams });
|
||||
navigateToScreen(CaseDetailStackEnum.PAST_FEEDBACK_DETAIL, {
|
||||
...commonParams,
|
||||
pageNo: isTopFeedbackItem ? (feedbackItem as IFeedback)?.offset : 1,
|
||||
});
|
||||
};
|
||||
const [isWhastappSendLoading, setIsWhatsappSendLoading] = useState(false);
|
||||
const { agentId, caseDetails } = useAppSelector((state) => ({
|
||||
@@ -46,80 +52,93 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
|
||||
const throttledSendToWhatsapp = React.useRef(debounce(shareToWhatsapp, 500));
|
||||
|
||||
return (
|
||||
<View style={[GenericStyles.ph16]}>
|
||||
<View style={[!isTopFeedbackItem ? GenericStyles.ph16 : {}]}>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() => handleRouting(CaseDetailStackEnum.PAST_FEEDBACK_DETAIL)}
|
||||
activeOpacity={!(feedbackItem as IFeedback)?.referenceId ? 1 : 0.7}
|
||||
onPress={handleRouting}
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
GenericStyles.alignCenter,
|
||||
showHorizontalLine ? GenericStyles.pb16 : GenericStyles.pb4,
|
||||
GenericStyles.pv16,
|
||||
isTopFeedbackItem ? styles.topFeedbackItem : styles.feedbackItem,
|
||||
]}
|
||||
>
|
||||
{feedbackItem.isSynced === false ? (
|
||||
<View style={GenericStyles.pr16}>
|
||||
<UnsyncedIcon />
|
||||
{isTopFeedbackItem ? (
|
||||
<View style={[GenericStyles.absolute]}>
|
||||
<Tag
|
||||
text={(feedbackItem as IFeedback)?.tagTitle}
|
||||
variant={TagVariant.lightBlue}
|
||||
style={!showHorizontalLine ? styles.currentMonthFeedback : styles.lastMonthfeedback}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
<View style={styles.feedBox}>
|
||||
<View style={styles.textBox}>
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
||||
{feedbackItem.type && feedbackTypeIcon[feedbackItem.type] ? (
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.mr8,
|
||||
feedbackItem.type !== FEEDBACK_TYPE.GEN_AI_BOT_FIELD ? styles.ml_4 : null,
|
||||
]}
|
||||
>
|
||||
{feedbackTypeIcon[feedbackItem.type]}
|
||||
</View>
|
||||
) : null}
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
||||
{feedbackItem.isSynced === false ? (
|
||||
<View style={GenericStyles.pr16}>
|
||||
<UnsyncedIcon />
|
||||
</View>
|
||||
) : null}
|
||||
<View style={GenericStyles.alignCenter}>
|
||||
{feedbackItem.type && feedbackTypeIcon[feedbackItem.type] ? (
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.pb4,
|
||||
GenericStyles.mr16,
|
||||
styles.textHeading,
|
||||
feedbackItem.type === FEEDBACK_TYPE.GEN_AI_BOT_FIELD ? styles.capitalized : null,
|
||||
GenericStyles.mr12,
|
||||
GenericStyles.alignCenter,
|
||||
GenericStyles.justifyContentCenter,
|
||||
styles.feedbackIcon,
|
||||
feedbackItem.type !== FEEDBACK_TYPE.GEN_AI_BOT_FIELD ? styles.ml_4 : null,
|
||||
]}
|
||||
>
|
||||
{sanitizeString(feedbackItem.interactionStatus)}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={styles.subText}>
|
||||
{sanitizeString(
|
||||
`${dateFormat(new Date(feedbackItem?.createdAt), BUSINESS_DATE_FORMAT)}`
|
||||
)}
|
||||
</Text>
|
||||
{feedbackTypeIcon[feedbackItem.type]}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
{feedbackItem?.type === 'FIELD_VISIT' ? (
|
||||
<View style={styles.ShareButton}>
|
||||
<TouchableOpacity
|
||||
hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }}
|
||||
activeOpacity={0.7}
|
||||
onPress={(e) => {
|
||||
e.stopPropagation();
|
||||
throttledSendToWhatsapp.current(
|
||||
feedbackItem,
|
||||
caseDetails,
|
||||
agentId,
|
||||
setIsWhatsappSendLoading
|
||||
);
|
||||
}}
|
||||
disabled={isWhastappSendLoading}
|
||||
>
|
||||
<IconLabel
|
||||
text="Share"
|
||||
icon={<WhatsAppFeedbackShareIcon width={15} height={15} />}
|
||||
textStyle={styles.ButtonText}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.feedBox}>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
style={[
|
||||
styles.textHeading,
|
||||
feedbackItem.type === FEEDBACK_TYPE.GEN_AI_BOT_FIELD ? styles.capitalized : null,
|
||||
]}
|
||||
>
|
||||
{sanitizeString(feedbackItem.interactionStatus)}
|
||||
</Text>
|
||||
{feedbackItem?.createdAt ? (
|
||||
<Text style={styles.subText}>
|
||||
{sanitizeString(
|
||||
`${dateFormat(new Date(feedbackItem?.createdAt), BUSINESS_DATE_FORMAT)}`
|
||||
)}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
{!(feedbackItem.isSynced === false || !(feedbackItem as IFeedback)?.referenceId) ? (
|
||||
<View style={GenericStyles.alignCenter}>
|
||||
<RightChevronIcon />
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
{!(feedbackItem.isSynced === false) ? <Chevron /> : null}
|
||||
{feedbackItem?.type === FEEDBACK_TYPE.FIELD_VISIT ? (
|
||||
<TouchableOpacity
|
||||
style={styles.ShareButton}
|
||||
hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }}
|
||||
activeOpacity={0.7}
|
||||
onPress={(e) => {
|
||||
e.stopPropagation();
|
||||
throttledSendToWhatsapp.current(
|
||||
feedbackItem,
|
||||
caseDetails,
|
||||
agentId,
|
||||
setIsWhatsappSendLoading
|
||||
);
|
||||
}}
|
||||
disabled={isWhastappSendLoading}
|
||||
>
|
||||
<IconLabel
|
||||
text="Share"
|
||||
icon={<WhatsAppFeedbackShareIcon width={15} height={15} />}
|
||||
textStyle={styles.ButtonText}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
) : null}
|
||||
</TouchableOpacity>
|
||||
{showHorizontalLine ? <View style={[GenericStyles.borderTop, GenericStyles.w100]} /> : null}
|
||||
</View>
|
||||
@@ -129,9 +148,9 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
|
||||
const styles = StyleSheet.create({
|
||||
textHeading: {
|
||||
fontSize: 14,
|
||||
lineHeight: 30,
|
||||
paddingTop: 4,
|
||||
color: COLORS.TEXT.DARK,
|
||||
lineHeight: 18,
|
||||
marginBottom: 2,
|
||||
color: COLORS.TEXT.BLACK,
|
||||
},
|
||||
capitalized: {
|
||||
textTransform: 'capitalize',
|
||||
@@ -142,18 +161,13 @@ const styles = StyleSheet.create({
|
||||
color: COLORS.TEXT.LIGHT,
|
||||
},
|
||||
feedBox: {
|
||||
flexBasis: '94%',
|
||||
},
|
||||
textBox: {
|
||||
marginLeft: 10,
|
||||
marginTop: 5,
|
||||
flexBasis: '80%',
|
||||
marginRight: 14,
|
||||
},
|
||||
ShareButton: {
|
||||
width: 75,
|
||||
height: 35,
|
||||
padding: 5,
|
||||
justifyContent: 'flex-start',
|
||||
marginLeft: 5,
|
||||
marginTop: 5,
|
||||
marginLeft: 36,
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
ButtonText: {
|
||||
color: COLORS.BASE.BLUE,
|
||||
@@ -161,6 +175,29 @@ const styles = StyleSheet.create({
|
||||
ml_4: {
|
||||
marginLeft: -4,
|
||||
},
|
||||
feedbackIcon: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
backgroundColor: COLORS.BACKGROUND.SILVER,
|
||||
borderRadius: 4,
|
||||
},
|
||||
feedbackItem: {
|
||||
paddingTop: 16,
|
||||
paddingHorizontal: 0,
|
||||
},
|
||||
topFeedbackItem: {
|
||||
paddingTop: 40,
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
currentMonthFeedback: {
|
||||
borderRadius: 0,
|
||||
borderBottomRightRadius: 8,
|
||||
},
|
||||
lastMonthfeedback: {
|
||||
borderRadius: 0,
|
||||
borderBottomRightRadius: 8,
|
||||
borderTopLeftRadius: 7,
|
||||
},
|
||||
});
|
||||
|
||||
export default FeedbackListItem;
|
||||
|
||||
60
src/screens/caseDetails/feedback/FeedbackLoading.tsx
Normal file
60
src/screens/caseDetails/feedback/FeedbackLoading.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import Tag, { TagVariant } from '@rn-ui-lib/components/Tag';
|
||||
import LineLoader from '@rn-ui-lib/components/suspense_loader/LineLoader';
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { IFeedbackLoading, TOP_FEEDBACK_MAP } from './types';
|
||||
|
||||
const FeedbackLoading = (props: IFeedbackLoading) => {
|
||||
const { isTopFeedbackItem = false, arrayLength = 5 } = props;
|
||||
return (
|
||||
<View>
|
||||
{Array.from({ length: arrayLength }).map((_, idx) => (
|
||||
<View style={[!isTopFeedbackItem ? GenericStyles.ph16 : {}]}>
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.pv16,
|
||||
isTopFeedbackItem ? styles.topFeedbackItem : styles.feedbackItem,
|
||||
]}
|
||||
>
|
||||
{isTopFeedbackItem ? (
|
||||
<View style={[GenericStyles.absolute]}>
|
||||
<Tag
|
||||
text={TOP_FEEDBACK_MAP[idx]}
|
||||
variant={TagVariant.lightBlue}
|
||||
style={idx === 0 ? styles.lastMonthfeedback : styles.currentMonthFeedback}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
<LineLoader key={idx} width={'100%'} height={36} />
|
||||
</View>
|
||||
{idx !== arrayLength ? (
|
||||
<View style={[GenericStyles.borderTop, GenericStyles.w100]} />
|
||||
) : null}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
currentMonthFeedback: {
|
||||
borderRadius: 0,
|
||||
borderBottomRightRadius: 8,
|
||||
},
|
||||
lastMonthfeedback: {
|
||||
borderRadius: 0,
|
||||
borderBottomRightRadius: 8,
|
||||
borderTopLeftRadius: 7,
|
||||
},
|
||||
feedbackItem: {
|
||||
paddingTop: 16,
|
||||
paddingHorizontal: 0,
|
||||
},
|
||||
topFeedbackItem: {
|
||||
paddingTop: 38,
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
});
|
||||
|
||||
export default FeedbackLoading;
|
||||
90
src/screens/caseDetails/feedback/TopFeedbacks.tsx
Normal file
90
src/screens/caseDetails/feedback/TopFeedbacks.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import FeedbackListItem from './FeedbackListItem';
|
||||
import { useAppDispatch, useAppSelector } from '@hooks';
|
||||
import { RootState } from '@store';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { GenericStyles, getShadowStyle } from '@rn-ui-lib/styles';
|
||||
import Text from '@rn-ui-lib/components/Text';
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
import SuspenseLoader from '@rn-ui-lib/components/suspense_loader/SuspenseLoader';
|
||||
import FeedbackLoading from './FeedbackLoading';
|
||||
import { getTopFeedbacks } from '@actions/feedbackActions';
|
||||
|
||||
interface ITopFeedbacks {
|
||||
caseId: string;
|
||||
}
|
||||
|
||||
const TopFeedbacks = (props: ITopFeedbacks) => {
|
||||
const { caseId } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
const caseDetail = useAppSelector((state: RootState) => state.allCases.caseDetails[caseId]) || {};
|
||||
const { loanAccountNumber } = caseDetail || {};
|
||||
const feedbackList = useAppSelector(
|
||||
(state: RootState) => state.topFeedbacks?.[loanAccountNumber as string]?.feedbacks || []
|
||||
);
|
||||
const isLoading = useAppSelector(
|
||||
(state: RootState) => state.topFeedbacks?.[loanAccountNumber as string]?.isLoading || false
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getTopFeedbacks(loanAccountNumber));
|
||||
}, []);
|
||||
|
||||
if (!feedbackList?.length) return null;
|
||||
|
||||
return (
|
||||
<View style={GenericStyles.pb8}>
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
GenericStyles.alignCenter,
|
||||
GenericStyles.justifyContentCenter,
|
||||
GenericStyles.pb16,
|
||||
GenericStyles.pt24,
|
||||
]}
|
||||
>
|
||||
<View style={[GenericStyles.borderTop, GenericStyles.flex35]} />
|
||||
<Text
|
||||
dark
|
||||
style={[
|
||||
GenericStyles.flex30,
|
||||
GenericStyles.centerAlignedText,
|
||||
GenericStyles.fontSize16,
|
||||
GenericStyles.fw600,
|
||||
styles.feedbackHeading,
|
||||
]}
|
||||
>
|
||||
Feedbacks
|
||||
</Text>
|
||||
<View style={[GenericStyles.borderTop, GenericStyles.flex35]} />
|
||||
</View>
|
||||
<View style={[GenericStyles.whiteBackground, GenericStyles.br8, getShadowStyle(2)]}>
|
||||
<SuspenseLoader
|
||||
loading={isLoading && !feedbackList?.length}
|
||||
fallBack={<FeedbackLoading arrayLength={2} isTopFeedbackItem />}
|
||||
>
|
||||
{feedbackList?.map((feedbackItem: any, idx: number) => (
|
||||
<FeedbackListItem
|
||||
key={feedbackItem.createdAt}
|
||||
feedbackItem={feedbackItem}
|
||||
loanAccountNumber={loanAccountNumber}
|
||||
showHorizontalLine={++idx !== feedbackList?.length}
|
||||
caseId={caseId}
|
||||
isTopFeedbackItem
|
||||
/>
|
||||
))}
|
||||
</SuspenseLoader>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
feedbackHeading: { color: COLORS.TEXT.BLACK },
|
||||
border: {
|
||||
borderWidth: 1,
|
||||
borderColor: COLORS.BORDER.PRIMARY,
|
||||
},
|
||||
});
|
||||
|
||||
export default TopFeedbacks;
|
||||
9
src/screens/caseDetails/feedback/types.ts
Normal file
9
src/screens/caseDetails/feedback/types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export const TOP_FEEDBACK_MAP: { [key: number]: string } = {
|
||||
0: 'Current month status',
|
||||
1: 'Last month status',
|
||||
};
|
||||
|
||||
export interface IFeedbackLoading {
|
||||
isTopFeedbackItem?: boolean;
|
||||
arrayLength?: number;
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import commitmentTrackerSlice from '@reducers/commitmentTrackerSlice';
|
||||
import nearbyCasesSlice from '@reducers/nearbyCasesSlice';
|
||||
import activeCallSlice from '@reducers/activeCallSlice';
|
||||
import documentsSlice from '@reducers/documentsSlice';
|
||||
import topFeedbacksSlice from '@reducers/topFeedbacksSlice';
|
||||
import escalationSlice from '@reducers/escalationSlice';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
@@ -67,6 +68,7 @@ const rootReducer = combineReducers({
|
||||
nearbyCasesSlice: nearbyCasesSlice,
|
||||
activeCall: activeCallSlice,
|
||||
documentsSlice: documentsSlice,
|
||||
topFeedbacks: topFeedbacksSlice,
|
||||
escalationSlice: escalationSlice,
|
||||
});
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ export enum FEEDBACK_TYPE {
|
||||
CALL_BRIDGE = 'CALL_BRIDGE',
|
||||
SELF_CALL = 'SELF_CALL',
|
||||
GEN_AI_BOT_FIELD = 'GEN_AI_BOT_FIELD',
|
||||
NOT_ATTEMPTED = 'NOT_ATTEMPTED'
|
||||
}
|
||||
|
||||
export interface FIELD_FEEDBACK_METADATA {
|
||||
@@ -75,6 +76,8 @@ export interface IFeedback {
|
||||
agentReferenceId: string;
|
||||
source: Address | IGeolocation | ICallingFeedback;
|
||||
interactionStatus: InteractionStatuses;
|
||||
tagTitle?: string;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export interface IUnSyncedFeedbackItem {
|
||||
|
||||
Reference in New Issue
Block a user