TP-42467 | Tele sensei v3.1 (#815)

* TP-42467 |release 18th jan| Aman Singh

* TP-42467 |3.2 half done| Aman Singh

* TP-42467 |EM UAT done| Aman Singh

* TP-42467 |linting fix| Aman Singh

* TP-42467 |linting fix| Aman Singh

* TP-42467 |tele sensei done| Aman Singh

* TP-42467 |tele sensei done| Aman Singh

* TP-42467 |tele sensei done| Aman Singh

* TP-42467 |tele sensei done| Aman Singh

* TP-42467 |tele sensei done| Aman Singh

* TP-42467 |final commit| Aman singh

* TP-42467 |lint fix| Aman singh

* TP-42467 |before UAT| Aman singh

* TP-48182 | UAT Fixes

* TP-48182 | UAT Fix

* TP-48182 | Resolved PR comments

* TP-48182 | Removed Mock Data

* TP-48182 | PR comments

* TP-57238|tele sensei v3.1 comments resolved| Aman Singh

* TP-48182 | Border Radius Removed from header

* TP-57238|tele sensei v3.1 comments resolved| Aman Singh

* TP-57238|tele sensei v3.1 comments resolved| Aman Singh

---------

Co-authored-by: yashmantri <mantri.ramkishor@navi.com>
Co-authored-by: Varnit Goyal <varnit.goyal@navi.com>
This commit is contained in:
Aman Singh
2024-02-20 19:08:44 +05:30
committed by GitHub
parent 9c1d2e31b6
commit b9817f9104
25 changed files with 2018 additions and 29 deletions

View File

@@ -89,6 +89,7 @@
--light-blue-background: #e5f3ff;
--navigation-blue-navigation-content-1: #fcfcfd;
--navigation-blue-c2: #caced5;
--navi-color-blue-navigation: #001833;
// black base order
--black-base: #1a1a1a;

View File

@@ -9,6 +9,7 @@ interface ICellRendererShortNumber {
hiddenPadding?: number;
significantDigits?: number;
isCurrency?: boolean;
ellipsisWrapperClass?: string;
}
const CellRendererShortNumber: React.FC<ICellRendererShortNumber> = props => {
@@ -17,7 +18,8 @@ const CellRendererShortNumber: React.FC<ICellRendererShortNumber> = props => {
lineClamp = 1,
hiddenPadding = 10,
significantDigits = 1,
isCurrency = false
isCurrency = false,
ellipsisWrapperClass
} = props;
if (!Number.isFinite(value)) {
@@ -26,7 +28,9 @@ const CellRendererShortNumber: React.FC<ICellRendererShortNumber> = props => {
}
if (!value && value !== 0) {
return <EllipsisText text={'-'} lineClamp={lineClamp} />;
return (
<EllipsisText ellipsisWrapperClass={ellipsisWrapperClass} text={'-'} lineClamp={lineClamp} />
);
}
let shorthandNumber = value == 0 ? '0' : shortNumberNotation(value, significantDigits);
@@ -37,7 +41,13 @@ const CellRendererShortNumber: React.FC<ICellRendererShortNumber> = props => {
if (isCurrency) {
shorthandNumber = `${shorthandNumber}`;
}
return <EllipsisText text={shorthandNumber ?? '-'} lineClamp={lineClamp} />;
return (
<EllipsisText
ellipsisWrapperClass={ellipsisWrapperClass}
text={shorthandNumber ?? '-'}
lineClamp={lineClamp}
/>
);
}
if (isCurrency) {
@@ -48,7 +58,11 @@ const CellRendererShortNumber: React.FC<ICellRendererShortNumber> = props => {
return (
<Tooltip placement="top" hiddenPadding={hiddenPadding} hideStrategy="referenceHidden">
<TooltipTrigger tooltipTriggerClassName="tooltipTriggerWrapper">
<EllipsisText text={shorthandNumber ?? '-'} lineClamp={lineClamp} />
<EllipsisText
ellipsisWrapperClass={ellipsisWrapperClass}
text={shorthandNumber ?? '-'}
lineClamp={lineClamp}
/>
</TooltipTrigger>
<TooltipContent
className="tooltipWrapper"

View File

@@ -128,6 +128,7 @@ function SideNavBar({ isDc97User, isHRCChatUser }: ISideNavbarProps) {
((percentCompletedCases.paid / percentCompletedCases.total) * 100).toFixed(0)
);
const hide = HideSideBar.some(path => pathname.includes(path));
const isTeleInhouseTeamLead = userData?.roles?.includes(Roles.ROLE_NAVI_TELE_INHOUSE_TEAM_LEAD);
const disableAmeyoToolbarFlag = window?.config?.DISABLE_AMEYO_TOOLBAR === 'true' || isDc97User;
@@ -422,7 +423,7 @@ function SideNavBar({ isDc97User, isHRCChatUser }: ISideNavbarProps) {
/>
) : null}
{isInternalTeamLead ? (
{isTeleInhouseTeamLead ? (
<SidebarLinks
activeIcon={<AgentsStatusIcon />}
inActiveIcon={<AgentsStatusIcon strokeColor="var(--navigation-blue-c2)" />}
@@ -489,12 +490,12 @@ function SideNavBar({ isDc97User, isHRCChatUser }: ISideNavbarProps) {
/>
) : null}
{isInternalTeamLead ? (
{isTeleInhouseTeamLead ? (
<SidebarLinks
key={APP_ROUTES.AGENT_AVAILABILITY.path}
activeIcon={<AvailabilityTrackerIcon />}
inActiveIcon={<AvailabilityTrackerIcon strokeColor="var(--navigation-blue-c2)" />}
name={'Availablity Tracker'}
name={'Availability Tracker'}
route={APP_ROUTES.AGENT_AVAILABILITY.path}
linkClickHandler={linkClickHandler}
currentPathname={pathname}
@@ -530,13 +531,13 @@ function SideNavBar({ isDc97User, isHRCChatUser }: ISideNavbarProps) {
/>
) : null}
{isInternalTeamLead ? (
{isTeleInhouseTeamLead ? (
<>
<SidebarLinks
key={APP_ROUTES.HR_DASHBOARD.path}
activeIcon={<PDIcon />}
inActiveIcon={<PDIcon fillColor="var(--navigation-blue-c2)" />}
name={'Tele Sensei'}
name={'Performance Dashboard'}
route={APP_ROUTES.SENSEI_TELE.path}
linkClickHandler={linkClickHandler}
currentPathname={pathname}

View File

@@ -4,9 +4,11 @@ import {
setAgentPerformance,
setApiResponse,
setDespositionSummary,
setEmiBucketWiseSummary,
setLoading,
setLoadingAgentPerformance,
setLoadingDespositionSummary
setLoadingDespositionSummary,
setLoadingEmiBucketWiseSummary
} from '../reducer';
export const getAgentPerformanceData = () => (dispatch: Dispatch) => {
@@ -62,3 +64,19 @@ export const getDespositionSummary = () => (dispatch: Dispatch) => {
dispatch(setLoadingDespositionSummary(false));
});
};
export const getEMIBucketWiseSummary = () => (dispatch: Dispatch) => {
dispatch(setLoadingEmiBucketWiseSummary(true));
const url = getApiUrl(ApiKeys.EMI_BUCKET_WISE_SUMMARY);
axiosInstance
.get(url)
.then(response => {
dispatch(setEmiBucketWiseSummary(response?.data?.data));
})
.catch(error => {
logError(error, 'emiBucketWiseSummary');
})
.finally(() => {
dispatch(setLoadingEmiBucketWiseSummary(false));
});
};

View File

@@ -185,7 +185,7 @@ export const AgentPerformanceColumns = [
},
{
field: 'avgAttemptPerUnpaidCase',
headerName: 'Average Calls / Unpaid Case',
headerName: 'Average Calls / Unpaid Cases',
minWidth: 150,
sortable: true,
wrapHeaderText: true,

View File

@@ -44,6 +44,10 @@
line-height: 20px;
/* 142.857% */
letter-spacing: -0.175px;
width: -webkit-fill-available !important;
width: -moz-available !important;
width: -webkit-fill-available !important;
width: fill-available !important;
}
.ag-header-group-cell-label {
@@ -159,9 +163,11 @@
}
.ag-floating-top {
overflow-y: hidden !important;
font-weight: 900;
color: var(--navi-color-gray-c1) !important;
border-bottom: 0px !important;
.ag-row {
background-color: var(--bg-primary) !important;
}

View File

@@ -62,6 +62,7 @@
line-height: 20px;
/* 142.857% */
letter-spacing: -0.175px;
width: -webkit-fill-available !important;
}
.ag-header-group-cell-label {
@@ -88,6 +89,7 @@
}
.ag-header-row {
width: 100% !important;
.ag-header-cell {
padding-left: 16px;
padding-right: 0px;
@@ -183,11 +185,15 @@
.ag-floating-top {
font-weight: 900;
overflow-y: hidden !important;
color: var(--navi-color-gray-c1) !important;
border-bottom: 0px !important;
.ag-row {
background-color: var(--bg-primary) !important;
}
.ag-floating-top-container {
width: -webkit-fill-available !important;
}
}
.ag-sort-indicator-icon {
@@ -200,5 +206,12 @@
.ag-header-cell-text {
line-height: 18px;
}
.ag-header-container {
width: 100% !important;
}
.ag-center-cols-viewport {
overflow: hidden;
}
}
}

View File

@@ -147,6 +147,10 @@ const Index: React.FC<IFilters> = props => {
return [data];
}, [tableData]);
useEffect(() => {
ref?.current?.api?.sizeColumnsToFit();
}, [tableData, loading]);
return (
<div className={cx(styles.agentTableContainer, 'agentPerformance')}>
<div className={styles.headerContainer}>
@@ -185,6 +189,7 @@ const Index: React.FC<IFilters> = props => {
animateRows
pinnedTopRowData={pinnedRow}
suppressRowClickSelection={true}
headerHeight={80}
PaginationComponent={
<Pagination
key={pageSize + pageSize}

View File

@@ -20,7 +20,7 @@
.agentDepositionSummary {
.ag-header-viewport {
.ag-header-row-column-group {
> div:nth-child(1) {
> div:nth-child(2) {
background-color: var(--green-light);
color: var(--green-base);
pointer-events: none;
@@ -32,9 +32,10 @@
line-height: 20px;
/* 142.857% */
letter-spacing: -0.175px;
width: -webkit-fill-available !important;
}
> div:nth-child(2) {
> div:nth-child(3) {
background-color: var(--pale-yellow);
color: var(--navi-color-yellow-dark);
pointer-events: none;
@@ -72,6 +73,7 @@
}
.ag-header-row {
width: 100% !important;
.ag-header-cell {
padding-left: 16px;
padding-right: 0px;
@@ -161,6 +163,7 @@
}
.ag-floating-top {
overflow-y: hidden !important;
font-weight: 900;
color: var(--navi-color-gray-c1) !important;
border-bottom: 0px !important;
@@ -185,5 +188,11 @@
.ag-horizontal-left-spacer {
visibility: hidden;
}
.ag-center-cols-viewport {
overflow: hidden;
}
.ag-header-container {
width: 100% !important;
}
}
}

View File

@@ -64,6 +64,10 @@ const Index: React.FC<IFilters> = props => {
setColumnData(newColumnData);
}, []);
useEffect(() => {
ref?.current?.api?.sizeColumnsToFit();
}, [tableData, loading]);
const handlePageChange = (e: number) => {
const newParams = { ...params };
const pageNumber = e.toString();

View File

@@ -0,0 +1,27 @@
import HorizontalProgress from '@cp/src/components/ProgressBars/horizontalProgress/HorizontalProgress';
import React from 'react';
import styles from './index.module.scss';
import cx from 'classnames';
import { formatNumber } from '@cp/src/utils/commonUtils';
interface ICallOverViewProgressBar {
title: string;
percentage: number;
}
const CallOverViewProgressBar: React.FC<ICallOverViewProgressBar> = props => {
const { title, percentage } = props;
return (
<div className={styles.numberOfCallsLineChartContainer}>
<span className={cx(styles.label, styles.text)}>{title}</span>
<HorizontalProgress
className={styles.progressColorBackground}
percentageClassName={styles.progressColor}
percentage={percentage}
/>
<span className={styles.text}>{formatNumber(percentage, true)}%</span>
</div>
);
};
export default CallOverViewProgressBar;

View File

@@ -0,0 +1,89 @@
import Dropdown from '@cp/src/components/Dropdown';
import HorizontalProgress from '@cp/src/components/ProgressBars/horizontalProgress/HorizontalProgress';
import { SelectPickerOptionProps } from '@cp/src/types/CommonConstans';
import { readQueryParams } from '@cp/src/utils/QueryParamsHelper';
import { formatNumber } from '@cp/src/utils/commonUtils';
import { Typography } from '@navi/web-ui/lib/primitives';
import cx from 'classnames';
import React from 'react';
import { AGENT_PERFORMANCE_TABLE, DROPDOWN_OPTIONS } from '../../constants';
import { ICallsOverView } from '../../types';
import NoDataFoundComponent from './NoDataFoundComponent';
import styles from './index.module.scss';
import CallOverViewProgressBar from './CallOverViewProgressBar';
const CallsOverView: React.FC<ICallsOverView> = props => {
const { data, updateAndNavigate, params } = props;
if (!data) {
return <NoDataFoundComponent title="Calls Overview" />;
}
const handleSelectionChange = (value: SelectPickerOptionProps) => {
if (!value) return;
const { [AGENT_PERFORMANCE_TABLE]: updatedParams } = readQueryParams();
const newParams = { ...updatedParams };
newParams.depositionSummaryCallOverViewFilter = value.value as string;
updateAndNavigate(newParams);
};
const { TOTAL_CALLS, VIA_AMEYO, VIA_LONGHORN, VIA_SELF } = data ?? {};
const longhornPercentage =
(Number(
VIA_LONGHORN?.[params.depositionSummaryCallOverViewFilter as keyof typeof VIA_LONGHORN]
) /
Number(
TOTAL_CALLS?.[params.depositionSummaryCallOverViewFilter as keyof typeof TOTAL_CALLS]
)) *
100;
const ameyoPercentage =
(Number(VIA_AMEYO?.[params.depositionSummaryCallOverViewFilter as keyof typeof VIA_AMEYO]) /
Number(
TOTAL_CALLS?.[params.depositionSummaryCallOverViewFilter as keyof typeof TOTAL_CALLS]
)) *
100;
const selfPercentage =
(Number(VIA_SELF?.[params.depositionSummaryCallOverViewFilter as keyof typeof VIA_SELF]) /
Number(
TOTAL_CALLS?.[params.depositionSummaryCallOverViewFilter as keyof typeof TOTAL_CALLS]
)) *
100;
return (
<div className={styles.ptpOverview}>
<div className={styles.headingAndFilter}>
<Typography variant="p3" className="font-medium text-[var(--navi-color-gray-c2)]">
Calls Overview
</Typography>
<Dropdown
containerClasses={styles.dropdownContainer}
innerContainerStyle={styles.dropdownInnerContainer}
options={DROPDOWN_OPTIONS}
onSelectionChange={handleSelectionChange}
defaultSelected={DROPDOWN_OPTIONS.find(
item => item.value === params.depositionSummaryCallOverViewFilter
)}
/>
</div>
<div className={cx(styles.titleContainer)}>
<div
className={cx({
[styles.pieChart]: true,
pieChartContainerClass: true
})}
>
<div className={cx(styles.pieChartContainer, styles.callsOverviewHeight)}>
<CallOverViewProgressBar title="Longhorn" percentage={longhornPercentage} />
<CallOverViewProgressBar title="Ameyo" percentage={ameyoPercentage} />
<CallOverViewProgressBar title="Self" percentage={selfPercentage} />
</div>
</div>
</div>
</div>
);
};
export default CallsOverView;

View File

@@ -0,0 +1,22 @@
import NoDataFound from '@cp/src/assets/icons/NoDataFound';
import { Typography } from '@navi/web-ui/lib/primitives';
import styles from './index.module.scss';
import React from 'react';
interface NoDataFoundComponentProps {
title: string;
}
const NoDataFoundComponent: React.FC<NoDataFoundComponentProps> = props => {
const { title } = props;
return (
<div className={styles.emptyView}>
<div className={styles.headingAndFilter}>
<Typography variant="p3">{title}</Typography>
</div>
<NoDataFound className={styles.noDataFoundIcon} />
</div>
);
};
export default NoDataFoundComponent;

View File

@@ -0,0 +1,117 @@
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import React from 'react';
import { formatNumber, pluralisation } from 'src/utils/commonUtils';
import styles from './index.module.scss';
import { Typography } from '@navi/web-ui/lib/primitives';
import { summaryGroup, summaryGroupColorMapping } from '../../constants';
import NoDataFoundComponent from './NoDataFoundComponent';
import { CashEarnedChartHCProps, responseDataType } from '../../types';
const transformData = (responseData: responseDataType) => {
const data = [];
let key: keyof typeof responseData;
for (key in responseData) {
data.push({
name: summaryGroup[key as keyof typeof summaryGroup],
y: responseData[key],
value: responseData[key],
color: summaryGroupColorMapping[key as keyof typeof summaryGroupColorMapping],
formattedValue: formatNumber(responseData[key] as number, false)
});
}
return data;
};
const getSum = (data: any) => {
let sum = 0;
let key: keyof typeof data;
for (key in data) {
sum += Number.isInteger(data[key]) ? data[key] : 0;
}
return sum;
};
const NumberOfCases: React.FC<CashEarnedChartHCProps> = props => {
const { dataTObeFed } = props;
if (!dataTObeFed) {
return <NoDataFoundComponent title="Number of Cases" />;
}
const sum = getSum(dataTObeFed);
const options = {
chart: {
plotBackgroundColor: null,
plotBorderWidth: 0,
plotShadow: false,
style: {
fontFamily: 'Inter'
},
renderTo: 'container',
height: 215,
width: 500
},
credits: {
enabled: false
},
title: {
text: `<div class='${styles.pieChartCenter}'>${formatNumber(sum)}<br><span class=${
styles.pieChartSubtext
}>Total ${pluralisation('Case', sum, 's')}<span></div>`,
align: 'center',
verticalAlign: 'middle'
},
tooltip: {
enabled: false
},
accessibility: {
point: {
valueSuffix: '%'
}
},
plotOptions: {
pie: {
size: 160,
animation: true,
dataLabels: {
enabled: true,
allowOverlap: true,
align: 'left',
format: `<div class=${styles.barChartLabel}>{point.name}</div><div class=${styles.barchartLabelNumber}>{point.formattedValue} <span class=${styles.percentage}>({point.percentage:.1f}%)</span></div>`,
useHTML: true,
distance: 20,
filter: {
property: 'percentage',
operator: '>',
value: 0
}
}
}
},
series: [
{
type: 'pie',
name: 'CashEarned',
innerSize: '70%',
data: transformData(dataTObeFed)
}
]
};
return (
<div className={styles.ptpOverview}>
<div className={styles.headingAndFilter}>
<Typography variant="p3" className="font-medium text-[var(--navi-color-gray-c2)]">
Number of cases
</Typography>
</div>
<div className={'pieChartContainerClass'}>
<div className={styles.pieChartContainer}>
<HighchartsReact highcharts={Highcharts} options={options} />
</div>
</div>
</div>
);
};
export default NumberOfCases;

View File

@@ -0,0 +1,159 @@
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import React from 'react';
import styles from './index.module.scss';
import cx from 'classnames';
import Dropdown from '@cp/src/components/Dropdown';
import { readQueryParams } from '@cp/src/utils/QueryParamsHelper';
import { Typography } from '@navi/web-ui/lib/primitives';
import { AGENT_PERFORMANCE_TABLE, DROPDOWN_OPTIONS } from '../../constants';
import { IPTPOverView } from '../../types';
import NoDataFoundComponent from './NoDataFoundComponent';
import { formatNumber } from '@cp/src/utils/commonUtils';
const PTPOverView: React.FC<IPTPOverView> = props => {
const { data, params, updateAndNavigate } = props;
if (!data) {
return <NoDataFoundComponent title="PTP Overview" />;
}
const { PTP_GENERATED, PTP_CONVERTED, BROKEN_PTP, FUTURE_PTP } = data;
const handleSelectionChange = (value: any) => {
if (!value) return;
const { [AGENT_PERFORMANCE_TABLE]: updatedParams } = readQueryParams();
const newParams = { ...updatedParams };
newParams.depositionSummaryPTPOverViewFilter = value.value as string;
updateAndNavigate(newParams);
};
const dataToBeFed = [
{
name: 'PTP Converted',
y: Number(
PTP_CONVERTED[params.depositionSummaryPTPOverViewFilter as keyof typeof PTP_CONVERTED]
),
value: Number(
PTP_CONVERTED[params.depositionSummaryPTPOverViewFilter as keyof typeof PTP_CONVERTED]
),
color: '#5D87FF',
formattedValue: formatNumber(
Number(
PTP_CONVERTED[params.depositionSummaryPTPOverViewFilter as keyof typeof PTP_CONVERTED]
),
true
)
},
{
name: 'Broken PTP',
y: Number(BROKEN_PTP[params.depositionSummaryPTPOverViewFilter as keyof typeof BROKEN_PTP]),
value: Number(
BROKEN_PTP[params.depositionSummaryPTPOverViewFilter as keyof typeof BROKEN_PTP]
),
color: '#6FE8C8',
formattedValue: formatNumber(
Number(BROKEN_PTP[params.depositionSummaryPTPOverViewFilter as keyof typeof BROKEN_PTP]),
true
)
},
{
name: 'Future PTP',
y: Number(FUTURE_PTP[params.depositionSummaryPTPOverViewFilter as keyof typeof FUTURE_PTP]),
value: Number(
FUTURE_PTP[params.depositionSummaryPTPOverViewFilter as keyof typeof FUTURE_PTP]
),
color: '#FA896B',
formattedValue: formatNumber(
Number(FUTURE_PTP[params.depositionSummaryPTPOverViewFilter as keyof typeof FUTURE_PTP]),
true
)
}
];
const options = {
chart: {
plotBackgroundColor: null,
plotBorderWidth: 0,
plotShadow: false,
style: {
fontFamily: 'Inter'
},
renderTo: 'container',
height: 225,
width: 500
},
credits: {
enabled: false
},
title: {
text: `<div>
<div class='${styles.chartTitleContainer}'>${formatNumber(
Number(
PTP_GENERATED[params.depositionSummaryPTPOverViewFilter as keyof typeof PTP_GENERATED]
)
)}</div>
</div>`,
align: 'center',
verticalAlign: 'middle'
},
tooltip: {
enabled: false
},
accessibility: {
point: {
valueSuffix: '%'
}
},
plotOptions: {
pie: {
size: 160,
animation: true,
dataLabels: {
enabled: true,
allowOverlap: true,
align: 'left',
format: `<div class=${styles.barChartLabel}>{point.name}</div><div class=${styles.barchartLabelNumber}>{point.formattedValue}</div>`,
useHTML: true,
distance: 10,
filter: {
property: 'percentage',
operator: '>',
value: 0
}
}
}
},
series: [
{
type: 'pie',
name: 'CashEarned',
innerSize: '70%',
data: dataToBeFed
}
]
};
return (
<div className={cx(styles.ptpOverview, styles.mb0)}>
<div className={styles.headingAndFilter}>
<Typography variant="p3" className="font-medium text-[var(--navi-color-gray-c2)]">
PTPs Overview
</Typography>
<Dropdown
containerClasses={styles.dropdownContainer}
innerContainerStyle={styles.dropdownInnerContainer}
options={DROPDOWN_OPTIONS}
onSelectionChange={handleSelectionChange}
defaultSelected={DROPDOWN_OPTIONS.find(
item => item.value === params.depositionSummaryPTPOverViewFilter
)}
/>
</div>
<div className={'pieChartContainerClass'}>
<div className={styles.pieChartContainer}>
<HighchartsReact highcharts={Highcharts} options={options} />
</div>
</div>
</div>
);
};
export default PTPOverView;

View File

@@ -0,0 +1,186 @@
import { ICellRendererParams } from 'ag-grid-community';
import styles from './index.module.scss';
import TableCellRenderer from '@cp/src/components/TableCellRenderer';
import CellRendererShortNumber from '@cp/src/components/TableCellRenderer/CellRendererShortNumber';
import cx from 'classnames';
import { shortNumberNotation } from '@cp/src/utils/commonUtils';
export const AgentPerformanceColumns = [
{
headerName: '',
children: [
{
field: 'name',
headerName: '',
minWidth: 280,
pinned: 'left',
cellClass: (params: ICellRendererParams) => {
return cx({ [styles.bold]: params?.data?.bold }, styles.border, {
[styles.blueBackground]: params?.data?.blue
});
},
suppressMovable: true,
cellRenderer: (params: ICellRendererParams) => {
const { name } = params.data || {};
return (
<TableCellRenderer
ellipsisWrapperClass={cx({ [styles.subPoint]: params?.data?.subPoint })}
value={name}
showToolTip
toolTipContent={name}
/>
);
}
}
]
},
{
headerName: 'Bucket Range',
children: [
{
field: '<4k',
headerName: 'Less than 4k',
width: 160,
autoHeaderHeight: true,
wrapHeaderText: true,
cellClass: (params: ICellRendererParams) => {
return cx({ [styles.blueBackground]: params?.data?.blue });
},
cellRenderer: (params: ICellRendererParams) => {
const data = params.data?.['<4k'];
const isCurrency = params.data?.isCurrency;
const isPercentage = params.data?.percentage;
const value = shortNumberNotation(data, 2) + '%';
if (isPercentage) {
return (
<TableCellRenderer
ellipsisWrapperClass={cx({ [styles.subPointInternal]: params?.data?.subPoint })}
value={value}
showToolTip
toolTipContent={value}
/>
);
}
return (
<CellRendererShortNumber
ellipsisWrapperClass={cx({
[styles.bold]: params?.data?.bold,
[styles.subPointInternal]: params?.data?.subPoint
})}
value={data}
isCurrency={isCurrency}
/>
);
}
},
{
field: '4k-7k',
wrapHeaderText: true,
headerName: '4k to 7k',
autoHeaderHeight: true,
width: 120,
cellClass: (params: ICellRendererParams) => {
return cx({ [styles.blueBackground]: params?.data?.blue });
},
cellRenderer: (params: ICellRendererParams) => {
const data = params.data?.['4k-7k'];
const isPercentage = params.data?.percentage;
const isCurrency = params.data?.isCurrency;
const value = shortNumberNotation(data, 2) + '%';
if (isPercentage) {
return (
<TableCellRenderer
ellipsisWrapperClass={cx({ [styles.subPointInternal]: params?.data?.subPoint })}
value={value}
showToolTip
toolTipContent={value}
/>
);
}
return (
<CellRendererShortNumber
ellipsisWrapperClass={cx({
[styles.bold]: params?.data?.bold,
[styles.subPointInternal]: params?.data?.subPoint
})}
isCurrency={isCurrency}
value={data}
/>
);
}
},
{
field: '7k-10k',
wrapHeaderText: true,
headerName: '7k to 10k',
autoHeaderHeight: true,
width: 120,
cellClass: (params: ICellRendererParams) => {
return cx({ [styles.blueBackground]: params?.data?.blue });
},
cellRenderer: (params: ICellRendererParams) => {
const data = params.data?.['7k-10k'];
const isPercentage = params.data?.percentage;
const isCurrency = params.data?.isCurrency;
const value = shortNumberNotation(data, 2) + '%';
if (isPercentage) {
return (
<TableCellRenderer
ellipsisWrapperClass={cx({ [styles.subPointInternal]: params?.data?.subPoint })}
value={value}
showToolTip
toolTipContent={value}
/>
);
}
return (
<CellRendererShortNumber
ellipsisWrapperClass={cx({
[styles.bold]: params?.data?.bold,
[styles.subPointInternal]: params?.data?.subPoint
})}
isCurrency={isCurrency}
value={data}
/>
);
}
},
{
field: '10k+',
wrapHeaderText: true,
headerName: 'More than 10k',
autoHeaderHeight: true,
cellClass: (params: ICellRendererParams) => {
return cx({ [styles.blueBackground]: params?.data?.blue });
},
width: 160,
cellRenderer: (params: ICellRendererParams) => {
const data = params.data?.['10k+'];
const isPercentage = params.data?.percentage;
const isCurrency = params.data?.isCurrency;
const value = shortNumberNotation(data, 2) + '%';
if (isPercentage) {
return (
<TableCellRenderer
ellipsisWrapperClass={cx({ [styles.subPointInternal]: params?.data?.subPoint })}
value={value}
showToolTip
toolTipContent={value}
/>
);
}
return (
<CellRendererShortNumber
ellipsisWrapperClass={cx({
[styles.bold]: params?.data?.bold,
[styles.subPointInternal]: params?.data?.subPoint
})}
isCurrency={isCurrency}
value={data}
/>
);
}
}
]
}
];

View File

@@ -0,0 +1,388 @@
.EmiBucketWiseSummary {
position: relative;
margin-top: 120px;
.tableContainer {
.tableActions {
display: flex;
justify-content: space-between;
// padding-right: 32px;
margin-bottom: 12px;
}
}
.border {
border-right: 1px solid var(--navi-color-gray-border) !important;
}
.bold {
font-weight: 600;
}
.blueBackground {
background-color: var(--bg-blue);
}
.dataContainer {
display: flex;
gap: 24px;
.tableContainer {
position: relative;
flex-basis: 66%;
box-shadow: 0px 8px 20px 0px rgba(221, 221, 221, 0.3);
.noDataFound {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
.subPoint {
margin-left: 20px;
color: var(--navi-color-gray-c2);
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 20px;
letter-spacing: -0.13px;
}
.subPointInternal {
color: var(--navi-color-gray-c2);
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 20px;
letter-spacing: -0.13px;
}
.mb0 {
margin-bottom: 0px !important;
}
.ptpOverview {
margin-bottom: 24px;
background: white;
padding-bottom: 32px;
border-radius: 16px;
box-shadow: 0px 8px 20px 0px rgba(221, 221, 221, 0.3);
height: 252px;
.headingAndFilter {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
border-radius: 8px 8px 0px 0px;
border: 0px solid var(--navi-color-gray-border);
background: #f1f2f4;
padding: 0px 16px;
margin-bottom: 12px;
height: 44px;
.heading {
font-size: 20px;
font-weight: 600;
line-height: 28px;
letter-spacing: -0.25px;
color: var(--navi-color-gray-c1);
}
.dropdownContainer {
width: 150px;
.dropdownInnerContainer {
border-radius: 4px;
border: 1px solid var(--navi-color-gray-border);
background: var(--navi-color-gray-bg-primary);
padding: 4px 12.5px;
box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.06);
}
}
}
}
.emptyView {
width: 500px;
height: 252px;
margin-bottom: 24px;
background: white;
padding: 16px 20px 16px 20px;
border-radius: 16px;
box-shadow: 0px 8px 20px 0px rgba(221, 221, 221, 0.3);
position: relative;
&:last-child {
margin-bottom: 0px;
}
.headingAndFilter {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.noDataFoundIcon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.barChartLabel {
color: var(--navi-color-gray-c2);
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 18px;
letter-spacing: -0.12px;
}
.barchartLabelNumber {
color: var(--navi-color-blue-navigation);
text-align: center;
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 12px;
/* 100% */
letter-spacing: -0.12px;
text-align: left;
}
.pieChartCenter {
color: var(--navi-color-gray-c3);
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 20px;
letter-spacing: -0.175px;
}
.pieChartSubtext {
color: var(--navi-color-gray-c3);
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 20px;
letter-spacing: -0.175px;
opacity: 0.5;
}
.progressColorBackground {
background-color: var(--grayscale-6);
height: 12px;
width: 305px;
}
.progressColor {
background-color: var(--green-base);
transition: width 400ms ease-in-out;
}
.percentage {
color: var(--navi-color-gray-c3);
}
.numberOfCallsLineChartContainer {
display: flex;
gap: 8px;
align-items: center;
.label {
flex-basis: 15%;
text-align: right;
}
.text {
color: var(--navi-color-gray-c2);
text-align: right;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 18px;
letter-spacing: -0.12px;
}
}
.pieChartContainer {
display: flex;
justify-content: space-around;
flex-direction: column;
}
.callsOverviewHeight {
height: 200px;
}
}
.fb32 {
flex-basis: 32%;
}
:global {
.agentDepositionSummary {
.ag-header-viewport {
.ag-header-row-column-group {
> div:nth-child(1) {
background-color: var(--pale-yellow);
color: var(--navi-color-yellow-dark);
pointer-events: none;
text-align: center;
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 600;
line-height: 20px;
/* 142.857% */
letter-spacing: -0.175px;
}
.ag-header-group-cell-label {
flex: none;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
text-align: center;
font-size: 14px;
font-style: normal;
font-weight: 600;
line-height: 20px;
letter-spacing: -0.175px;
}
}
.ag-pinned-left-cols-container {
.ag-cell {
padding-right: 5px !important;
}
}
.ag-header-row {
.ag-header-cell {
padding-left: 16px;
padding-right: 0px;
}
}
.ag-row {
.ag-cell {
padding-left: 16px;
padding-right: 0px;
}
}
.ag-pinned-left-header {
.ag-header-cell {
padding-right: 10px;
}
.ag-header-group-cell {
background-color: unset !important;
padding-left: 5px;
}
}
.ag-center-cols-clipper {
min-height: unset !important;
}
.ag-header-group-cell {
border-right: 1px solid var(--greyscale-light);
padding: 0 !important;
margin: 0 !important;
}
.ag-header-group-cell-label {
display: flex;
align-items: center;
justify-content: center;
padding: 0;
}
.ag-header-group-cell {
.ag-header-group-text {
font-size: 14px !important;
}
:nth-child(1) {
display: inline-block;
align-items: center;
justify-content: center;
text-align: center;
width: 100%;
}
}
.paginationWrapperClasses {
overflow: unset;
z-index: var(--z-index-pagination-wrapper);
bottom: 4px;
> div:nth-child(1) {
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
}
}
.customHeaderManagement {
width: 140px;
text-align: center;
.ag-header-cell-comp-wrapper {
.ag-header-cell-label {
display: grid;
margin: auto;
}
}
span {
text-overflow: inherit;
text-wrap: wrap;
width: 42px; // according to figma width
}
}
.ag-react-container {
display: none;
}
.ag-floating-top {
font-weight: 900;
overflow-y: hidden !important;
color: var(--navi-color-gray-c1) !important;
border-bottom: 0px !important;
.ag-row {
background-color: var(--bg-primary) !important;
}
}
.ag-sort-indicator-icon {
padding-right: 4px;
}
.ag-row-last {
border-bottom: none !important;
}
.ag-header-cell-text {
line-height: 18px;
}
.ag-horizontal-left-spacer {
visibility: hidden;
}
}
.pieChartContainerClass {
display: flex;
justify-content: center;
}
}

View File

@@ -0,0 +1,275 @@
import Download from '@cp/src/assets/images/icons/Download';
import Loader from '@cp/src/components/Loader/Loader';
import { RootState } from '@cp/src/store';
import { DateFormat, dateFormat } from '@cp/src/utils/DateHelper';
import { AgTable } from '@navi/web-ui/lib/components';
import { Button, Typography } from '@navi/web-ui/lib/primitives';
import { SortChangedEvent } from 'ag-grid-community';
import { AgGridReact } from 'ag-grid-react';
import cx from 'classnames';
import React, { useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { IBuckerrWiseSummarySum, IEMIBucketWiseSummary, IFilters } from '../../types';
import { AgentPerformanceColumns } from './columnDef';
import styles from './index.module.scss';
import {
SUMMARY_GROUP,
emiBucketWiseRowsEnum,
generateFinalSummaryGroup,
getRowObject,
IEmiBucketWiseDataObject
} from '../../constants';
import NumberOfCases from './NumberOfCases';
import CallsOverView from './CallsOverView';
import PTPOverView from './PtpOverview';
import NoDataFound from '@cp/src/assets/icons/NoDataFound';
import { l } from 'msw/lib/core/GraphQLHandler-d4787f91';
const Index: React.FC<IFilters> = props => {
const { params, updateAndNavigate, url } = props;
const ref = useRef<AgGridReact<any>>(null);
const [columnData, setColumnData] = React.useState(AgentPerformanceColumns);
const [dataObject, setDataObject] = React.useState<IEmiBucketWiseDataObject>();
const tableData = useSelector((state: RootState) => state.teleSensei.emiBucketWiseSummaryFilter);
const loading = useSelector((state: RootState) => state.teleSensei.loadingDespositionSummary);
const [data, setData] = React.useState<Array<any>>([]);
const dispatch = useDispatch();
const handleSort = (e: SortChangedEvent<IEMIBucketWiseSummary[]>) => {
if (e?.source === 'gridOptionsChanged') return;
const newParams = { ...params };
const sortModel = e?.columnApi?.getColumnState();
const sortedColumns = sortModel?.find(item => item.sort !== null);
if (!sortedColumns) {
delete newParams?.sortColumnDisposition;
delete newParams?.sortOrderDisposition;
newParams.pageNoDisposition = '1';
updateAndNavigate(newParams);
ref?.current?.api.paginationGoToPage(0);
return;
}
const { colId, sort } = sortedColumns;
newParams.sortColumnDisposition = colId;
newParams.sortOrderDisposition = sort as string;
newParams.pageNoDisposition = '1';
ref?.current?.api.paginationGoToPage(0);
updateAndNavigate(newParams);
};
useEffect(() => {
if (!params?.sortColumnDisposition) {
setColumnData(AgentPerformanceColumns);
}
const newColumnData = AgentPerformanceColumns.map(item => {
const children = item.children?.map(child => {
if (child.field === params?.sortColumnDisposition) {
return {
...child,
sort: params?.sortOrderDisposition as any
};
}
return child;
});
return {
...item,
children
};
});
setColumnData(newColumnData);
}, []);
const handleDownloadReport = () => {
ref?.current?.api?.exportDataAsCsv({
fileName: `EMI-Bucket-Wise-Summary-${dateFormat(new Date(), DateFormat.FILE_DATE_FORMAT)}`
});
};
const generateData = () => {
const allBucketWiseSummary = tableData?.flatMap(item => item.bucketWiseSummary);
const allData: IEmiBucketWiseDataObject = getRowObject();
const finalSummaryGroup = generateFinalSummaryGroup();
if (!allBucketWiseSummary.length) {
setData([]);
setDataObject(null);
return;
}
let coverageUnpaidCases4k = 0;
let coverageUnpaidCases7k = 0;
let coverageUnpaidCases10k = 0;
let coverageUnpaidCases4k7k = 0;
let avgUnpaidCases4k = 0;
let avgUnpaidCases7k = 0;
let avgUnpaidCases10k = 0;
let avgUnpaidCases4k7k = 0;
for (const item of allBucketWiseSummary) {
const { bucketGroup } = item;
let key: keyof typeof finalSummaryGroup;
switch (bucketGroup) {
case '<4k':
for (key in item) {
finalSummaryGroup['<4k'][key as IBuckerrWiseSummarySum] +=
item[key as keyof typeof item];
}
coverageUnpaidCases4k += item.unpaidCases ?? 0 * item.unpaidCasesCoverage ?? 0;
avgUnpaidCases4k += item.averageUniqueDaysCalledPerUnpaidCases * item.unpaidCases ?? 0;
break;
case '4k-7k':
for (key in item) {
finalSummaryGroup['4k-7k'][key as IBuckerrWiseSummarySum] +=
item[key as keyof typeof item];
}
coverageUnpaidCases4k7k += item.unpaidCases ?? 0 * item.unpaidCasesCoverage ?? 0;
avgUnpaidCases4k7k += item.averageUniqueDaysCalledPerUnpaidCases * item.unpaidCases ?? 0;
break;
case '7k-10k':
for (key in item) {
finalSummaryGroup['7k-10k'][key as IBuckerrWiseSummarySum] +=
item[key as keyof typeof item];
}
coverageUnpaidCases7k += item.unpaidCases ?? 0 * item.unpaidCasesCoverage ?? 0;
avgUnpaidCases7k += item.averageUniqueDaysCalledPerUnpaidCases * item.unpaidCases ?? 0;
break;
case '10k+':
for (key in item) {
finalSummaryGroup['10k+'][key as IBuckerrWiseSummarySum] +=
item[key as keyof typeof item];
}
coverageUnpaidCases10k += item.unpaidCases ?? 0 * item.unpaidCasesCoverage ?? 0;
avgUnpaidCases10k += item.averageUniqueDaysCalledPerUnpaidCases * item.unpaidCases ?? 0;
break;
default:
break;
}
}
for (const allDataKey in allData) {
for (const summaryGroupKey in SUMMARY_GROUP) {
const rightHandSideKey = allData[allDataKey as keyof typeof allData].key;
allData[allDataKey][SUMMARY_GROUP[summaryGroupKey]] =
finalSummaryGroup[SUMMARY_GROUP[summaryGroupKey]][rightHandSideKey];
}
}
// EMI CE % calculation
allData[emiBucketWiseRowsEnum.EMI_CE] = {
...allData[emiBucketWiseRowsEnum.EMI_CE],
'<4k': (finalSummaryGroup['<4k'].emiCollected / finalSummaryGroup['<4k'].emiAllocated) * 100,
'4k-7k':
(finalSummaryGroup['4k-7k'].emiCollected / finalSummaryGroup['4k-7k'].emiAllocated) * 100,
'7k-10k':
(finalSummaryGroup['7k-10k'].emiCollected / finalSummaryGroup['7k-10k'].emiAllocated) * 100,
'10k+':
(finalSummaryGroup['10k+'].emiCollected / finalSummaryGroup['10k+'].emiAllocated) * 100
};
//Calculation for coverage on unpaid cases
allData[emiBucketWiseRowsEnum.COVERAGE_UNPAID_CASES] = {
...allData[emiBucketWiseRowsEnum.COVERAGE_UNPAID_CASES],
'<4k':
(finalSummaryGroup['<4k'].unpaidCasesCovered / finalSummaryGroup['<4k'].unpaidCases) * 100,
'4k-7k':
(finalSummaryGroup['4k-7k'].unpaidCasesCovered / finalSummaryGroup['4k-7k'].unpaidCases) *
100,
'7k-10k':
(finalSummaryGroup['7k-10k'].unpaidCasesCovered / finalSummaryGroup['7k-10k'].unpaidCases) *
100,
'10k+':
(finalSummaryGroup['10k+'].unpaidCasesCovered / finalSummaryGroup['10k+'].unpaidCases) * 100
};
allData[emiBucketWiseRowsEnum.AVG_CALLS_UNPAID_CASES] = {
...allData[emiBucketWiseRowsEnum.AVG_CALLS_UNPAID_CASES],
'<4k': finalSummaryGroup['<4k'].totalCalls / coverageUnpaidCases4k,
'4k-7k': finalSummaryGroup['4k-7k'].totalCalls / coverageUnpaidCases4k7k,
'7k-10k': finalSummaryGroup['7k-10k'].totalCalls / coverageUnpaidCases7k,
'10k+': finalSummaryGroup['10k+'].totalCalls / coverageUnpaidCases10k
};
allData[emiBucketWiseRowsEnum.AVG_UNIQUE_DAYS_CALLED_UNPAID_CASES] = {
...allData[emiBucketWiseRowsEnum.AVG_UNIQUE_DAYS_CALLED_UNPAID_CASES],
'<4k': avgUnpaidCases4k / finalSummaryGroup['<4k'].unpaidCases,
'4k-7k': avgUnpaidCases4k7k / finalSummaryGroup['4k-7k'].unpaidCases,
'7k-10k': avgUnpaidCases7k / finalSummaryGroup['7k-10k'].unpaidCases,
'10k+': avgUnpaidCases10k / finalSummaryGroup['10k+'].unpaidCases
};
const totalCases = Object.values({ ...allData });
setData(totalCases);
setDataObject(allData);
};
useEffect(() => {
generateData();
}, [tableData]);
useEffect(() => {
ref?.current?.api?.sizeColumnsToFit();
}, [tableData, loading]);
return (
<div className={cx(styles.EmiBucketWiseSummary, 'agentDepositionSummary')}>
<div className={styles.tableContainer}>
<div className={styles.tableActions}>
<Typography color="var(--grayscale-2)" variant="h3">
EMI Bucket wise Summary
</Typography>
<Button
startAdornment={<Download />}
className={styles.downloadReport}
variant="text"
onClick={handleDownloadReport}
disabled={tableData.length === 0}
>
Download Report
</Button>
</div>
<div className={styles.dataContainer}>
<div className={styles.tableContainer}>
<AgTable
ref={ref}
columnDefs={columnData}
style={{
height: 899.5
}}
headerHeight={70}
defaultColDef={{
suppressMovable: true,
wrapText: true,
autoHeight: true
}}
theme={'alpine'}
rowData={data}
onSortChanged={handleSort}
pagination
suppressPaginationPanel={true}
paginationWrapperClasses="paginationWrapperClasses"
animateRows
sizeColumnsToFit
suppressRowClickSelection={true}
suppressRowDrag={false}
alternateRowColor="var(--greyscale-content-4)"
getRowHeight={params => params.data?.rowHeight}
/>
{!data.length ? <NoDataFound className={styles.noDataFound} /> : null}
</div>
<div className={styles.fb32}>
<NumberOfCases
dataTObeFed={data.find(i => i.key === 'totalCases')}
casesData={data.find(i => i.key === 'totalCases')}
/>
<CallsOverView
data={dataObject}
updateAndNavigate={updateAndNavigate}
params={params}
/>
<PTPOverView data={dataObject} updateAndNavigate={updateAndNavigate} params={params} />
</div>
</div>
</div>
<Loader show={loading} className={'loadingState'} animate={false} />
</div>
);
};
export default Index;

View File

@@ -4,7 +4,6 @@
justify-content: space-between;
align-items: center;
padding: 16px;
border-radius: 0px 0px 32px 32px;
box-shadow: 0px 14px 38px 0px rgba(125, 125, 125, 0.08);
position: sticky;
top: 0;

View File

@@ -1,3 +1,6 @@
import { SelectPickerOptionProps } from '@cp/src/components/interfaces';
import { IAllDataBucketWiseSummary } from '../types';
export enum ViewType {
TEAM_LEAD = 'TEAM_LEAD',
SENIOR_TEAM_LEAD = 'SENIOR_TEAM_LEAD'
@@ -12,5 +15,500 @@ export const TABLE_DEFAULT_VALUES = {
page: '1',
orderBy: 'name',
order: 'asc',
goToStart: 0
goToStart: 0,
depositionSummaryPTPOverViewFilter: '<4k',
depositionSummaryCallOverViewFilter: '<4k'
};
export enum summaryGroup {
'<4k' = 'Less than 4k',
'4k-7k' = '4k to 7k',
'10k+' = 'More than 10k',
'7k-10k' = '7k to 10k'
}
export enum summaryGroupColorMapping {
'<4k' = '#FFAD1E',
'4k-7k' = '#FA896B',
'10k+' = '#5D87FF',
'7k-10k' = '#6FE8C8'
}
export enum emiBucketWiseRowsEnum {
EMI_ALLOCATED = 'EMI_ALLOCATED',
EMI_CE = 'EMI_CE',
TOTAL_CASES = 'TOTAL_CASES',
UNPAID_CASES = 'UNPAID_CASES',
TOTAL_CALLS = 'TOTAL_CALLS',
VIA_LONGHORN = 'VIA_LONGHORN',
VIA_AMEYO = 'VIA_AMEYO',
VIA_SELF = 'VIA_SELF',
COVERAGE_UNPAID_CASES = 'COVERAGE_UNPAID_CASES',
AVG_CALLS_UNPAID_CASES = 'AVG_CALLS_UNPAID_CASES',
AVG_UNIQUE_DAYS_CALLED_UNPAID_CASES = 'AVG_UNIQUE_DAYS_CALLED_UNPAID_CASES',
PTP_GENERATED = 'PTP_GENERATED',
PTP_CONVERTED = 'PTP_CONVERTED',
BROKEN_PTP = 'BROKEN_PTP',
FUTURE_PTP = 'FUTURE_PTP'
}
export enum emiBucketWiseRowsReadableNameEnum {
EMI_ALLOCATED = 'EMI ALLOCATED',
EMI_CE = 'EMI CE',
TOTAL_CASES = 'TOTAL CASES',
UNPAID_CASES = 'UNPAID CASES',
TOTAL_CALLS = 'TOTAL CALLS',
VIA_LONGHORN = 'VIA LONGHORN',
VIA_AMEYO = 'VIA AMEYO',
VIA_SELF = 'VIA SELF',
COVERAGE_UNPAID_CASES = 'COVERAGE UNPAID CASES',
AVG_CALLS_UNPAID_CASES = 'AVG CALLS UNPAID CASES',
AVG_UNIQUE_DAYS_CALLED_UNPAID_CASES = 'AVG UNIQUE DAYS CALLED UNPAID CASES',
PTP_GENERATED = 'PTP GENERATED',
PTP_CONVERTED = 'PTP CONVERTED',
BROKEN_PTP = 'BROKEN PTP',
FUTURE_PTP = 'FUTURE PTP'
}
export const emiBucketWiseData = [
{
name: emiBucketWiseRowsEnum.EMI_ALLOCATED,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'emiAllocated'
},
{
name: emiBucketWiseRowsEnum.EMI_CE,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'emiCollectionEfficiency'
},
{
name: emiBucketWiseRowsEnum.UNPAID_CASES,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'unpaidCases'
},
{
name: emiBucketWiseRowsEnum.TOTAL_CALLS,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'totalCalls'
},
{
name: emiBucketWiseRowsEnum.VIA_LONGHORN,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'slashCalls'
},
{
name: emiBucketWiseRowsEnum.VIA_AMEYO,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'ameyoCalls'
},
{
name: emiBucketWiseRowsEnum.VIA_SELF,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
selfCalls: 'selfCalls'
},
{
name: emiBucketWiseRowsEnum.COVERAGE_UNPAID_CASES,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'unpaidCasesCoverage'
},
{
name: emiBucketWiseRowsEnum.AVG_CALLS_UNPAID_CASES,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'averageCallsPerUnpaidCases'
},
{
name: emiBucketWiseRowsEnum.AVG_UNIQUE_DAYS_CALLED_UNPAID_CASES,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'averageUniqueDaysCalledPerUnpaidCases'
},
{
name: emiBucketWiseRowsEnum.PTP_GENERATED,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'generatedPtpCount'
},
{
name: emiBucketWiseRowsEnum.PTP_CONVERTED,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'convertedPtpCount'
},
{
name: emiBucketWiseRowsEnum.BROKEN_PTP,
lessThan4k: 0,
between4k7k: 0,
between7k10k: 0,
greaterThan10k: 0,
key: 'brokenPtpCount'
}
];
export const emiBucketWiseRows = [
'EMI Allocated',
'EMI CE %',
'% Unpaid cases',
'Total Calls',
'Via Longhorn',
'Via Ameyo',
'Via Self',
'Coverage % Unpaid cases',
'Avg calls/Unpaid cases',
'Avg Unique Days Called/Unpaid cases',
'% PTP Generated',
'% PTP Converted',
'% Broken PTP'
];
// create emiBucketWiseData object with keys as enum values
export type IEmiBucketWiseDataObject = typeof emiBucketWiseDataObject;
export const emiBucketWiseDataObject = {
[emiBucketWiseRowsEnum.EMI_ALLOCATED]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'emiAllocated',
name: 'EMI Allocated'
},
[emiBucketWiseRowsEnum.EMI_CE]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'emiCollectionEfficiency',
name: 'EMI CE %'
},
[emiBucketWiseRowsEnum.TOTAL_CASES]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'totalCases',
name: 'Total Cases',
bold: true
},
[emiBucketWiseRowsEnum.UNPAID_CASES]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'unpaidCases',
name: 'Unpaid Cases'
},
[emiBucketWiseRowsEnum.TOTAL_CALLS]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'totalCalls',
name: 'Total Calls',
bold: true
},
[emiBucketWiseRowsEnum.VIA_LONGHORN]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'slashCalls',
name: 'Via Longhorn',
subPoint: true,
blue: true
},
[emiBucketWiseRowsEnum.VIA_AMEYO]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'ameyoCalls',
name: 'Via Ameyo',
subPoint: true,
blue: true
},
[emiBucketWiseRowsEnum.VIA_SELF]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'selfCalls',
name: 'Via Self',
subPoint: true,
blue: true
},
[emiBucketWiseRowsEnum.COVERAGE_UNPAID_CASES]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'unpaidCasesCoverage',
name: 'Coverage on Unpaid Cases %'
},
[emiBucketWiseRowsEnum.AVG_CALLS_UNPAID_CASES]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'averageCallsPerUnpaidCases',
name: 'Avg Calls/Unpaid Cases'
},
[emiBucketWiseRowsEnum.AVG_UNIQUE_DAYS_CALLED_UNPAID_CASES]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'averageUniqueDaysCalledPerUnpaidCases',
name: 'Avg Unique Days Called/Unpaid Cases'
},
[emiBucketWiseRowsEnum.PTP_GENERATED]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'generatedPtpCount',
name: 'PTP Generated',
bold: true
},
[emiBucketWiseRowsEnum.PTP_CONVERTED]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'convertedPtpCount',
name: 'PTP Converted',
subPoint: true
},
[emiBucketWiseRowsEnum.BROKEN_PTP]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'brokenPtpCount',
name: 'Broken PTP',
subPoint: true
},
[emiBucketWiseRowsEnum.FUTURE_PTP]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'futurePtpCount',
name: 'Future PTP',
subPoint: true
}
};
export const getRowObject = () => {
return {
[emiBucketWiseRowsEnum.EMI_ALLOCATED]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'emiAllocated',
name: 'EMI Allocated',
isCurrency: true
},
[emiBucketWiseRowsEnum.EMI_CE]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'emiCollectionEfficiency',
name: 'EMI CE %',
percentage: true
},
[emiBucketWiseRowsEnum.TOTAL_CASES]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'totalCases',
name: 'Total Cases',
bold: true
},
[emiBucketWiseRowsEnum.UNPAID_CASES]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'unpaidCases',
name: 'Unpaid Cases'
},
[emiBucketWiseRowsEnum.TOTAL_CALLS]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'totalCalls',
name: 'Total Calls',
bold: true
},
[emiBucketWiseRowsEnum.VIA_LONGHORN]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'slashCalls',
name: 'Via Longhorn',
subPoint: true,
blue: true,
rowHeight: 42
},
[emiBucketWiseRowsEnum.VIA_AMEYO]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'ameyoCalls',
name: 'Via Ameyo',
subPoint: true,
blue: true,
rowHeight: 42
},
[emiBucketWiseRowsEnum.VIA_SELF]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'selfCalls',
name: 'Via Self',
subPoint: true,
blue: true,
rowHeight: 42
},
[emiBucketWiseRowsEnum.COVERAGE_UNPAID_CASES]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'unpaidCasesCoverage',
name: 'Coverage on Unpaid Cases %',
blueBackground: true,
percentage: true
},
[emiBucketWiseRowsEnum.AVG_CALLS_UNPAID_CASES]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'averageCallsPerUnpaidCases',
name: 'Avg Calls/Unpaid Cases'
},
[emiBucketWiseRowsEnum.AVG_UNIQUE_DAYS_CALLED_UNPAID_CASES]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'averageUniqueDaysCalledPerUnpaidCases',
name: 'Avg Unique Days Called/Unpaid Cases'
},
[emiBucketWiseRowsEnum.PTP_GENERATED]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'generatedPtpCount',
name: 'PTP Generated',
bold: true
},
[emiBucketWiseRowsEnum.PTP_CONVERTED]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'convertedPtpCount',
name: 'PTP Converted',
subPoint: true,
blue: true,
rowHeight: 42
},
[emiBucketWiseRowsEnum.BROKEN_PTP]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'brokenPtpCount',
name: 'Broken PTP',
subPoint: true,
blue: true,
rowHeight: 42
},
[emiBucketWiseRowsEnum.FUTURE_PTP]: {
'<4k': 0,
'4k-7k': 0,
'7k-10k': 0,
'10k+': 0,
key: 'futurePtpCount',
name: 'Future PTP',
subPoint: true,
blue: true,
rowHeight: 42
}
};
};
export const DROPDOWN_OPTIONS = [
{ value: '<4k', label: 'Less than 4k' },
{ value: '4k-7k', label: '4k to 7k' },
{ value: '7k-10k', label: '7k to 10k' },
{ value: '10k+', label: 'More than 10k' }
] as SelectPickerOptionProps[];
export const SUMMARY_GROUP = ['<4k', '4k-7k', '10k+', '7k-10k'];
export const generateFinalSummaryGroup = () => {
const finalSummaryGroup: IAllDataBucketWiseSummary = {};
for (const key of SUMMARY_GROUP) {
finalSummaryGroup[key as string] = {
emiAllocated: 0,
emiCollectionEfficiency: 0,
totalCases: 0,
unpaidCases: 0,
totalCalls: 0,
slashCalls: 0,
ameyoCalls: 0,
selfCalls: 0,
unpaidCasesCoverage: 0,
averageCallsPerUnpaidCases: 0,
averageUniqueDaysCalledPerUnpaidCases: 0,
generatedPtpCount: 0,
convertedPtpCount: 0,
brokenPtpCount: 0,
futurePtpCount: 0,
emiCollected: 0,
unpaidCasesCovered: 0
};
}
return finalSummaryGroup;
};

