Merge branch 'master' of github.com:navi-medici/address-verification-app into WhatsappFeedbackEnhancment
This commit is contained in:
Submodule RN-UI-LIB updated: 64db024f2d...11fc56fcf6
@@ -131,8 +131,8 @@ def reactNativeArchitectures() {
|
||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||
}
|
||||
|
||||
def VERSION_CODE = 96
|
||||
def VERSION_NAME = "2.5.1"
|
||||
def VERSION_CODE = 97
|
||||
def VERSION_NAME = "2.5.2"
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "AV_APP",
|
||||
"version": "2.5.1",
|
||||
"version": "2.5.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android:dev": "yarn move:dev && react-native run-android",
|
||||
|
||||
@@ -20,6 +20,7 @@ import { AppDispatch } from '../store/store';
|
||||
export const getPerformanceMetrics =
|
||||
(
|
||||
caseDetailsIds: Array<string>,
|
||||
isExternalAgent: boolean,
|
||||
setIsLoading: (isLoading: boolean) => void,
|
||||
callbackFn?: () => void
|
||||
) =>
|
||||
@@ -37,11 +38,35 @@ export const getPerformanceMetrics =
|
||||
atLeastOneEmiCollectedCases,
|
||||
allCases,
|
||||
} = response?.data?.casesSplit || {};
|
||||
const { unVisitedCount, nonContactableCount, nonPtpCount, brokenPtpCount } =
|
||||
getFiltersCount(caseDetailsIds, response?.data?.casesSplit);
|
||||
|
||||
if (isExternalAgent) {
|
||||
dispatch(
|
||||
setPerformanceData({
|
||||
cases: {
|
||||
visitedCases,
|
||||
unvisitedCases: unVisitedCount,
|
||||
contactableCases,
|
||||
nonContactableCases: nonContactableCount,
|
||||
totalPtp: ptpCases,
|
||||
nonPtp: nonPtpCount,
|
||||
convertedPtp: convertedPtpCases,
|
||||
brokenPtp: brokenPtpCount,
|
||||
totalEmi: allCases ?? 0,
|
||||
atleastOneEmiCollected: atLeastOneEmiCollectedCases,
|
||||
},
|
||||
lastUpdatedAt: response?.data?.lastUpdatedAt,
|
||||
totalCashCollected: response?.data?.totalCashCollected ?? 0,
|
||||
feedbackDetails: response?.data?.feedbackSplitView,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const { totalLevel, currentLevel } = response?.data?.performanceLevel || {};
|
||||
const { tillDate, progression } = response?.data?.cashCollectedComparison || {};
|
||||
const graphData = getGraphFormattedData(progression);
|
||||
const { unVisitedCount, nonContactableCount, nonPtpCount, brokenPtpCount } =
|
||||
getFiltersCount(caseDetailsIds, response?.data?.casesSplit);
|
||||
const totalLevelInNumber = Number(totalLevel);
|
||||
const currentLevelInNumber = Number(currentLevel);
|
||||
|
||||
|
||||
34
src/assets/icons/SuspiciousFeedbackIcon.tsx
Normal file
34
src/assets/icons/SuspiciousFeedbackIcon.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
import React from 'react';
|
||||
import Svg, { Rect, G, Path, Mask } from 'react-native-svg';
|
||||
|
||||
interface SuspiciousFeedbackIconProps {
|
||||
size?: number;
|
||||
fillColor?: string;
|
||||
}
|
||||
|
||||
const SuspiciousFeedbackIcon = ({
|
||||
size = 20,
|
||||
fillColor = COLORS.BACKGROUND.RED,
|
||||
}: SuspiciousFeedbackIconProps) => {
|
||||
return (
|
||||
<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} fill="none">
|
||||
<Rect width={size} height={size} rx={4} fill={fillColor} />
|
||||
<Mask id="mask0_7693_106251" maskUnits="userSpaceOnUse" x={2} y={2} width={16} height={16}>
|
||||
<Rect x={2} y={2} width={16} height={16} fill="#D9D9D9" />
|
||||
</Mask>
|
||||
<G mask="url(#mask0_7693_106251)">
|
||||
<Path
|
||||
d="M5.99992 14.4967L4.46659 16.0301C4.25547 16.2412 4.01381 16.2884 3.74159 16.1717C3.46936 16.0551 3.33325 15.8467 3.33325 15.5467V5.16341C3.33325 4.79674 3.46381 4.48286 3.72492 4.22174C3.98603 3.96063 4.29992 3.83008 4.66659 3.83008H15.3333C15.6999 3.83008 16.0138 3.96063 16.2749 4.22174C16.536 4.48286 16.6666 4.79674 16.6666 5.16341V13.1634C16.6666 13.5301 16.536 13.844 16.2749 14.1051C16.0138 14.3662 15.6999 14.4967 15.3333 14.4967H5.99992Z"
|
||||
fill="#E92C2C"
|
||||
/>
|
||||
<Path
|
||||
d="M10.2241 5.50195C9.87298 5.50195 9.59034 5.79043 9.59753 6.14151L9.67453 9.90445C9.68065 10.2036 9.92491 10.4429 10.2241 10.4429C10.5234 10.4429 10.7676 10.2036 10.7737 9.90445L10.8507 6.14151C10.8579 5.79043 10.5753 5.50195 10.2241 5.50195ZM10.2222 12.502C10.4374 12.502 10.6214 12.4365 10.7744 12.3055C10.9273 12.1723 11.0025 12.012 10.9999 11.8246C11.0025 11.6395 10.9273 11.4815 10.7744 11.3505C10.6214 11.2173 10.4374 11.1507 10.2222 11.1507C10.0122 11.1507 9.83072 11.2173 9.67777 11.3505C9.52481 11.4815 9.44704 11.6395 9.44444 11.8246C9.44704 11.9488 9.48463 12.0628 9.55722 12.1667C9.62721 12.2683 9.72054 12.3496 9.83721 12.4105C9.95387 12.4715 10.0822 12.502 10.2222 12.502Z"
|
||||
fill="white"
|
||||
/>
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default SuspiciousFeedbackIcon;
|
||||
@@ -612,7 +612,7 @@ export const CLICKSTREAM_EVENT_NAMES = {
|
||||
},
|
||||
FA_PERFORMANCE_DASHBOARD_UNVISITED_CASES_LOAD: {
|
||||
name: 'FA_PERFORMANCE_DASHBOARD_UNVISITED_CASES_LOAD',
|
||||
description: 'Performance Dashboard Button Clicked',
|
||||
description: 'Performance Dashboard Unvisited Cases Load',
|
||||
},
|
||||
FA_PERFORMANCE_DASHBOARD_NON_CONTACTABLE_CASES_CLICKED: {
|
||||
name: 'FA_PERFORMANCE_DASHBOARD_NON_CONTACTABLE_CASES_CLICKED',
|
||||
|
||||
@@ -38,6 +38,14 @@ const initialState: AgentPerformanceInterface = {
|
||||
},
|
||||
cashCollectedGraphData: [],
|
||||
lastUpdatedAt: 0,
|
||||
feedbackDetails: {
|
||||
totalFeedbackCount: 0,
|
||||
todaysFeedbackCount: 0,
|
||||
todaysGenuineFeedbackCount: 0,
|
||||
todaysSuspiciousFeedbackCount: 0,
|
||||
totalSuspiciousFeedbackCount: 0,
|
||||
totalGenuineFeedbackCount: 0,
|
||||
},
|
||||
},
|
||||
cashCollectedData: {
|
||||
data: [],
|
||||
|
||||
@@ -5,6 +5,7 @@ import { SCREEN_ANIMATION_DURATION } from '../../common/Constants';
|
||||
import { PageRouteEnum } from '../auth/ProtectedRouter';
|
||||
import CashCollected from '../cashCollected';
|
||||
import FilteredCases from '../filteredCases';
|
||||
import { Animation } from './interface';
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
|
||||
@@ -27,8 +28,24 @@ function DashBoardScreens() {
|
||||
animationDuration: SCREEN_ANIMATION_DURATION,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen name={PageRouteEnum.CASH_COLLECTED} component={CashCollected} />
|
||||
<Stack.Screen name={PageRouteEnum.FILTERED_CASES} component={FilteredCases} />
|
||||
<Stack.Screen
|
||||
name={PageRouteEnum.CASH_COLLECTED}
|
||||
component={CashCollected}
|
||||
options={{
|
||||
header: () => null,
|
||||
animationDuration: SCREEN_ANIMATION_DURATION,
|
||||
animation: Animation.SLIDE_FROM_RIGHT,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name={PageRouteEnum.FILTERED_CASES}
|
||||
component={FilteredCases}
|
||||
options={{
|
||||
header: () => null,
|
||||
animationDuration: SCREEN_ANIMATION_DURATION,
|
||||
animation: Animation.SLIDE_FROM_RIGHT,
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
34
src/screens/Dashboard/ExternalAgentDashboard.tsx
Normal file
34
src/screens/Dashboard/ExternalAgentDashboard.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { useAppSelector } from '@hooks';
|
||||
import React from 'react';
|
||||
import ExternalAgentPerformanceCard from './ExternalAgentPerformanceCard';
|
||||
import PerformanceOverview from './PerformanceOverview';
|
||||
|
||||
const ExternalAgentDashboard = () => {
|
||||
const performanceData = useAppSelector((state) => state.agentPerformance.performanceData);
|
||||
const { totalCashCollected = 0, cases, feedbackDetails } = performanceData || {};
|
||||
const { totalFeedbackCount = 0, totalSuspiciousFeedbackCount = 0 } = feedbackDetails || {};
|
||||
|
||||
let suspiciousFeedback = 0;
|
||||
|
||||
if (totalFeedbackCount && totalSuspiciousFeedbackCount) {
|
||||
suspiciousFeedback = (totalSuspiciousFeedbackCount * 100) / totalFeedbackCount;
|
||||
}
|
||||
|
||||
const externalAgentPerformanceOverview = {
|
||||
totalCashCollected,
|
||||
atleastOneEmiCollected: cases?.atleastOneEmiCollected,
|
||||
totalEmi: cases?.totalEmi,
|
||||
suspiciousFeedback,
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<PerformanceOverview
|
||||
isExternalAgent
|
||||
performanceOverviewData={externalAgentPerformanceOverview}
|
||||
/>
|
||||
<ExternalAgentPerformanceCard />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExternalAgentDashboard;
|
||||
111
src/screens/Dashboard/ExternalAgentPerformanceCard.tsx
Normal file
111
src/screens/Dashboard/ExternalAgentPerformanceCard.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import { navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import { useAppDispatch, useAppSelector } from '../../hooks';
|
||||
import { setFilteredListToast } from '../../reducer/allCasesSlice';
|
||||
import { setSelectedAgentDashboardFilter } from '../../reducer/filtersSlice';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import { PageRouteEnum } from '../auth/ProtectedRouter';
|
||||
import PerformanceCardV1 from './PerformanceCardV1';
|
||||
import PerformanceCardV2 from './PerformanceCardV2';
|
||||
import {
|
||||
CurrentAllocationStatsFilterMap,
|
||||
CurrentAllocationStatsMap,
|
||||
PerformanceDetailsType,
|
||||
} from './interface';
|
||||
import {
|
||||
getClickedEventName,
|
||||
getExternalAgentPerformanceDetails,
|
||||
getPerformanceDetailFilter,
|
||||
} from './utils';
|
||||
import { COMMON_FILTER } from './constants';
|
||||
|
||||
const ExternalAgentPerformanceCard = () => {
|
||||
const { performanceData, filters } = useAppSelector((state) => ({
|
||||
performanceData: state?.agentPerformance?.performanceData,
|
||||
filters: state?.filters?.filters,
|
||||
}));
|
||||
const { cases, feedbackDetails } = performanceData || {};
|
||||
const performanceDetails = getExternalAgentPerformanceDetails(cases, feedbackDetails);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleActionItemClick = (item: PerformanceDetailsType) => {
|
||||
if (item?.redirectionType) {
|
||||
addClickstreamEvent(getClickedEventName(item.redirectionType), {});
|
||||
dispatch(
|
||||
setSelectedAgentDashboardFilter(
|
||||
getPerformanceDetailFilter(
|
||||
item.redirectionType,
|
||||
Object.keys(filters?.[COMMON_FILTER]?.filters ?? {})?.includes(
|
||||
CurrentAllocationStatsFilterMap[item.redirectionType]
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
dispatch(
|
||||
setFilteredListToast({
|
||||
showToast: true,
|
||||
caseType: CurrentAllocationStatsMap[item.redirectionType],
|
||||
})
|
||||
);
|
||||
navigateToScreen(PageRouteEnum.FILTERED_CASES);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{performanceDetails?.map((item) =>
|
||||
item?.v1 ? (
|
||||
<PerformanceCardV1 item={item} handleActionItemClick={handleActionItemClick} />
|
||||
) : (
|
||||
<PerformanceCardV2 item={item} handleActionItemClick={handleActionItemClick} />
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
...GenericStyles.row,
|
||||
...GenericStyles.plr16,
|
||||
...GenericStyles.mb8,
|
||||
...GenericStyles.mt16,
|
||||
},
|
||||
title: {
|
||||
fontSize: 13,
|
||||
marginLeft: 4,
|
||||
marginBottom: 4,
|
||||
},
|
||||
totalCount: {
|
||||
color: COLORS.TEXT.DARK,
|
||||
fontSize: 36,
|
||||
fontWeight: '600',
|
||||
paddingTop: 16,
|
||||
},
|
||||
rightIcon: {
|
||||
marginTop: 2,
|
||||
},
|
||||
subTitle: {
|
||||
color: COLORS.BASE.BLUE,
|
||||
},
|
||||
fw700: {
|
||||
fontWeight: '700',
|
||||
},
|
||||
pressableCard: {
|
||||
backgroundColor: COLORS.BACKGROUND.PRIMARY,
|
||||
width: '47%',
|
||||
...GenericStyles.mb16,
|
||||
...GenericStyles.pt12,
|
||||
...GenericStyles.ph12,
|
||||
...getShadowStyle(2),
|
||||
borderRadius: 6,
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
export default ExternalAgentPerformanceCard;
|
||||
26
src/screens/Dashboard/InternalAgentDashboard.tsx
Normal file
26
src/screens/Dashboard/InternalAgentDashboard.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { useAppSelector } from '@hooks';
|
||||
import React from 'react';
|
||||
import InternalAgentPerformanceCard from './InternalAgentPerformanceCard';
|
||||
import PerformanceMeter from './PerformanceMeter';
|
||||
import PerformanceOverview from './PerformanceOverview';
|
||||
|
||||
const InternalAgentDashboard = () => {
|
||||
const performanceData = useAppSelector((state) => state.agentPerformance.performanceData);
|
||||
const { totalCashCollected = 0, cases } = performanceData || {};
|
||||
|
||||
const internalAgentPerformanceOverview = {
|
||||
totalCashCollected,
|
||||
atleastOneEmiCollected: cases?.atleastOneEmiCollected,
|
||||
totalEmi: cases?.totalEmi,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PerformanceOverview performanceOverviewData={internalAgentPerformanceOverview} />
|
||||
<PerformanceMeter />
|
||||
<InternalAgentPerformanceCard />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InternalAgentDashboard;
|
||||
68
src/screens/Dashboard/InternalAgentPerformanceCard.tsx
Normal file
68
src/screens/Dashboard/InternalAgentPerformanceCard.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
|
||||
import { navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import { useAppDispatch, useAppSelector } from '../../hooks';
|
||||
import { setFilteredListToast } from '../../reducer/allCasesSlice';
|
||||
import { setSelectedAgentDashboardFilter } from '../../reducer/filtersSlice';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import { PageRouteEnum } from '../auth/ProtectedRouter';
|
||||
import PerformanceCardV1 from './PerformanceCardV1';
|
||||
import {
|
||||
CurrentAllocationStatsFilterMap,
|
||||
CurrentAllocationStatsMap,
|
||||
PerformanceDetailsType,
|
||||
} from './interface';
|
||||
import { getClickedEventName, getPerformanceDetailFilter, getPerformanceDetails } from './utils';
|
||||
|
||||
const InternalAgentPerformanceCard = () => {
|
||||
const { performanceData, filters } = useAppSelector((state) => ({
|
||||
performanceData: state.agentPerformance.performanceData,
|
||||
filters: state.filters.filters,
|
||||
}));
|
||||
const { cases } = performanceData || {};
|
||||
const performanceDetails = getPerformanceDetails(cases);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleActionItemClick = (item: PerformanceDetailsType) => {
|
||||
dispatch(
|
||||
setSelectedAgentDashboardFilter(
|
||||
getPerformanceDetailFilter(
|
||||
item.redirectionType,
|
||||
Object.keys(filters?.['COMMON']?.filters ?? {})?.includes(
|
||||
CurrentAllocationStatsFilterMap[item.redirectionType]
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
dispatch(
|
||||
setFilteredListToast({
|
||||
showToast: true,
|
||||
caseType: CurrentAllocationStatsMap[item.redirectionType],
|
||||
})
|
||||
);
|
||||
navigateToScreen(PageRouteEnum.FILTERED_CASES);
|
||||
addClickstreamEvent(getClickedEventName(item.redirectionType), {});
|
||||
};
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{performanceDetails?.map((item) => (
|
||||
<PerformanceCardV1 item={item} handleActionItemClick={handleActionItemClick} />
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
...GenericStyles.row,
|
||||
...GenericStyles.plr16,
|
||||
...GenericStyles.mb8,
|
||||
...GenericStyles.mt16,
|
||||
},
|
||||
});
|
||||
|
||||
export default InternalAgentPerformanceCard;
|
||||
@@ -1,123 +0,0 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import Heading from '../../../RN-UI-LIB/src/components/Heading';
|
||||
import Text from '../../../RN-UI-LIB/src/components/Text';
|
||||
import Chevron from '../../../RN-UI-LIB/src/Icons/Chevron';
|
||||
import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import { navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import { useAppDispatch, useAppSelector } from '../../hooks';
|
||||
import { setFilteredListToast } from '../../reducer/allCasesSlice';
|
||||
import { setSelectedAgentDashboardFilter } from '../../reducer/filtersSlice';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import { PageRouteEnum } from '../auth/ProtectedRouter';
|
||||
import {
|
||||
CurrentAllocationStatsFilterMap,
|
||||
CurrentAllocationStatsMap,
|
||||
PerformanceDetailsType,
|
||||
} from './interface';
|
||||
import { getClickedEventName, getPerformanceDetailFilter, getPerformanceDetails } from './utils';
|
||||
|
||||
const PerformanceCard = () => {
|
||||
const { performanceData, filters } = useAppSelector((state) => ({
|
||||
performanceData: state.agentPerformance.performanceData,
|
||||
filters: state.filters.filters,
|
||||
}));
|
||||
const { cases } = performanceData || {};
|
||||
const performanceDetails = getPerformanceDetails(cases);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleActionItemClick = (item: PerformanceDetailsType) => {
|
||||
dispatch(
|
||||
setSelectedAgentDashboardFilter(
|
||||
getPerformanceDetailFilter(
|
||||
item.redirectionType,
|
||||
Object.keys(filters?.['COMMON']?.filters ?? {})?.includes(
|
||||
CurrentAllocationStatsFilterMap[item.redirectionType]
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
dispatch(
|
||||
setFilteredListToast({
|
||||
showToast: true,
|
||||
caseType: CurrentAllocationStatsMap[item.redirectionType],
|
||||
})
|
||||
);
|
||||
navigateToScreen(PageRouteEnum.FILTERED_CASES);
|
||||
addClickstreamEvent(getClickedEventName(item.redirectionType), {});
|
||||
};
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{performanceDetails.map((item) => (
|
||||
<View key={item.convertedText} style={styles.pressableCard}>
|
||||
<Heading style={styles.totalCount}>{item.totalConverted}</Heading>
|
||||
<Text style={styles.title}>{item.convertedText}</Text>
|
||||
<TouchableOpacity onPress={() => handleActionItemClick(item)} activeOpacity={0.8}>
|
||||
<View style={[GenericStyles.borderTop, GenericStyles.w100]} />
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
GenericStyles.alignCenter,
|
||||
GenericStyles.spaceBetween,
|
||||
GenericStyles.pv12,
|
||||
]}
|
||||
>
|
||||
<View style={GenericStyles.row}>
|
||||
<Text style={[styles.subTitle, styles.fw700]}>{item.totalNotConverted} </Text>
|
||||
<Text style={styles.subTitle}>{item.notConvertedText}</Text>
|
||||
</View>
|
||||
<View style={styles.rightIcon}>
|
||||
<Chevron fillColor={COLORS.TEXT.BLUE} />
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
...GenericStyles.row,
|
||||
...GenericStyles.plr16,
|
||||
...GenericStyles.mb8,
|
||||
...GenericStyles.mt16,
|
||||
},
|
||||
title: {
|
||||
fontSize: 13,
|
||||
marginTop: 8,
|
||||
marginBottom: 8,
|
||||
},
|
||||
totalCount: {
|
||||
color: COLORS.TEXT.DARK,
|
||||
fontSize: 36,
|
||||
fontWeight: '600',
|
||||
paddingTop: 16,
|
||||
},
|
||||
rightIcon: {
|
||||
marginTop: 2,
|
||||
},
|
||||
subTitle: {
|
||||
color: COLORS.BASE.BLUE,
|
||||
},
|
||||
fw700: {
|
||||
fontWeight: '700',
|
||||
},
|
||||
pressableCard: {
|
||||
backgroundColor: COLORS.BACKGROUND.PRIMARY,
|
||||
width: '47%',
|
||||
...GenericStyles.mb16,
|
||||
...GenericStyles.pt12,
|
||||
...GenericStyles.ph12,
|
||||
...getShadowStyle(2),
|
||||
borderRadius: 6,
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
export default PerformanceCard;
|
||||
80
src/screens/Dashboard/PerformanceCardV1.tsx
Normal file
80
src/screens/Dashboard/PerformanceCardV1.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
import Heading from '@rn-ui-lib/components/Heading';
|
||||
import Text from '@rn-ui-lib/components/Text';
|
||||
import Chevron from '@rn-ui-lib/icons/Chevron';
|
||||
import { GenericStyles, getShadowStyle } from '@rn-ui-lib/styles';
|
||||
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { PerformanceCardProps } from './interface';
|
||||
|
||||
const PerformanceCardV1 = (props: PerformanceCardProps) => {
|
||||
const { item, handleActionItemClick } = props;
|
||||
return (
|
||||
<View key={item.convertedText} style={cardStyles.pressableCard}>
|
||||
<Heading style={cardStyles.totalCount}>{item.totalConverted}</Heading>
|
||||
<Text style={cardStyles.title}>{item.convertedText}</Text>
|
||||
<TouchableOpacity onPress={() => handleActionItemClick(item)} activeOpacity={0.8}>
|
||||
<View style={[GenericStyles.borderTop, GenericStyles.w100]} />
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
GenericStyles.alignCenter,
|
||||
GenericStyles.spaceBetween,
|
||||
GenericStyles.pv12,
|
||||
]}
|
||||
>
|
||||
<View style={GenericStyles.row}>
|
||||
<Text style={[cardStyles.subTitle, cardStyles.fw700]}>{item.totalNotConverted} </Text>
|
||||
<Text style={cardStyles.subTitle}>{item.notConvertedText}</Text>
|
||||
</View>
|
||||
<View style={cardStyles.rightIcon}>
|
||||
<Chevron fillColor={COLORS.TEXT.BLUE} />
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export const cardStyles = StyleSheet.create({
|
||||
container: {
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
...GenericStyles.row,
|
||||
...GenericStyles.plr16,
|
||||
...GenericStyles.mb8,
|
||||
...GenericStyles.mt16,
|
||||
},
|
||||
title: {
|
||||
fontSize: 13,
|
||||
marginTop: 8,
|
||||
marginBottom: 8,
|
||||
},
|
||||
totalCount: {
|
||||
color: COLORS.TEXT.DARK,
|
||||
fontSize: 36,
|
||||
fontWeight: '600',
|
||||
paddingTop: 16,
|
||||
},
|
||||
rightIcon: {
|
||||
marginTop: 2,
|
||||
},
|
||||
subTitle: {
|
||||
color: COLORS.BASE.BLUE,
|
||||
},
|
||||
fw700: {
|
||||
fontWeight: '700',
|
||||
},
|
||||
pressableCard: {
|
||||
backgroundColor: COLORS.BACKGROUND.PRIMARY,
|
||||
width: '47%',
|
||||
...GenericStyles.mb16,
|
||||
...GenericStyles.pt12,
|
||||
...GenericStyles.ph12,
|
||||
...getShadowStyle(2),
|
||||
borderRadius: 6,
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
export default PerformanceCardV1;
|
||||
103
src/screens/Dashboard/PerformanceCardV2.tsx
Normal file
103
src/screens/Dashboard/PerformanceCardV2.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
import Heading from '@rn-ui-lib/components/Heading';
|
||||
import Text from '@rn-ui-lib/components/Text';
|
||||
import Chevron from '@rn-ui-lib/icons/Chevron';
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
import React from 'react';
|
||||
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { PerformanceCardProps } from './interface';
|
||||
import { cardStyles } from './PerformanceCardV1';
|
||||
|
||||
const PerformanceCardV2 = (props: PerformanceCardProps) => {
|
||||
const { item, handleActionItemClick } = props;
|
||||
return (
|
||||
<View key={item.convertedText} style={cardStyles.pressableCard}>
|
||||
<View style={[GenericStyles.row, GenericStyles.alignItemsFlexEnd, GenericStyles.pb4]}>
|
||||
<Heading style={cardStyles.totalCount}>{item.totalConverted}</Heading>
|
||||
<Text style={styles.title}>{item.convertedText}</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[GenericStyles.borderTop, GenericStyles.w100, { borderColor: COLORS.BORDER.GREY }]}
|
||||
/>
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter, GenericStyles.pv6]}>
|
||||
<View
|
||||
style={[GenericStyles.br8, styles.totalNotConvertedContainer, styles.greenBackground]}
|
||||
/>
|
||||
<Text style={[cardStyles.fw700, GenericStyles.fontSize14, styles.darkText]}>
|
||||
{item.otherDetailsCount}{' '}
|
||||
</Text>
|
||||
<Text style={styles.notConvertedText}>{item.otherDetailsText}</Text>
|
||||
</View>
|
||||
{!item?.redirectionType && (
|
||||
<>
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.borderTop,
|
||||
GenericStyles.w100,
|
||||
{ borderColor: COLORS.BORDER.GREY },
|
||||
]}
|
||||
/>
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter, GenericStyles.pv6]}>
|
||||
<View style={[GenericStyles.br8, styles.totalNotConvertedContainer]} />
|
||||
<Text style={[cardStyles.fw700, GenericStyles.fontSize14, styles.darkText]}>
|
||||
{item.totalNotConverted}{' '}
|
||||
</Text>
|
||||
<Text style={styles.notConvertedText}>{item.notConvertedText}</Text>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
{item?.redirectionType && (
|
||||
<TouchableOpacity
|
||||
onPress={() => handleActionItemClick(item)}
|
||||
activeOpacity={0.8}
|
||||
hitSlop={{ left: 12, right: 12 }}
|
||||
>
|
||||
<View style={[GenericStyles.borderTop, GenericStyles.w100]} />
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
GenericStyles.alignCenter,
|
||||
GenericStyles.spaceBetween,
|
||||
GenericStyles.pv12,
|
||||
]}
|
||||
>
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
||||
<View style={[GenericStyles.br8, styles.totalNotConvertedContainer]} />
|
||||
<Text style={[cardStyles.subTitle, cardStyles.fw700]}>{item.totalNotConverted} </Text>
|
||||
<Text style={cardStyles.subTitle}>{item.notConvertedText}</Text>
|
||||
</View>
|
||||
<View style={cardStyles.rightIcon}>
|
||||
<Chevron fillColor={COLORS.TEXT.BLUE} />
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
title: {
|
||||
fontSize: 13,
|
||||
marginLeft: 4,
|
||||
marginBottom: 4,
|
||||
},
|
||||
totalNotConvertedContainer: {
|
||||
width: 2,
|
||||
height: 16,
|
||||
backgroundColor: COLORS.BORDER.DARK_RED,
|
||||
marginRight: 6,
|
||||
},
|
||||
notConvertedText: {
|
||||
fontSize: 12,
|
||||
color: COLORS.TEXT.BLACK,
|
||||
},
|
||||
greenBackground: {
|
||||
backgroundColor: COLORS.BORDER.DARK_GREEN,
|
||||
},
|
||||
darkText: {
|
||||
color: COLORS.TEXT.DARK,
|
||||
},
|
||||
});
|
||||
|
||||
export default PerformanceCardV2;
|
||||
@@ -28,6 +28,7 @@ const PerformanceItem = (props: PerformanceItemProps) => {
|
||||
GenericStyles.spaceBetween,
|
||||
styles.performanceContainer,
|
||||
]}
|
||||
hitSlop={{ left: 16, right: 16 }}
|
||||
onPress={handlePress}
|
||||
>
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
||||
@@ -41,7 +42,7 @@ const PerformanceItem = (props: PerformanceItemProps) => {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
performanceContainer: {
|
||||
padding: 14,
|
||||
paddingVertical: 14,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ const PerformanceLevelGraph = () => {
|
||||
const isBiggerBox = (box: Box) => box.id === performanceLevel?.currentLevel;
|
||||
|
||||
const selfLevelPosition =
|
||||
((cashCollectedTillDate.self - cashCollectedTillDate.belowLevel) * 100) /
|
||||
(cashCollectedTillDate.aboveLevel - cashCollectedTillDate.belowLevel + 1); // if denominator difference is 0 we are adding it to 1, to avoid calculation error, this should never happen i
|
||||
((cashCollectedTillDate?.self - cashCollectedTillDate?.belowLevel) * 100) /
|
||||
(cashCollectedTillDate?.aboveLevel - cashCollectedTillDate?.belowLevel + 1); // if denominator difference is 0 we are adding it to 1, to avoid calculation error, this should never happen i
|
||||
|
||||
const getBoxStyles = (box: Box) => {
|
||||
return {
|
||||
|
||||
@@ -9,12 +9,13 @@ import EmiCollectedIcon from '../../assets/icons/EmiCollectedIcon';
|
||||
import PerformanceItem from './PerformanceItem';
|
||||
import { PerformanceDetails } from './constants';
|
||||
import { PageRouteEnum } from '../auth/ProtectedRouter';
|
||||
import { useAppSelector } from '../../hooks';
|
||||
import { formatAmount } from '../../../RN-UI-LIB/src/utlis/amount';
|
||||
import SuspiciousFeedbackIcon from '@assets/icons/SuspiciousFeedbackIcon';
|
||||
import { PerformanceOverviewProps } from './interface';
|
||||
import { sanatizeSuspisiousFeedback } from './utils';
|
||||
|
||||
const PerformanceOverview = () => {
|
||||
const performanceData = useAppSelector((state) => state.agentPerformance.performanceData);
|
||||
const { totalCashCollected = 0, cases } = performanceData || {};
|
||||
const PerformanceOverview = (props: PerformanceOverviewProps) => {
|
||||
const { isExternalAgent, performanceOverviewData } = props;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
@@ -28,7 +29,7 @@ const PerformanceOverview = () => {
|
||||
numberOfLines={1}
|
||||
style={[styles.fw700, styles.leftContent, GenericStyles.fontSize16, styles.width150]}
|
||||
>
|
||||
{formatAmount(Number(totalCashCollected.toFixed(2)), false)}
|
||||
{formatAmount(Number(performanceOverviewData?.totalCashCollected?.toFixed(2)), false)}
|
||||
</Text>
|
||||
<View style={styles.rightIcon}>
|
||||
<Chevron fillColor={COLORS.TEXT.BLUE} />
|
||||
@@ -43,12 +44,30 @@ const PerformanceOverview = () => {
|
||||
rightSideView={
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
||||
<Text style={[GenericStyles.fontSize16, styles.fw700, styles.leftContent]}>
|
||||
{cases?.atleastOneEmiCollected}{' '}
|
||||
{performanceOverviewData?.atleastOneEmiCollected}{' '}
|
||||
</Text>
|
||||
<Text style={[styles.rightContent, GenericStyles.fontSize14]}>
|
||||
/ {performanceOverviewData?.totalEmi}
|
||||
</Text>
|
||||
<Text style={[styles.rightContent, GenericStyles.fontSize14]}>/ {cases?.totalEmi}</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
{isExternalAgent && (
|
||||
<>
|
||||
<View style={[GenericStyles.borderTop, GenericStyles.w100]} />
|
||||
<PerformanceItem
|
||||
title={PerformanceDetails.suspiciousFeedback}
|
||||
icon={<SuspiciousFeedbackIcon />}
|
||||
rightSideView={
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
||||
<Text style={[GenericStyles.fontSize16, styles.fw700, styles.leftContent]}>
|
||||
{sanatizeSuspisiousFeedback(performanceOverviewData?.suspiciousFeedback ?? 0)}%
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -59,6 +78,7 @@ const styles = StyleSheet.create({
|
||||
...GenericStyles.br6,
|
||||
...GenericStyles.mr16,
|
||||
...GenericStyles.ml16,
|
||||
paddingHorizontal: 16,
|
||||
...getShadowStyle(2),
|
||||
},
|
||||
leftContent: {
|
||||
|
||||
@@ -4,6 +4,7 @@ export const PerformanceDetails = {
|
||||
totalCashCollected: 'Total cash collected',
|
||||
caseWithAtleast1Emi: 'Cases with atleast 1 EMI collected',
|
||||
performanceLevel: 'Performance level',
|
||||
suspiciousFeedback: 'Suspicious feedback %',
|
||||
};
|
||||
|
||||
export const PerformanceCardDetails = {
|
||||
@@ -15,6 +16,11 @@ export const PerformanceCardDetails = {
|
||||
nonPtp: 'non PTPs',
|
||||
ptpConverted: 'PTPs converted',
|
||||
brokenPtp: 'broken PTPs',
|
||||
suspicious: 'suspicious',
|
||||
genuine: 'genuine',
|
||||
feedbacks: 'feedbacks today',
|
||||
ptps: 'PTPs',
|
||||
paid: 'paid',
|
||||
};
|
||||
|
||||
export const PerfomanceHeaderTitle = 'Performance summary';
|
||||
@@ -70,3 +76,5 @@ export const BIGGER_BOX_SIZE_MULTIPLIER = 1.25;
|
||||
|
||||
export const xAxisLabels = ['6th', '13th', '20th', '27th'];
|
||||
export const yAxisLabels = [4, 3, 2, 1];
|
||||
|
||||
export const COMMON_FILTER = 'COMMON';
|
||||
|
||||
@@ -16,21 +16,21 @@ import { useAppDispatch, useAppSelector } from '../../hooks';
|
||||
import { resetTodoList } from '../../reducer/allCasesSlice';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import DashboardHeader from './DashboardHeader';
|
||||
import PerformanceCard from './PerformanceCard';
|
||||
import PerformanceMeter from './PerformanceMeter';
|
||||
import PerformanceOverview from './PerformanceOverview';
|
||||
import ExternalAgentDashboard from './ExternalAgentDashboard';
|
||||
import InternalAgentDashboard from './InternalAgentDashboard';
|
||||
|
||||
const Dashboard = () => {
|
||||
const [refreshing, setRefreshing] = React.useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const caseDetailsIds = useAppSelector((state) => Object.keys(state.allCases.caseDetails));
|
||||
const isExternalAgent = useAppSelector((state) => state.user?.isExternalAgent);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const isFocused = useIsFocused();
|
||||
|
||||
const fetchAgentPerformanceMetrics = (callbackFn?: () => void) => {
|
||||
setIsLoading(true);
|
||||
dispatch(getPerformanceMetrics(caseDetailsIds, setIsLoading, callbackFn));
|
||||
dispatch(getPerformanceMetrics(caseDetailsIds, isExternalAgent, setIsLoading, callbackFn));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -68,9 +68,7 @@ const Dashboard = () => {
|
||||
style={styles.scrollViewContainer}
|
||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
|
||||
>
|
||||
<PerformanceOverview />
|
||||
<PerformanceMeter />
|
||||
<PerformanceCard />
|
||||
{isExternalAgent ? <ExternalAgentDashboard /> : <InternalAgentDashboard />}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
|
||||
@@ -43,6 +43,15 @@ export interface CashCollectedProgressItem {
|
||||
belowLevel: number;
|
||||
}
|
||||
|
||||
export interface FeedbackDetals {
|
||||
totalFeedbackCount: number;
|
||||
todaysFeedbackCount: number;
|
||||
todaysGenuineFeedbackCount: number;
|
||||
todaysSuspiciousFeedbackCount: number;
|
||||
totalSuspiciousFeedbackCount: number;
|
||||
totalGenuineFeedbackCount: number;
|
||||
}
|
||||
|
||||
export interface PerformanceDataType {
|
||||
cases: CasesType;
|
||||
performanceLevel: PerformanceLevelType;
|
||||
@@ -51,6 +60,7 @@ export interface PerformanceDataType {
|
||||
lastUpdatedAt: number;
|
||||
cashCollectedTillDate: CashCollectedProgressItem;
|
||||
cashCollectedGraphData: CashCollectedGraphData;
|
||||
feedbackDetails: FeedbackDetals;
|
||||
}
|
||||
|
||||
export interface CashCollectedData {
|
||||
@@ -122,3 +132,35 @@ export interface CashCollectedSplitPayload {
|
||||
pageNo: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
interface PerformanceOverviewData {
|
||||
totalCashCollected: number;
|
||||
atleastOneEmiCollected: number;
|
||||
totalEmi: number;
|
||||
suspiciousFeedback?: number;
|
||||
}
|
||||
|
||||
export interface PerformanceOverviewProps {
|
||||
isExternalAgent?: boolean;
|
||||
performanceOverviewData: PerformanceOverviewData;
|
||||
}
|
||||
|
||||
interface PerformanceCardItem {
|
||||
totalConverted: number;
|
||||
convertedText: string;
|
||||
totalNotConverted: number;
|
||||
notConvertedText: string;
|
||||
redirectionType: CurrentAllocationStats;
|
||||
otherDetailsCount?: number;
|
||||
otherDetailsText?: string;
|
||||
v1?: boolean;
|
||||
}
|
||||
|
||||
export interface PerformanceCardProps {
|
||||
item: PerformanceCardItem;
|
||||
handleActionItemClick: (item: PerformanceDetailsType) => void;
|
||||
}
|
||||
|
||||
export enum Animation {
|
||||
SLIDE_FROM_RIGHT = 'slide_from_right',
|
||||
}
|
||||
|
||||
@@ -84,6 +84,63 @@ export const getPerformanceDetails = (cases: CasesType) => {
|
||||
];
|
||||
};
|
||||
|
||||
export const getExternalAgentPerformanceDetails = (cases: CasesType, feedbackDetails: any) => {
|
||||
const {
|
||||
visitedCases,
|
||||
unvisitedCases,
|
||||
contactableCases,
|
||||
nonContactableCases,
|
||||
totalPtp,
|
||||
convertedPtp,
|
||||
brokenPtp,
|
||||
} = cases || {};
|
||||
|
||||
const {
|
||||
todaysFeedbackCount = 0,
|
||||
todaysGenuineFeedbackCount = 0,
|
||||
todaysSuspiciousFeedbackCount = 0,
|
||||
} = feedbackDetails || {};
|
||||
return [
|
||||
{
|
||||
totalConverted: visitedCases,
|
||||
convertedText: PerformanceCardDetails.visitedCases,
|
||||
totalNotConverted: unvisitedCases,
|
||||
notConvertedText: PerformanceCardDetails.unVisitedCases,
|
||||
otherDetailsCount: 0,
|
||||
otherDetailsText: '',
|
||||
redirectionType: CurrentAllocationStats.UNVISITED,
|
||||
v1: true,
|
||||
},
|
||||
{
|
||||
totalConverted: contactableCases,
|
||||
convertedText: PerformanceCardDetails.contactableCases,
|
||||
totalNotConverted: nonContactableCases,
|
||||
notConvertedText: PerformanceCardDetails.nonContactableCases,
|
||||
otherDetailsCount: 0,
|
||||
otherDetailsText: '',
|
||||
redirectionType: CurrentAllocationStats.NON_CONTACTABLE,
|
||||
v1: true,
|
||||
},
|
||||
{
|
||||
totalConverted: totalPtp,
|
||||
convertedText: PerformanceCardDetails.ptps,
|
||||
otherDetailsCount: convertedPtp,
|
||||
otherDetailsText: PerformanceCardDetails.paid,
|
||||
totalNotConverted: brokenPtp,
|
||||
notConvertedText: PerformanceCardDetails.brokenPtp,
|
||||
redirectionType: CurrentAllocationStats.BROKEN_PTP,
|
||||
},
|
||||
{
|
||||
totalConverted: todaysFeedbackCount,
|
||||
convertedText: PerformanceCardDetails.feedbacks,
|
||||
otherDetailsCount: todaysGenuineFeedbackCount,
|
||||
otherDetailsText: PerformanceCardDetails.genuine,
|
||||
totalNotConverted: todaysSuspiciousFeedbackCount,
|
||||
notConvertedText: PerformanceCardDetails.suspicious,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export const getFiltersCount = (caseDetailsIds: Array<string>, cases: CasesType) => {
|
||||
const { unvisitedCaseIds, nonContactableCaseIds, nonPtpCaseIds, brokenPtpCaseIds } = cases || {};
|
||||
|
||||
@@ -294,17 +351,23 @@ export const formatNumberWithSuffix = (number: number): string => {
|
||||
const formatted = (number / 1000).toFixed(1);
|
||||
return formatted.endsWith('.0') ? formatted.slice(0, -2) + 'k' : formatted + 'k';
|
||||
} else {
|
||||
return number.toString();
|
||||
return number?.toString();
|
||||
}
|
||||
};
|
||||
|
||||
export const isAgentDashboardVisible = () => {
|
||||
const user = useAppSelector((state) => state.user);
|
||||
|
||||
const isExternalAgent = user?.isExternalAgent;
|
||||
const roles = user?.agentRoles;
|
||||
|
||||
const isFieldAgent = roles?.includes(IUserRole.ROLE_FIELD_AGENT);
|
||||
|
||||
return !isExternalAgent && isFieldAgent && roles?.length === 1;
|
||||
return isFieldAgent && roles?.length === 1;
|
||||
};
|
||||
|
||||
export const sanatizeSuspisiousFeedback = (feedbackPercent: number) => {
|
||||
if (feedbackPercent) {
|
||||
return feedbackPercent?.toFixed(1);
|
||||
}
|
||||
return feedbackPercent;
|
||||
};
|
||||
|
||||
@@ -101,9 +101,6 @@ const CasesList: React.FC<ICasesList> = ({
|
||||
showAgentSelectionBottomSheet: state.reportees.showAgentSelectionBottomSheet,
|
||||
}));
|
||||
|
||||
const { showToast = false, caseType = '' } =
|
||||
useAppSelector((state: RootState) => state.allCases?.filteredListToast) || {};
|
||||
|
||||
const [showFilterModal, setShowFilterModal] = React.useState<boolean>(false);
|
||||
const flashListRef = useRef<GenericType>(null);
|
||||
const scrollAnimation = useRef(new Animated.Value(0)).current;
|
||||
@@ -111,6 +108,7 @@ const CasesList: React.FC<ICasesList> = ({
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const firePageLoadEvent = () => {
|
||||
if (isAgentDashboard) return;
|
||||
if (getCurrentScreen()?.name === 'Cases') {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_CASE_LIST_LOAD);
|
||||
} else {
|
||||
@@ -244,23 +242,6 @@ const CasesList: React.FC<ICasesList> = ({
|
||||
intermediateTodoListMap,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (showToast) {
|
||||
const filteredCasesCount = filteredCasesList.length;
|
||||
toast({
|
||||
type: 'info',
|
||||
text1: `${filteredCasesCount} ${caseType} case(s) have been filtered`,
|
||||
visibilityTime: 3000,
|
||||
});
|
||||
dispatch(
|
||||
setFilteredListToast({
|
||||
showToast: false,
|
||||
caseType,
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [filteredCasesList]);
|
||||
|
||||
const handleSearchChange = useCallback(
|
||||
debounce((query: string) => {
|
||||
setSearchQuery(query);
|
||||
|
||||
@@ -11,6 +11,8 @@ import QuickFilters from '../../components/screens/allCases/allCasesFilters/Quic
|
||||
import { useAppSelector } from '../../hooks';
|
||||
import { VisitPlanStatus } from '../../reducer/userSlice';
|
||||
import { FeedbackStatus } from '../caseDetails/interface';
|
||||
import { RootState } from '@store';
|
||||
import { FiltePlaceholderText } from './interface';
|
||||
|
||||
interface IFilters {
|
||||
searchQuery: string;
|
||||
@@ -39,6 +41,10 @@ const Filters: React.FC<IFilters> = ({
|
||||
filterCount: isVisitPlan ? state.filters.filterCountVisitPlan : state.filters.filterCount,
|
||||
})
|
||||
);
|
||||
|
||||
const { caseType = '' } =
|
||||
useAppSelector((state: RootState) => state.allCases?.filteredListToast) || {};
|
||||
|
||||
const getBarWidth = () => {
|
||||
if (!totalPinnedCount) {
|
||||
return 0;
|
||||
@@ -46,6 +52,12 @@ const Filters: React.FC<IFilters> = ({
|
||||
return attemptedCount / totalPinnedCount;
|
||||
};
|
||||
|
||||
const getTextInputPlaceholder = () => {
|
||||
if (isVisitPlan) return FiltePlaceholderText.VISIT_PLAN;
|
||||
if (caseType && isAgentDashboard) return `${caseType} ${FiltePlaceholderText.CASES}`;
|
||||
return FiltePlaceholderText.MY_CASES;
|
||||
};
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={[GenericStyles.mh16, GenericStyles.pb16, GenericStyles.centerAlignedRow]}>
|
||||
@@ -54,7 +66,7 @@ const Filters: React.FC<IFilters> = ({
|
||||
style={!isAgentDashboard ? styles.textInput : styles.flexBasis95}
|
||||
LeftComponent={<SearchIcon />}
|
||||
onChangeText={handleSearchChange}
|
||||
placeholder={`Search in ${isVisitPlan ? 'visit plan' : 'my cases'}`}
|
||||
placeholder={`Search in ${getTextInputPlaceholder()}`}
|
||||
defaultValue={searchQuery}
|
||||
testID="test_search"
|
||||
showClearIcon
|
||||
|
||||
@@ -338,3 +338,9 @@ export interface IReportee {
|
||||
agencyCode: string;
|
||||
agencyName: string;
|
||||
}
|
||||
|
||||
export enum FiltePlaceholderText {
|
||||
VISIT_PLAN = 'visit plan',
|
||||
CASES = 'cases',
|
||||
MY_CASES = 'my cases',
|
||||
}
|
||||
|
||||
@@ -82,10 +82,6 @@ const AuthRouter = () => {
|
||||
|
||||
NetworkStatusService.listenForOnline(dispatch);
|
||||
|
||||
useEffect(() => {
|
||||
Linking.addEventListener('url', (event) => handleUrl(event.url));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoggedIn) {
|
||||
CosmosForegroundService.isRunning().then((isRunning) => {
|
||||
|
||||
@@ -43,13 +43,15 @@ const CashCollectedItem = (props: CashCollectedItemProps) => {
|
||||
>
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter, styles.flexBasis96]}>
|
||||
<CaseItemAvatar caseDetailObj={caseItemAvatarCaseDetailObj} shouldBatchAvatar={false} />
|
||||
<View style={GenericStyles.pl12}>
|
||||
<Text style={styles.textHeading}>{sanitizeString(customerName)}</Text>
|
||||
<Text style={styles.subText}>
|
||||
Collected : <Text>{formatAmount(Number(amountCollected?.toFixed(2)), false)}</Text>
|
||||
<View style={[GenericStyles.pl12, GenericStyles.fill]}>
|
||||
<Text style={styles.textHeading} numberOfLines={1}>
|
||||
{sanitizeString(customerName)}
|
||||
</Text>
|
||||
<Text style={styles.subText}>
|
||||
Current outstanding :{' '}
|
||||
Collected: <Text>{formatAmount(Number(amountCollected?.toFixed(2)), false)}</Text>
|
||||
</Text>
|
||||
<Text style={styles.subText}>
|
||||
Current outstanding:{' '}
|
||||
<Text style={totalOverdueAmount > 0 ? styles.red : styles.overdueAmountColor}>
|
||||
{formatAmount(totalOverdueAmount, false)}
|
||||
</Text>
|
||||
|
||||
@@ -100,6 +100,30 @@ const CashCollected = () => {
|
||||
</View>
|
||||
);
|
||||
|
||||
const listFooterComponent = () => {
|
||||
if (data?.length) {
|
||||
if (data.length < paginationDetails.totalElements) {
|
||||
return (
|
||||
<View
|
||||
style={[GenericStyles.centerAlignedRow, GenericStyles.mb12, styles.loadingContainer]}
|
||||
>
|
||||
{isLoading ? <ActivityIndicator color={COLORS.BASE.BLUE} /> : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
if (data.length === paginationDetails.totalElements) {
|
||||
return (
|
||||
<View style={[GenericStyles.centerAlignedRow, { marginTop: -6, marginBottom: 36 }]}>
|
||||
<Text style={{ color: COLORS.TEXT.LIGHT }}>You have reached the end of the list</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<SafeAreaView style={[GenericStyles.fill, styles.container]}>
|
||||
@@ -117,19 +141,7 @@ const CashCollected = () => {
|
||||
)}
|
||||
onEndReached={fetchMoreData}
|
||||
onEndReachedThreshold={0.5}
|
||||
ListFooterComponent={
|
||||
data?.length && data.length < paginationDetails.totalElements ? (
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.centerAlignedRow,
|
||||
GenericStyles.mb12,
|
||||
styles.loadingContainer,
|
||||
]}
|
||||
>
|
||||
{isLoading ? <ActivityIndicator color={COLORS.BASE.BLUE} /> : null}
|
||||
</View>
|
||||
) : null
|
||||
}
|
||||
ListFooterComponent={listFooterComponent()}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.centerAbsolute}>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import React from 'react';
|
||||
import { getPageLoadEventName } from '@screens/Dashboard/utils';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useAppSelector } from '../../hooks';
|
||||
import CasesList from '../allCases/CasesList';
|
||||
|
||||
const FilteredCases = () => {
|
||||
const { pendingList, pinnedList, completedList } = useAppSelector((state) => state.allCases);
|
||||
const { caseType } = useAppSelector((state) => state.allCases?.filteredListToast) || {};
|
||||
|
||||
useEffect(() => {
|
||||
if (caseType) addClickstreamEvent(getPageLoadEventName(caseType));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CasesList casesList={[...pendingList, ...pinnedList, ...completedList]} isAgentDashboard />
|
||||
|
||||
Reference in New Issue
Block a user