View File

@@ -1,10 +1,21 @@
import React, { useEffect } from 'react';
import Filter from './components/filters';
import AgentPerformance from './components/agentPerformance';
import { getAgentInputData, getAgentPerformanceData, getDespositionSummary } from './action';
import {
getAgentInputData,
getAgentPerformanceData,
getDespositionSummary,
getEMIBucketWiseSummary
} from './action';
import { useDispatch, useSelector } from 'react-redux';
import { createQueryParams, readQueryParams } from '@cp/src/utils/QueryParamsHelper';
import { IAgent, IAgentPerformance, IAgentPerformanceParams, IDepositionSummary } from './types';
import {
IAgent,
IAgentPerformance,
IAgentPerformanceParams,
IDepositionSummary,
IEMIBucketWiseSummary
} from './types';
import { AGENT_PERFORMANCE_TABLE, TABLE_DEFAULT_VALUES, pollingInterval } from './constants';
import { useLocation, useNavigate } from 'react-router';
import AgentInput from './components/agentInput';
@@ -13,6 +24,7 @@ import store, { RootState } from '@cp/src/store';
import {
setAgentPerformanceFiltered,
setDespositionSummaryFilter,
setEmiBucketWiseSummaryFilter,
setFilteredData
} from './reducer';
import { poll } from '@cp/src/utils/polling';
@@ -25,16 +37,22 @@ import { Typography } from '@navi/web-ui/lib/primitives';
import AgentInputIcon from '@cp/src/assets/icons/AgentInputIcon';
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@cp/src/service/clickStream.constant';
import EmiBucketSummary from './components/emiBucketWiseSummary';
const Index = () => {
const dispatch = useDispatch();
const { loading, loadingAgentPerformance, agentData, IAgentPerformance, despositionSummary } =
useSelector((state: RootState) => state.teleSensei);
const {
loading,
loadingAgentPerformance,
agentData,
IAgentPerformance,
despositionSummary,
emiBucketWiseSummary
} = useSelector((state: RootState) => state.teleSensei);
const location = useLocation();
const { [AGENT_PERFORMANCE_TABLE]: params } = readQueryParams();
const navigate = useNavigate();
const unsubscribeRef = React.useRef<() => void>();
const updateAndNavigate = (updatedParams: IAgentPerformanceParams) => {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_TELE_SENSEI_FILTER_APPLY, { updatedParams });
const url = createQueryParams({
@@ -47,6 +65,7 @@ const Index = () => {
dispatch(getAgentInputData());
dispatch(getAgentPerformanceData());
dispatch(getDespositionSummary());
dispatch(getEMIBucketWiseSummary());
}, []);
useEffect(() => {
@@ -65,10 +84,18 @@ const Index = () => {
queryParams['pageSizeInput'] = TABLE_DEFAULT_VALUES.rowsPerPage;
}
if (!queryParams?.pageNoDisposition) {
queryParams['pageNoDisposition'] = '1';
queryParams['pageNoDisposition'] = TABLE_DEFAULT_VALUES.page;
}
if (!queryParams?.pageSizeDisposition) {
queryParams['pageSizeDisposition'] = '10';
queryParams['pageSizeDisposition'] = TABLE_DEFAULT_VALUES.rowsPerPage;
}
if (!queryParams?.depositionSummaryCallOverViewFilter) {
queryParams['depositionSummaryCallOverViewFilter'] =
TABLE_DEFAULT_VALUES.depositionSummaryCallOverViewFilter;
}
if (!queryParams?.depositionSummaryPTPOverViewFilter) {
queryParams['depositionSummaryPTPOverViewFilter'] =
TABLE_DEFAULT_VALUES.depositionSummaryPTPOverViewFilter;
}
updateAndNavigate(queryParams);
}, []);
@@ -79,6 +106,7 @@ const Index = () => {
let filteredAgentData: IAgent[] = [];
let filteredAgentInput: IAgentPerformance[] = [];
let filteredDespositionData: IDepositionSummary[] = [];
let filteredEmiBucketData: IEMIBucketWiseSummary[] = [];
// filtering this out if both the filter selected
if (params?.teamLeadId && params?.agentId) {
const dataAfterTLFilter = agentData?.filter(
@@ -107,6 +135,15 @@ const Index = () => {
);
dispatch(setDespositionSummaryFilter(filteredDespositionData));
const dataAfterTLFilterEmiBucket = emiBucketWiseSummary?.filter(
agentData => agentData?.tlReferenceId === params?.teamLeadId
);
filteredEmiBucketData = dataAfterTLFilterEmiBucket?.filter(agentData =>
selectedAgents?.some((agentID: string) => agentData?.agentReferenceId === agentID)
);
dispatch(setEmiBucketWiseSummaryFilter(filteredEmiBucketData));
return;
}
if (params?.teamLeadId && !params?.agentId) {
@@ -124,6 +161,11 @@ const Index = () => {
agentData => agentData?.tlReferenceId === params?.teamLeadId
);
dispatch(setDespositionSummaryFilter(filteredDespositionData));
filteredEmiBucketData = emiBucketWiseSummary?.filter(
agentData => agentData?.tlReferenceId === params?.teamLeadId
);
dispatch(setEmiBucketWiseSummaryFilter(filteredEmiBucketData));
return;
}
if (!params?.teamLeadId && params?.agentId) {
@@ -142,6 +184,11 @@ const Index = () => {
selectedAgents?.some((agentID: string) => agentData?.agentReferenceId === agentID)
);
dispatch(setDespositionSummaryFilter(filteredDespositionData));
filteredEmiBucketData = emiBucketWiseSummary?.filter(agentData =>
selectedAgents?.some((agentID: string) => agentData?.agentReferenceId === agentID)
);
dispatch(setEmiBucketWiseSummaryFilter(filteredEmiBucketData));
return;
}
if (!params?.teamLeadId && !params?.agentId) {
@@ -153,9 +200,12 @@ const Index = () => {
filteredDespositionData = despositionSummary;
dispatch(setDespositionSummaryFilter(filteredDespositionData));
filteredEmiBucketData = emiBucketWiseSummary;
dispatch(setEmiBucketWiseSummaryFilter(filteredEmiBucketData));
return;
}
}, [location, agentData, IAgentPerformance, despositionSummary]);
}, [location, agentData, IAgentPerformance, despositionSummary, emiBucketWiseSummary]);
return (
<div>
@@ -175,6 +225,11 @@ const Index = () => {
updateAndNavigate={updateAndNavigate}
url={location.hash}
/>
<EmiBucketSummary
params={params}
updateAndNavigate={updateAndNavigate}
url={location.hash}
/>
</div>
</div>
<Loader show={loading || loadingAgentPerformance} />

View File

@@ -17,7 +17,10 @@ const initialState: IAgentData = {
lastRefreshedTimeStampInput: '',
despositionSummary: [],
loadingDespositionSummary: false,
despositionSummaryFilter: []
despositionSummaryFilter: [],
emiBucketWiseSummary: [],
loadingEmiBucketWiseSummary: false,
emiBucketWiseSummaryFilter: []
};
const TeleSenseiSlice = createSlice({
@@ -58,6 +61,15 @@ const TeleSenseiSlice = createSlice({
setDespositionSummary(state, action) {
const { data } = action.payload;
state.despositionSummary = data;
},
setEmiBucketWiseSummary(state, action) {
state.emiBucketWiseSummary = action.payload;
},
setLoadingEmiBucketWiseSummary(state, action) {
state.loadingEmiBucketWiseSummary = action.payload;
},
setEmiBucketWiseSummaryFilter(state, action) {
state.emiBucketWiseSummaryFilter = action.payload;
}
}
});
@@ -71,7 +83,10 @@ export const {
setAgentPerformanceFiltered,
setDespositionSummaryFilter,
setLoadingDespositionSummary,
setDespositionSummary
setDespositionSummary,
setEmiBucketWiseSummary,
setEmiBucketWiseSummaryFilter,
setLoadingEmiBucketWiseSummary
} = TeleSenseiSlice.actions;
export default TeleSenseiSlice.reducer;

View File

@@ -1,4 +1,5 @@
import { ViewType } from '../constants';
import { A } from 'msw/lib/core/RequestHandler-bb5cbb8f';
import { IEmiBucketWiseDataObject, ViewType } from '../constants';
export type IViewType = keyof typeof ViewType | null;
@@ -18,6 +19,10 @@ export interface IAgentPerformanceParams {
sortOrderDisposition?: string;
pageNoDisposition?: string;
pageSizeDisposition?: string;
depositionSummaryCallOverViewFilter?: string;
depositionSummaryPTPOverViewFilter?: string;
unpaidCasesCovered?: number;
unpaidCases?: number;
}
export interface IFilters {
@@ -68,6 +73,9 @@ export interface IAgentData {
despositionSummary: IDepositionSummary[];
loadingDespositionSummary: boolean;
despositionSummaryFilter: IDepositionSummary[];
emiBucketWiseSummary: IEMIBucketWiseSummary[];
loadingEmiBucketWiseSummary: boolean;
emiBucketWiseSummaryFilter: IEMIBucketWiseSummary[];
}
export enum FilterType {
@@ -115,3 +123,79 @@ export interface IDepositionSummary {
ptpTodayCount: number;
ptpTodayEmi: number;
}
export interface IEMIBucketWiseSummary {
agentName: string;
agentReferenceId: string;
tlReferenceId: string;
dueCycle: string;
bucketWiseSummary: IBucketWiseSummary[];
}
interface IBucketWiseSummary {
bucketGroup: string;
emiAllocated: number;
emiCollectionEfficiency: number;
totalCases: number;
unpaidCases: number;
totalCalls: number;
slashCalls: number;
ameyoCalls: number;
selfCalls: number;
unpaidCasesCoverage: number;
averageCallsPerUnpaidCases: number;
averageUniqueDaysCalledPerUnpaidCases: number;
generatedPtpCount: number;
convertedPtpCount: number;
brokenPtpCount: number;
futurePtpCount: number;
emiCollected: number;
}
export interface IAllDataBucketWiseSummary {
[key: string]: IBuckerrWiseSummarySum;
}
export interface IBuckerrWiseSummarySum {
emiAllocated: number;
emiCollectionEfficiency: number;
totalCases: number;
unpaidCases: number;
totalCalls: number;
slashCalls: number;
ameyoCalls: number;
selfCalls: number;
unpaidCasesCoverage: number;
averageCallsPerUnpaidCases: number;
averageUniqueDaysCalledPerUnpaidCases: number;
generatedPtpCount: number;
convertedPtpCount: number;
brokenPtpCount: number;
futurePtpCount: number;
emiCollected: number;
unpaidCasesCovered: number;
}
export interface ICallsOverView {
data: IEmiBucketWiseDataObject;
params: IAgentPerformanceParams;
updateAndNavigate: (params: IAgentPerformanceParams) => void;
}
export interface CashEarnedChartHCProps {
dataTObeFed: Array<responseDataType>;
casesData: Array<responseDataType>;
}
export type responseDataType = {
label: string;
value: string;
fill: string;
color: string;
};
export interface IPTPOverView {
data: IEmiBucketWiseDataObject;
params: IAgentPerformanceParams;
updateAndNavigate: (params: IAgentPerformanceParams) => void;
}

View File

@@ -71,7 +71,8 @@ export enum Roles {
NAVI_INHOUSE_FIELD_ZONAL_MANAGER = 'NAVI_INHOUSE_FIELD_ZONAL_MANAGER',
ROLE_NAVI_FIELD_AGENCY_TEAM_LEAD = 'ROLE_NAVI_FIELD_AGENCY_TEAM_LEAD',
ROLE_HUMAN_REMINDER_TEAM_LEAD = 'ROLE_HUMAN_REMINDER_TEAM_LEAD',
ROLE_HUMAN_REMINDER_CHAT_AGENT = 'ROLE_HUMAN_REMINDER_CHAT_AGENT'
ROLE_HUMAN_REMINDER_CHAT_AGENT = 'ROLE_HUMAN_REMINDER_CHAT_AGENT',
ROLE_NAVI_TELE_INHOUSE_TEAM_LEAD = 'ROLE_NAVI_TELE_INHOUSE_TEAM_LEAD'
}
export const permissionScreenConstants = {

View File

@@ -167,7 +167,8 @@ export enum ApiKeys {
GET_AGENCY_NAME_OPTIONS,
GET_AGENCY_DETAILS_DATA,
GET_AGENT_PERFORMANCE_DATA,
GET_AGENT_PERFORMANCE_TL_DATA
GET_AGENT_PERFORMANCE_TL_DATA,
EMI_BUCKET_WISE_SUMMARY
}
// TODO: try to get rid of `as`
@@ -325,6 +326,8 @@ API_URLS[ApiKeys.GET_AGENT_PERFORMANCE_DATA] =
'/longhorn/external-agency-performance-dashboard/field-governance/agent';
API_URLS[ApiKeys.GET_AGENT_PERFORMANCE_TL_DATA] =
'/longhorn/external-agency-performance-dashboard/overall-performance/agent-performance';
API_URLS[ApiKeys.EMI_BUCKET_WISE_SUMMARY] =
'/team-lead/dashboard/reportees-emi-bucket-wise-summary';
// TODO: try to get rid of `as`
const MOCK_API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;