Tele sensei v2 (#778)
* TP-42467 |agent performance v2| Aman Singh * TP-42467 |lint issue fixed| Aman Singh * TP-42467 |lint issue fixed| Aman Singh * TP-42467 |lint issue fixed| Aman Singh * TP-42467 |lint issue fixed| Aman Singh * TP-42467 |fixed design issues| Aman Singh * TP-42467 |fixed design issues| Aman Singh * TP-42467 |api tested on local | Aman Singh * TP-42467 |prettier issue fixed| Aman Singh
This commit is contained in:
@@ -5,4 +5,5 @@
|
||||
-webkit-box-orient: vertical;
|
||||
line-height: 24px;
|
||||
width: fit-content;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
64
src/components/TableCellRenderer/CellRendererShortNumber.tsx
Normal file
64
src/components/TableCellRenderer/CellRendererShortNumber.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import EllipsisText from '@cp/src/components/EllipsisText';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@cp/src/components/TooltipV2/TooltipV2';
|
||||
import { formatNumber, shortNumberNotation } from '@cp/src/utils/commonUtils';
|
||||
import React from 'react';
|
||||
|
||||
interface ICellRendererShortNumber {
|
||||
value: number;
|
||||
lineClamp?: number;
|
||||
hiddenPadding?: number;
|
||||
significantDigits?: number;
|
||||
isCurrency?: boolean;
|
||||
}
|
||||
|
||||
const CellRendererShortNumber: React.FC<ICellRendererShortNumber> = props => {
|
||||
const {
|
||||
value,
|
||||
lineClamp = 1,
|
||||
hiddenPadding = 10,
|
||||
significantDigits = 1,
|
||||
isCurrency = false
|
||||
} = props;
|
||||
|
||||
if (!Number.isFinite(value)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('CellRendererShortNumber: value is not a number', typeof value);
|
||||
}
|
||||
|
||||
if (!value && value !== 0) {
|
||||
return <EllipsisText text={'-'} lineClamp={lineClamp} />;
|
||||
}
|
||||
|
||||
let shorthandNumber = value == 0 ? '0' : shortNumberNotation(value, significantDigits);
|
||||
|
||||
let toolTipContent = formatNumber(value, true);
|
||||
|
||||
if (String(value) === shorthandNumber) {
|
||||
if (isCurrency) {
|
||||
shorthandNumber = `₹${shorthandNumber}`;
|
||||
}
|
||||
return <EllipsisText text={shorthandNumber ?? '-'} lineClamp={lineClamp} />;
|
||||
}
|
||||
|
||||
if (isCurrency) {
|
||||
shorthandNumber = `₹${shorthandNumber}`;
|
||||
toolTipContent = `₹${toolTipContent}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip placement="top" hiddenPadding={hiddenPadding} hideStrategy="referenceHidden">
|
||||
<TooltipTrigger tooltipTriggerClassName="tooltipTriggerWrapper">
|
||||
<EllipsisText text={shorthandNumber ?? '-'} lineClamp={lineClamp} />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
className="tooltipWrapper"
|
||||
arrowColor="var(--tooltip-background-color)"
|
||||
bgColor="var(--tooltip-background-color)"
|
||||
>
|
||||
{toolTipContent}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default CellRendererShortNumber;
|
||||
@@ -496,7 +496,7 @@ function SideNavBar({ isDc97User, isHRCChatUser }: ISideNavbarProps) {
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{isTeamLead ? (
|
||||
{isInternalTeamLead ? (
|
||||
<>
|
||||
<SidebarLinks
|
||||
key={APP_ROUTES.HR_DASHBOARD.path}
|
||||
|
||||
@@ -3,8 +3,10 @@ import { Dispatch } from 'redux';
|
||||
import {
|
||||
setAgentPerformance,
|
||||
setApiResponse,
|
||||
setDespositionSummary,
|
||||
setLoading,
|
||||
setLoadingAgentPerformance
|
||||
setLoadingAgentPerformance,
|
||||
setLoadingDespositionSummary
|
||||
} from '../reducer';
|
||||
|
||||
export const getAgentPerformanceData = () => (dispatch: Dispatch) => {
|
||||
@@ -44,3 +46,19 @@ export const getAgentInputData = () => (dispatch: Dispatch) => {
|
||||
dispatch(setLoadingAgentPerformance(false));
|
||||
});
|
||||
};
|
||||
|
||||
export const getDespositionSummary = () => (dispatch: Dispatch) => {
|
||||
dispatch(setLoadingDespositionSummary(true));
|
||||
const url = getApiUrl(ApiKeys.GET_DEPOSITIONS);
|
||||
axiosInstance
|
||||
.get(url)
|
||||
.then(response => {
|
||||
dispatch(setDespositionSummary(response.data));
|
||||
})
|
||||
.catch(error => {
|
||||
logError(error, 'getAgentInputData');
|
||||
})
|
||||
.finally(() => {
|
||||
dispatch(setLoadingDespositionSummary(false));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,6 +3,9 @@ import { ICellRendererParams } from 'ag-grid-community';
|
||||
import cx from 'classnames';
|
||||
import RenderWithToolTip from '../renderWithTooltip';
|
||||
import styles from './index.module.scss';
|
||||
import TableCellRenderer from '@cp/src/components/TableCellRenderer';
|
||||
import CellRendererShortNumber from '@cp/src/components/TableCellRenderer/CellRendererShortNumber';
|
||||
import { Cell } from 'recharts';
|
||||
|
||||
export const AgentPerformanceColumns = [
|
||||
{
|
||||
@@ -47,13 +50,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { allocatedCases } = params.data;
|
||||
if (!allocatedCases) return <>-</>;
|
||||
const value = formatNumber(allocatedCases, true);
|
||||
return (
|
||||
<RenderWithToolTip
|
||||
value={shortNumberNotation(allocatedCases, 1)}
|
||||
toolTipContent={value}
|
||||
/>
|
||||
);
|
||||
return <CellRendererShortNumber value={allocatedCases} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -66,10 +63,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { unpaidCases } = params.data;
|
||||
if (!unpaidCases) return <>-</>;
|
||||
const value = formatNumber(unpaidCases, true);
|
||||
return (
|
||||
<RenderWithToolTip value={shortNumberNotation(unpaidCases, 1)} toolTipContent={value} />
|
||||
);
|
||||
return <CellRendererShortNumber value={unpaidCases} />;
|
||||
}
|
||||
},
|
||||
// {
|
||||
@@ -108,7 +102,7 @@ export const AgentPerformanceColumns = [
|
||||
const timeInHoursAndMinutes = `${hours ? `${hours} min` : ''} ${Math.floor(
|
||||
averageTalkTimePerCall % 60
|
||||
)} sec`;
|
||||
return <>{timeInHoursAndMinutes}</>;
|
||||
return <TableCellRenderer value={timeInHoursAndMinutes} />;
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -125,12 +119,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { totalCalls } = params.data;
|
||||
if (!totalCalls) return <>-</>;
|
||||
return (
|
||||
<RenderWithToolTip
|
||||
value={shortNumberNotation(totalCalls, 1)}
|
||||
toolTipContent={formatNumber(totalCalls, true)}
|
||||
/>
|
||||
);
|
||||
return <CellRendererShortNumber value={totalCalls} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -144,12 +133,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { longhornCalls } = params.data;
|
||||
if (!longhornCalls) return <>-</>;
|
||||
return (
|
||||
<RenderWithToolTip
|
||||
value={shortNumberNotation(longhornCalls, 1)}
|
||||
toolTipContent={formatNumber(longhornCalls, true)}
|
||||
/>
|
||||
);
|
||||
return <CellRendererShortNumber value={longhornCalls} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -163,12 +147,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { ameyoCalls } = params.data;
|
||||
if (!ameyoCalls) return <>-</>;
|
||||
return (
|
||||
<RenderWithToolTip
|
||||
value={shortNumberNotation(ameyoCalls, 1)}
|
||||
toolTipContent={formatNumber(ameyoCalls, true)}
|
||||
/>
|
||||
);
|
||||
return <CellRendererShortNumber value={ameyoCalls} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -182,12 +161,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { selfCalls } = params.data;
|
||||
if (!selfCalls) return <>-</>;
|
||||
return (
|
||||
<RenderWithToolTip
|
||||
value={shortNumberNotation(selfCalls, 1)}
|
||||
toolTipContent={formatNumber(selfCalls, true)}
|
||||
/>
|
||||
);
|
||||
return <CellRendererShortNumber value={selfCalls} />;
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -206,7 +180,7 @@ export const AgentPerformanceColumns = [
|
||||
const { coverageOfUnpaidCases } = params.data;
|
||||
if (!coverageOfUnpaidCases) return <>-</>;
|
||||
const formattedPercent = shortNumberNotation(coverageOfUnpaidCases) + '%';
|
||||
return <>{formattedPercent}</>;
|
||||
return <TableCellRenderer value={formattedPercent} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -221,7 +195,7 @@ export const AgentPerformanceColumns = [
|
||||
const { avgAttemptPerUnpaidCase } = params.data;
|
||||
if (!avgAttemptPerUnpaidCase) return <>-</>;
|
||||
const number = formatNumber(avgAttemptPerUnpaidCase, true);
|
||||
return <>{number}</>;
|
||||
return <TableCellRenderer value={number} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -235,7 +209,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { lanConnectivity } = params.data;
|
||||
if (!lanConnectivity) return <>-</>;
|
||||
return <div className={cx(styles.nameField)}>{Number(lanConnectivity).toFixed(2)}%</div>;
|
||||
return <TableCellRenderer value={`${Number(lanConnectivity).toFixed(2)}%`} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -250,7 +224,7 @@ export const AgentPerformanceColumns = [
|
||||
const { averageUniqueDaysCalledPerCase } = params.data;
|
||||
if (!averageUniqueDaysCalledPerCase) return <>-</>;
|
||||
const number = formatNumber(averageUniqueDaysCalledPerCase, true);
|
||||
return <>{number}</>;
|
||||
return <TableCellRenderer value={number} />;
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,31 +1,11 @@
|
||||
.agentTableContainer {
|
||||
margin-top: 32px;
|
||||
.agentInputTableContainer {
|
||||
position: relative;
|
||||
|
||||
.headerContainer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-right: 32px;
|
||||
|
||||
.header {
|
||||
margin-left: 104px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 32px;
|
||||
}
|
||||
|
||||
.tableContainer {
|
||||
background: linear-gradient(90deg, #fff8f0 0.32%, rgba(255, 250, 245, 0.24) 99.68%);
|
||||
padding: 48px 32px 100px 32px;
|
||||
|
||||
.downloadReport {
|
||||
position: absolute;
|
||||
top: 42px;
|
||||
right: 37px;
|
||||
.tableActions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +15,7 @@
|
||||
}
|
||||
|
||||
:global {
|
||||
.agentInput {
|
||||
.agentInputTable {
|
||||
.ag-header-viewport {
|
||||
.ag-header-row-column-group {
|
||||
> div:nth-child(1) {
|
||||
@@ -53,20 +33,6 @@
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
background-color: var(--orange-oldlase);
|
||||
color: var(--orange-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;
|
||||
}
|
||||
|
||||
> div:nth-child(3) {
|
||||
background-color: var(--pale-yellow);
|
||||
color: var(--navi-color-yellow-dark);
|
||||
pointer-events: none;
|
||||
@@ -192,14 +158,13 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ag-cell-value {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.ag-floating-top {
|
||||
font-weight: 900;
|
||||
color: var(--navi-color-gray-c1) !important;
|
||||
border-bottom: 0px !important;
|
||||
.ag-row {
|
||||
background-color: var(--bg-primary) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ag-sort-indicator-icon {
|
||||
@@ -209,9 +174,11 @@
|
||||
.ag-row-last {
|
||||
border-bottom: var(--ag-borders-row) var(--ag-row-border-color) !important;
|
||||
}
|
||||
|
||||
.ag-header-cell-text {
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.ag-horizontal-left-spacer {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ const Index: React.FC<IFilters> = props => {
|
||||
const pinnedRow = useMemo(() => {
|
||||
if (tableData.length === 0) return [];
|
||||
const data: IAgentPerformance = {
|
||||
agentName: 'Overall Input',
|
||||
agentName: 'Overall',
|
||||
allocatedCases: 0,
|
||||
unpaidCases: 0,
|
||||
avgTalkTimePerCall: 0,
|
||||
@@ -155,24 +155,22 @@ const Index: React.FC<IFilters> = props => {
|
||||
}, [tableData]);
|
||||
|
||||
return (
|
||||
<div className={cx(styles.agentTableContainer, 'agentInput')}>
|
||||
<div className={styles.headerContainer}>
|
||||
<Typography className={styles.header} color="var(--navi-color-gray-c1)" variant="h2">
|
||||
Agent Input
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<AgentInputIcon className={styles.icon} />
|
||||
<div className={cx(styles.agentInputTableContainer, 'agentInputTable')}>
|
||||
<div className={styles.tableContainer}>
|
||||
<Button
|
||||
startAdornment={<Download />}
|
||||
className={styles.downloadReport}
|
||||
variant="text"
|
||||
onClick={handleDownloadReport}
|
||||
disabled={tableData.length === 0}
|
||||
>
|
||||
Download Report
|
||||
</Button>
|
||||
<div className={styles.tableActions}>
|
||||
<Typography color="var(--grayscale-2)" variant="h3">
|
||||
Agents Effort Summary
|
||||
</Typography>
|
||||
<Button
|
||||
startAdornment={<Download />}
|
||||
className={styles.downloadReport}
|
||||
variant="text"
|
||||
onClick={handleDownloadReport}
|
||||
disabled={tableData.length === 0}
|
||||
>
|
||||
Download Report
|
||||
</Button>
|
||||
</div>
|
||||
<AgTable
|
||||
ref={ref}
|
||||
columnDefs={columnData}
|
||||
@@ -207,6 +205,7 @@ const Index: React.FC<IFilters> = props => {
|
||||
}
|
||||
suppressRowClickSelection={true}
|
||||
suppressRowDrag={false}
|
||||
alternateRowColor="var(--greyscale-content-4)"
|
||||
/>
|
||||
</div>
|
||||
<Loader show={loading} className={'loadingState'} animate={false} />
|
||||
|
||||
@@ -10,6 +10,9 @@ import {
|
||||
levelColorsList,
|
||||
levelConstructList
|
||||
} from '@cp/src/components/AgentprogressMeter/constant';
|
||||
import TableCellRenderer from '@cp/src/components/TableCellRenderer';
|
||||
import CellRendererShortNumber from '@cp/src/components/TableCellRenderer/CellRendererShortNumber';
|
||||
import { Cell } from 'recharts';
|
||||
|
||||
export const AgentPerformanceColumns = [
|
||||
{
|
||||
@@ -65,9 +68,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { allocatedCases } = params.data;
|
||||
if (!allocatedCases) return <>-</>;
|
||||
const value = formatNumber(allocatedCases, true);
|
||||
const shortnumber = shortNumberNotation(allocatedCases, 1);
|
||||
return <RenderWithToolTip value={shortnumber} toolTipContent={value} />;
|
||||
return <CellRendererShortNumber value={allocatedCases} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -82,12 +83,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { allocatedEmi } = params.data;
|
||||
if (!allocatedEmi) return <>-</>;
|
||||
return (
|
||||
<RenderWithToolTip
|
||||
value={'₹ ' + shortNumberNotation(allocatedEmi, 1)}
|
||||
toolTipContent={formatAmount(allocatedEmi)}
|
||||
/>
|
||||
);
|
||||
return <CellRendererShortNumber value={allocatedEmi} isCurrency />;
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -106,9 +102,7 @@ export const AgentPerformanceColumns = [
|
||||
const { emiCollectionEfficiency } = params.data;
|
||||
if (!emiCollectionEfficiency) return <>-</>;
|
||||
return (
|
||||
<div className={cx(styles.nameField)}>
|
||||
{shortNumberNotation(emiCollectionEfficiency, 2)}%
|
||||
</div>
|
||||
<TableCellRenderer value={`${shortNumberNotation(emiCollectionEfficiency, 2)}%`} />
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -123,9 +117,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { dpdAchievement } = params.data;
|
||||
if (!dpdAchievement) return <>-</>;
|
||||
return (
|
||||
<div className={cx(styles.nameField)}>{shortNumberNotation(dpdAchievement, 2)}%</div>
|
||||
);
|
||||
return <TableCellRenderer value={`${shortNumberNotation(dpdAchievement, 2)}%`} />;
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -143,12 +135,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { cashCollected } = params.data;
|
||||
if (!cashCollected) return <>-</>;
|
||||
return (
|
||||
<RenderWithToolTip
|
||||
value={'₹ ' + shortNumberNotation(cashCollected, 1)}
|
||||
toolTipContent={'₹ ' + formatNumber(cashCollected, true)}
|
||||
/>
|
||||
);
|
||||
return <CellRendererShortNumber value={cashCollected} isCurrency />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -162,12 +149,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { todayDRR } = params.data;
|
||||
if (!todayDRR) return <>-</>;
|
||||
return (
|
||||
<RenderWithToolTip
|
||||
value={'₹ ' + shortNumberNotation(todayDRR, 1)}
|
||||
toolTipContent={'₹ ' + formatNumber(todayDRR, true)}
|
||||
/>
|
||||
);
|
||||
return <CellRendererShortNumber value={todayDRR} isCurrency />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -181,12 +163,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { todayCashCollected } = params.data;
|
||||
if (!todayCashCollected) return <>-</>;
|
||||
return (
|
||||
<RenderWithToolTip
|
||||
value={'₹ ' + shortNumberNotation(todayCashCollected, 1)}
|
||||
toolTipContent={'₹ ' + formatNumber(todayCashCollected, true)}
|
||||
/>
|
||||
);
|
||||
return <CellRendererShortNumber value={todayCashCollected} isCurrency />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -200,12 +177,7 @@ export const AgentPerformanceColumns = [
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { collectedEmi } = params.data;
|
||||
if (!collectedEmi) return <>-</>;
|
||||
return (
|
||||
<RenderWithToolTip
|
||||
value={'₹ ' + shortNumberNotation(collectedEmi, 1)}
|
||||
toolTipContent={'₹ ' + formatNumber(collectedEmi, true)}
|
||||
/>
|
||||
);
|
||||
return <CellRendererShortNumber value={collectedEmi} isCurrency />;
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -51,20 +51,6 @@
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
background-color: var(--orange-oldlase);
|
||||
color: var(--orange-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;
|
||||
}
|
||||
|
||||
> div:nth-child(3) {
|
||||
background-color: var(--pale-yellow);
|
||||
color: var(--navi-color-yellow-dark);
|
||||
pointer-events: none;
|
||||
@@ -199,14 +185,15 @@
|
||||
font-weight: 900;
|
||||
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-cell-value {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.ag-row-last {
|
||||
border-bottom: var(--ag-borders-row) var(--ag-row-border-color) !important;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ const Index: React.FC<IFilters> = props => {
|
||||
|
||||
const pinnedRow = React.useMemo(() => {
|
||||
const data: IAgent = {
|
||||
agentName: 'Overall Performance',
|
||||
agentName: 'Overall',
|
||||
agentReferenceId: ' ',
|
||||
tlReferenceId: ' ',
|
||||
allocatedCases: 0,
|
||||
@@ -114,9 +114,11 @@ const Index: React.FC<IFilters> = props => {
|
||||
agentCycleTargetDPD: 0
|
||||
};
|
||||
if (!tableData?.length) return [data];
|
||||
let sumProductofEmiAllocatedAndDPDTarget = 0;
|
||||
const mappingWithDifferentCycles: { [key: string]: number } = {};
|
||||
tableData.forEach((item: IAgent) => {
|
||||
mappingWithDifferentCycles[item.dueCycle] = item.agentCycleTargetDPD;
|
||||
sumProductofEmiAllocatedAndDPDTarget += item.allocatedEmi * item.agentCycleTargetDPD ?? 0;
|
||||
data.allocatedCases += item.allocatedCases;
|
||||
data.allocatedEmiInThousands += item.allocatedEmiInThousands;
|
||||
data.allocatedEmi += item.allocatedEmi;
|
||||
@@ -138,7 +140,7 @@ const Index: React.FC<IFilters> = props => {
|
||||
distinctCount++;
|
||||
distinctSum += +mappingWithDifferentCycles[key];
|
||||
}
|
||||
data.dpdAchievement = data.emiCollectionEfficiency / (distinctSum / distinctCount);
|
||||
data.dpdAchievement = (data.collectedEmi / sumProductofEmiAllocatedAndDPDTarget) * 100;
|
||||
return [data];
|
||||
}, [tableData]);
|
||||
|
||||
@@ -193,6 +195,7 @@ const Index: React.FC<IFilters> = props => {
|
||||
}
|
||||
suppressScrollOnNewData={true}
|
||||
suppressRowDrag={false}
|
||||
alternateRowColor="var(--greyscale-content-4)"
|
||||
/>
|
||||
</div>
|
||||
<Loader show={loading} className={'loadingState'} />
|
||||
|
||||
179
src/pages/SenseiTele/components/despositionSummary/ColumnDef.tsx
Normal file
179
src/pages/SenseiTele/components/despositionSummary/ColumnDef.tsx
Normal file
@@ -0,0 +1,179 @@
|
||||
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';
|
||||
|
||||
export const AgentPerformanceColumns = [
|
||||
{
|
||||
headerName: '',
|
||||
children: [
|
||||
{
|
||||
field: 'agentName',
|
||||
headerName: 'Agent Name',
|
||||
maxWidth: 180,
|
||||
pinned: 'left',
|
||||
suppressMovable: true,
|
||||
sortable: true,
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { agentName } = params.data || {};
|
||||
return <TableCellRenderer value={agentName} showToolTip toolTipContent={agentName} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'dueCycle',
|
||||
wrapHeaderText: true,
|
||||
headerName: 'Cycle',
|
||||
sortable: true,
|
||||
autoHeaderHeight: true,
|
||||
width: 90,
|
||||
pinned: 'left',
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { dueCycle } = params.data;
|
||||
return <TableCellRenderer value={dueCycle} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'unpaidCases',
|
||||
wrapHeaderText: true,
|
||||
headerName: ' Unpaid Cases',
|
||||
sortable: true,
|
||||
serverSortable: true,
|
||||
autoHeaderHeight: true,
|
||||
cellClass: styles.border,
|
||||
headerClass: styles.border,
|
||||
width: 100,
|
||||
pinned: 'left',
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { unpaidCases } = params.data;
|
||||
return <CellRendererShortNumber value={unpaidCases} />;
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
headerName: "Today's PTP's",
|
||||
children: [
|
||||
{
|
||||
field: 'ptpTodayCount',
|
||||
headerName: 'PTP (Count)',
|
||||
width: 120,
|
||||
suppressMovable: true,
|
||||
sortable: true,
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { ptpTodayCount } = params.data || {};
|
||||
return <CellRendererShortNumber value={ptpTodayCount} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'ptpTodayEmi',
|
||||
wrapHeaderText: true,
|
||||
headerName: 'PTP (EMI)',
|
||||
sortable: true,
|
||||
autoHeaderHeight: true,
|
||||
width: 120,
|
||||
cellClass: styles.border,
|
||||
headerClass: styles.border,
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { ptpTodayEmi } = params.data;
|
||||
return <CellRendererShortNumber isCurrency value={ptpTodayEmi} />;
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
headerName: 'Overall Feedback Quality',
|
||||
children: [
|
||||
{
|
||||
field: 'ptpCases',
|
||||
wrapHeaderText: true,
|
||||
headerName: 'PTP',
|
||||
sortable: true,
|
||||
serverSortable: true,
|
||||
autoHeaderHeight: true,
|
||||
width: 90,
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { ptpCases } = params.data;
|
||||
return <CellRendererShortNumber value={ptpCases} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'brokenPtp',
|
||||
wrapHeaderText: true,
|
||||
headerName: 'BPTP',
|
||||
sortable: true,
|
||||
serverSortable: true,
|
||||
autoHeaderHeight: true,
|
||||
width: 100,
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { brokenPtp } = params.data;
|
||||
return <CellRendererShortNumber value={brokenPtp} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'neutral',
|
||||
wrapHeaderText: true,
|
||||
headerName: 'Neutral',
|
||||
sortable: true,
|
||||
serverSortable: true,
|
||||
autoHeaderHeight: true,
|
||||
width: 90,
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { neutral } = params.data;
|
||||
return <CellRendererShortNumber value={neutral} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'softNonContactable',
|
||||
wrapHeaderText: true,
|
||||
headerName: 'Soft NC',
|
||||
sortable: true,
|
||||
serverSortable: true,
|
||||
autoHeaderHeight: true,
|
||||
width: 100,
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { softNonContactable } = params.data;
|
||||
return <CellRendererShortNumber value={softNonContactable} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'hardNonContactable',
|
||||
wrapHeaderText: true,
|
||||
headerName: 'Hard NC',
|
||||
sortable: true,
|
||||
serverSortable: true,
|
||||
autoHeaderHeight: true,
|
||||
width: 100,
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { hardNonContactable } = params.data;
|
||||
return <CellRendererShortNumber value={hardNonContactable} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'dispute',
|
||||
wrapHeaderText: true,
|
||||
headerName: 'Dispute / RTP',
|
||||
sortable: true,
|
||||
serverSortable: true,
|
||||
autoHeaderHeight: true,
|
||||
width: 100,
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { dispute } = params.data;
|
||||
return <CellRendererShortNumber value={dispute} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'expired',
|
||||
wrapHeaderText: true,
|
||||
headerName: 'Expired',
|
||||
sortable: true,
|
||||
serverSortable: true,
|
||||
autoHeaderHeight: true,
|
||||
width: 100,
|
||||
cellRenderer: (params: ICellRendererParams) => {
|
||||
const { expired } = params.data;
|
||||
return <CellRendererShortNumber value={expired} />;
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,189 @@
|
||||
.agentDepositionTableContainer {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
:global {
|
||||
.agentDepositionSummary {
|
||||
.ag-header-viewport {
|
||||
.ag-header-row-column-group {
|
||||
> div:nth-child(1) {
|
||||
background-color: var(--green-light);
|
||||
color: var(--green-base);
|
||||
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;
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
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;
|
||||
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: var(--ag-borders-row) var(--ag-row-border-color) !important;
|
||||
}
|
||||
|
||||
.ag-header-cell-text {
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.ag-horizontal-left-spacer {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
192
src/pages/SenseiTele/components/despositionSummary/index.tsx
Normal file
192
src/pages/SenseiTele/components/despositionSummary/index.tsx
Normal file
@@ -0,0 +1,192 @@
|
||||
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, Pagination } 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, useMemo, useRef } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { IDepositionSummary, IFilters } from '../../types';
|
||||
import { AgentPerformanceColumns } from './ColumnDef';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const Index: React.FC<IFilters> = props => {
|
||||
const { params, updateAndNavigate, url } = props;
|
||||
const ref = useRef<AgGridReact<any>>(null);
|
||||
const [columnData, setColumnData] = React.useState(AgentPerformanceColumns);
|
||||
const tableData = useSelector((state: RootState) => state.teleSensei.despositionSummaryFilter);
|
||||
const loading = useSelector((state: RootState) => state.teleSensei.loadingDespositionSummary);
|
||||
const dispatch = useDispatch();
|
||||
const handleSort = (e: SortChangedEvent<any>) => {
|
||||
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 handlePageChange = (e: number) => {
|
||||
const newParams = { ...params };
|
||||
const pageNumber = e.toString();
|
||||
newParams.pageNoDisposition = pageNumber;
|
||||
ref?.current?.api.paginationGoToPage(e - 1);
|
||||
updateAndNavigate(newParams);
|
||||
};
|
||||
|
||||
const handlePageSizeChange = (e: number) => {
|
||||
const newParams = { ...params };
|
||||
const pageSize = e.toString();
|
||||
newParams.pageSizeDisposition = pageSize;
|
||||
ref?.current?.api.paginationSetPageSize(e);
|
||||
updateAndNavigate(newParams);
|
||||
};
|
||||
|
||||
const pageNumber = () => {
|
||||
ref?.current?.api?.paginationGoToPage(Number(params?.pageNoDisposition) - 1);
|
||||
return Number(params?.pageNoDisposition);
|
||||
};
|
||||
const pageSize = Number(params?.pageSizeInput);
|
||||
|
||||
const handleDownloadReport = () => {
|
||||
// if (tableData.length > 500) {
|
||||
// dispatch(getAgentInputDataCSV());
|
||||
// return;
|
||||
// }
|
||||
ref?.current?.api?.exportDataAsCsv({
|
||||
fileName: `Agent-desposition-${dateFormat(new Date(), DateFormat.FILE_DATE_FORMAT)}`
|
||||
});
|
||||
};
|
||||
|
||||
const pinnedRow = useMemo(() => {
|
||||
const data: IDepositionSummary = {
|
||||
agentName: 'Overall',
|
||||
agentReferenceId: 'string;',
|
||||
tlReferenceId: 'string;',
|
||||
unpaidCases: 0,
|
||||
ptpCases: 0,
|
||||
brokenPtp: 0,
|
||||
neutral: 0,
|
||||
softNonContactable: 0,
|
||||
hardNonContactable: 0,
|
||||
dispute: 0,
|
||||
expired: 0,
|
||||
dueCycle: '',
|
||||
ptpTodayCount: 0,
|
||||
ptpTodayEmi: 0
|
||||
};
|
||||
|
||||
tableData.forEach((item: IDepositionSummary) => {
|
||||
data.unpaidCases += item.unpaidCases ?? 0;
|
||||
data.ptpCases += item.ptpCases ?? 0;
|
||||
data.brokenPtp += item.brokenPtp ?? 0;
|
||||
data.neutral += item.neutral ?? 0;
|
||||
data.softNonContactable += item.softNonContactable ?? 0;
|
||||
data.hardNonContactable += item.hardNonContactable ?? 0;
|
||||
data.dispute += item.dispute;
|
||||
data.expired += item.expired;
|
||||
data.ptpTodayCount += item.ptpTodayCount ?? 0;
|
||||
data.ptpTodayEmi += item.ptpTodayEmi ?? 0;
|
||||
});
|
||||
|
||||
return [data];
|
||||
}, [tableData]);
|
||||
|
||||
return (
|
||||
<div className={cx(styles.agentDepositionTableContainer, 'agentDepositionSummary')}>
|
||||
<div className={styles.tableContainer}>
|
||||
<div className={styles.tableActions}>
|
||||
<Typography color="var(--grayscale-2)" variant="h3">
|
||||
Dispositions Summary
|
||||
</Typography>
|
||||
<Button
|
||||
startAdornment={<Download />}
|
||||
className={styles.downloadReport}
|
||||
variant="text"
|
||||
onClick={handleDownloadReport}
|
||||
disabled={tableData.length === 0}
|
||||
>
|
||||
Download Report
|
||||
</Button>
|
||||
</div>
|
||||
<AgTable
|
||||
ref={ref}
|
||||
columnDefs={columnData}
|
||||
style={{
|
||||
height: 450
|
||||
}}
|
||||
headerHeight={70}
|
||||
defaultColDef={{
|
||||
suppressMovable: true,
|
||||
wrapText: true,
|
||||
autoHeight: true
|
||||
}}
|
||||
theme={'alpine'}
|
||||
rowData={tableData}
|
||||
onSortChanged={handleSort}
|
||||
pagination
|
||||
suppressPaginationPanel={true}
|
||||
paginationPageSize={pageSize}
|
||||
paginationWrapperClasses="paginationWrapperClasses"
|
||||
animateRows
|
||||
pinnedTopRowData={pinnedRow}
|
||||
PaginationComponent={
|
||||
<Pagination
|
||||
key={pageSize}
|
||||
totalCount={tableData.length}
|
||||
pageSize={pageSize}
|
||||
currentPage={pageNumber()}
|
||||
pageSizeOptions={[10, 15, 20, 50]}
|
||||
onPageChange={handlePageChange}
|
||||
onPageSizeChange={handlePageSizeChange}
|
||||
/>
|
||||
}
|
||||
suppressRowClickSelection={true}
|
||||
suppressRowDrag={false}
|
||||
sizeColumnsToFit
|
||||
alternateRowColor="var(--greyscale-content-4)"
|
||||
/>
|
||||
</div>
|
||||
<Loader show={loading} className={'loadingState'} animate={false} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
||||
@@ -9,6 +9,9 @@ import { FilterType, IFilters } from '../../types';
|
||||
import styles from './index.module.scss';
|
||||
import { DateFormat, dateFormat } from '@cp/src/utils/DateHelper';
|
||||
import cx from 'classnames';
|
||||
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@cp/src/service/clickStream.constant';
|
||||
import { TABLE_DEFAULT_VALUES } from '../../constants';
|
||||
|
||||
const Index: React.FC<IFilters> = props => {
|
||||
const { params, updateAndNavigate } = props;
|
||||
@@ -91,7 +94,9 @@ const Index: React.FC<IFilters> = props => {
|
||||
}
|
||||
updatedParams['pageNoPerformance'] = '1';
|
||||
updatedParams['pageNoInput'] = '1';
|
||||
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_TELE_SENSEI_FILTERS, {
|
||||
...updatedParams
|
||||
});
|
||||
updateAndNavigate(updatedParams);
|
||||
};
|
||||
|
||||
@@ -99,7 +104,12 @@ const Index: React.FC<IFilters> = props => {
|
||||
const updatedParams = { ...params };
|
||||
delete updatedParams['agentId'];
|
||||
delete updatedParams['teamLeadId'];
|
||||
updatedParams['pageNo'] = '1';
|
||||
updatedParams['pageNoInput'] = TABLE_DEFAULT_VALUES.page;
|
||||
updatedParams['pageNoPerformance'] = TABLE_DEFAULT_VALUES.page;
|
||||
updatedParams['pageNoDisposition'] = TABLE_DEFAULT_VALUES.page;
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_TELE_SENSEI_FILTERS, {
|
||||
...updatedParams
|
||||
});
|
||||
updateAndNavigate(updatedParams);
|
||||
};
|
||||
|
||||
|
||||
35
src/pages/SenseiTele/index.module.scss
Normal file
35
src/pages/SenseiTele/index.module.scss
Normal file
@@ -0,0 +1,35 @@
|
||||
.agentTableContainer {
|
||||
margin-top: 32px;
|
||||
position: relative;
|
||||
|
||||
.headerContainer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-right: 32px;
|
||||
|
||||
.header {
|
||||
margin-left: 104px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 32px;
|
||||
}
|
||||
|
||||
.tableContainer {
|
||||
background: linear-gradient(90deg, #fff8f0 0.32%, rgba(255, 250, 245, 0.24) 99.68%);
|
||||
padding: 48px 32px 100px 32px;
|
||||
|
||||
.downloadReport {
|
||||
position: absolute;
|
||||
top: 42px;
|
||||
right: 37px;
|
||||
}
|
||||
}
|
||||
|
||||
.border {
|
||||
border-right: 1px solid var(--navi-color-gray-border) !important;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,35 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import Filter from './components/filters';
|
||||
import AgentPerformance from './components/agentPerformance';
|
||||
import { getAgentInputData, getAgentPerformanceData } from './action';
|
||||
import { getAgentInputData, getAgentPerformanceData, getDespositionSummary } from './action';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { createQueryParams, readQueryParams } from '@cp/src/utils/QueryParamsHelper';
|
||||
import { IAgent, IAgentPerformance, IAgentPerformanceParams } from './types';
|
||||
import { IAgent, IAgentPerformance, IAgentPerformanceParams, IDepositionSummary } from './types';
|
||||
import { AGENT_PERFORMANCE_TABLE, TABLE_DEFAULT_VALUES, pollingInterval } from './constants';
|
||||
import { useLocation, useNavigate } from 'react-router';
|
||||
import AgentInput from './components/agentInput';
|
||||
import Loader from '@cp/src/components/Loader/Loader';
|
||||
import store, { RootState } from '@cp/src/store';
|
||||
import { setAgentPerformanceFiltered, setFilteredData } from './reducer';
|
||||
import {
|
||||
setAgentPerformanceFiltered,
|
||||
setDespositionSummaryFilter,
|
||||
setFilteredData
|
||||
} from './reducer';
|
||||
import { poll } from '@cp/src/utils/polling';
|
||||
import { noop } from '@navi/web-ui/lib/utils/common';
|
||||
import { getServerTime } from '@cp/src/utils/DateHelper';
|
||||
import DespositionSummary from './components/despositionSummary';
|
||||
import cx from 'classnames';
|
||||
import styles from './index.module.scss';
|
||||
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';
|
||||
|
||||
const Index = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { loading, loadingAgentPerformance, agentData, IAgentPerformance } = useSelector(
|
||||
(state: RootState) => state.teleSensei
|
||||
);
|
||||
const { loading, loadingAgentPerformance, agentData, IAgentPerformance, despositionSummary } =
|
||||
useSelector((state: RootState) => state.teleSensei);
|
||||
const location = useLocation();
|
||||
|
||||
const { [AGENT_PERFORMANCE_TABLE]: params } = readQueryParams();
|
||||
@@ -35,9 +45,11 @@ const Index = () => {
|
||||
useEffect(() => {
|
||||
dispatch(getAgentInputData());
|
||||
dispatch(getAgentPerformanceData());
|
||||
dispatch(getDespositionSummary());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_TELE_SENSEI_PAGE_LOAD);
|
||||
const queryParams: IAgentPerformanceParams = { ...params };
|
||||
if (!queryParams?.pageNoPerformance) {
|
||||
queryParams['pageNoPerformance'] = TABLE_DEFAULT_VALUES.page;
|
||||
@@ -51,6 +63,12 @@ const Index = () => {
|
||||
if (!queryParams?.pageSizeInput) {
|
||||
queryParams['pageSizeInput'] = TABLE_DEFAULT_VALUES.rowsPerPage;
|
||||
}
|
||||
if (!queryParams?.pageNoDisposition) {
|
||||
queryParams['pageNoDisposition'] = '1';
|
||||
}
|
||||
if (!queryParams?.pageSizeDisposition) {
|
||||
queryParams['pageSizeDisposition'] = '10';
|
||||
}
|
||||
updateAndNavigate(queryParams);
|
||||
}, []);
|
||||
|
||||
@@ -59,6 +77,7 @@ const Index = () => {
|
||||
if (!IAgentPerformance) return;
|
||||
let filteredAgentData: IAgent[] = [];
|
||||
let filteredAgentInput: IAgentPerformance[] = [];
|
||||
let filteredDespositionData: IDepositionSummary[] = [];
|
||||
// filtering this out if both the filter selected
|
||||
if (params?.teamLeadId && params?.agentId) {
|
||||
const dataAfterTLFilter = agentData?.filter(
|
||||
@@ -78,6 +97,15 @@ const Index = () => {
|
||||
);
|
||||
dispatch(setAgentPerformanceFiltered(filteredAgentInput));
|
||||
|
||||
const dataAfterTLFilterDisposition = despositionSummary?.filter(
|
||||
agentData => agentData?.tlReferenceId === params?.teamLeadId
|
||||
);
|
||||
|
||||
filteredDespositionData = dataAfterTLFilterDisposition?.filter(agentData =>
|
||||
selectedAgents?.some((agentID: string) => agentData?.agentReferenceId === agentID)
|
||||
);
|
||||
dispatch(setDespositionSummaryFilter(filteredDespositionData));
|
||||
|
||||
return;
|
||||
}
|
||||
if (params?.teamLeadId && !params?.agentId) {
|
||||
@@ -90,6 +118,11 @@ const Index = () => {
|
||||
agentData => agentData?.tlReferenceId === params?.teamLeadId
|
||||
);
|
||||
dispatch(setAgentPerformanceFiltered(filteredAgentInput));
|
||||
|
||||
filteredDespositionData = despositionSummary?.filter(
|
||||
agentData => agentData?.tlReferenceId === params?.teamLeadId
|
||||
);
|
||||
dispatch(setDespositionSummaryFilter(filteredDespositionData));
|
||||
return;
|
||||
}
|
||||
if (!params?.teamLeadId && params?.agentId) {
|
||||
@@ -103,6 +136,11 @@ const Index = () => {
|
||||
selectedAgents?.some((agentID: string) => agentData?.agentReferenceId === agentID)
|
||||
);
|
||||
dispatch(setAgentPerformanceFiltered(filteredAgentInput));
|
||||
|
||||
filteredDespositionData = despositionSummary?.filter(agentData =>
|
||||
selectedAgents?.some((agentID: string) => agentData?.agentReferenceId === agentID)
|
||||
);
|
||||
dispatch(setDespositionSummaryFilter(filteredDespositionData));
|
||||
return;
|
||||
}
|
||||
if (!params?.teamLeadId && !params?.agentId) {
|
||||
@@ -111,15 +149,33 @@ const Index = () => {
|
||||
|
||||
filteredAgentInput = IAgentPerformance;
|
||||
dispatch(setAgentPerformanceFiltered(filteredAgentInput));
|
||||
|
||||
filteredDespositionData = despositionSummary;
|
||||
dispatch(setDespositionSummaryFilter(filteredDespositionData));
|
||||
return;
|
||||
}
|
||||
}, [location, agentData, IAgentPerformance]);
|
||||
}, [location, agentData, IAgentPerformance, despositionSummary]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Filter params={params} updateAndNavigate={updateAndNavigate} url={location.hash} />
|
||||
<AgentPerformance params={params} updateAndNavigate={updateAndNavigate} url={location.hash} />
|
||||
<AgentInput params={params} updateAndNavigate={updateAndNavigate} url={location.hash} />
|
||||
<div className={styles.agentTableContainer}>
|
||||
<div className={styles.headerContainer}>
|
||||
<Typography className={styles.header} color="var(--navi-color-gray-c1)" variant="h2">
|
||||
Agent Input
|
||||
</Typography>
|
||||
</div>
|
||||
<AgentInputIcon className={styles.icon} />
|
||||
<div className={styles.tableContainer}>
|
||||
<AgentInput params={params} updateAndNavigate={updateAndNavigate} url={location.hash} />
|
||||
<DespositionSummary
|
||||
params={params}
|
||||
updateAndNavigate={updateAndNavigate}
|
||||
url={location.hash}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Loader show={loading || loadingAgentPerformance} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -7,13 +7,17 @@ import { IAgentData } from '../types';
|
||||
const initialState: IAgentData = {
|
||||
agentData: [],
|
||||
agentNames: [],
|
||||
lastRefreshedTimeStamp: '',
|
||||
lastRefreshedTimeStampPerformance: '',
|
||||
tlNames: [],
|
||||
loading: false,
|
||||
filteredData: [],
|
||||
IAgentPerformance: [],
|
||||
IAgentPerformanceFilter: [],
|
||||
loadingAgentPerformance: false
|
||||
loadingAgentPerformance: false,
|
||||
lastRefreshedTimeStampInput: '',
|
||||
despositionSummary: [],
|
||||
loadingDespositionSummary: false,
|
||||
despositionSummaryFilter: []
|
||||
};
|
||||
|
||||
const TeleSenseiSlice = createSlice({
|
||||
@@ -44,6 +48,16 @@ const TeleSenseiSlice = createSlice({
|
||||
},
|
||||
setAgentPerformanceFiltered(state, action) {
|
||||
state.IAgentPerformanceFilter = action.payload;
|
||||
},
|
||||
setDespositionSummaryFilter(state, action) {
|
||||
state.despositionSummaryFilter = action.payload;
|
||||
},
|
||||
setLoadingDespositionSummary(state, action) {
|
||||
state.loadingDespositionSummary = action.payload;
|
||||
},
|
||||
setDespositionSummary(state, action) {
|
||||
const { data } = action.payload;
|
||||
state.despositionSummary = data;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -54,7 +68,10 @@ export const {
|
||||
setFilteredData,
|
||||
setAgentPerformance,
|
||||
setLoadingAgentPerformance,
|
||||
setAgentPerformanceFiltered
|
||||
setAgentPerformanceFiltered,
|
||||
setDespositionSummaryFilter,
|
||||
setLoadingDespositionSummary,
|
||||
setDespositionSummary
|
||||
} = TeleSenseiSlice.actions;
|
||||
|
||||
export default TeleSenseiSlice.reducer;
|
||||
|
||||
@@ -14,6 +14,10 @@ export interface IAgentPerformanceParams {
|
||||
sortOrderInput?: string;
|
||||
sortColumnPerformance?: string;
|
||||
sortOrderPerformance?: string;
|
||||
sortColumnDisposition?: string;
|
||||
sortOrderDisposition?: string;
|
||||
pageNoDisposition?: string;
|
||||
pageSizeDisposition?: string;
|
||||
}
|
||||
|
||||
export interface IFilters {
|
||||
@@ -61,6 +65,9 @@ export interface IAgentData {
|
||||
loadingAgentPerformance: boolean;
|
||||
IAgentPerformanceFilter: IAgentPerformance[];
|
||||
lastRefreshedTimeStampInput: string;
|
||||
despositionSummary: IDepositionSummary[];
|
||||
loadingDespositionSummary: boolean;
|
||||
despositionSummaryFilter: IDepositionSummary[];
|
||||
}
|
||||
|
||||
export enum FilterType {
|
||||
@@ -91,3 +98,20 @@ export interface IAgentPerformance {
|
||||
connectedCallsAmeyoLH: number;
|
||||
averageUniqueDaysCalledPerCase: number;
|
||||
}
|
||||
|
||||
export interface IDepositionSummary {
|
||||
agentName: string;
|
||||
agentReferenceId: string;
|
||||
tlReferenceId: string;
|
||||
unpaidCases: number;
|
||||
ptpCases: number;
|
||||
brokenPtp: number;
|
||||
neutral: number;
|
||||
softNonContactable: number;
|
||||
hardNonContactable: number;
|
||||
dispute: number;
|
||||
expired: number;
|
||||
dueCycle: string;
|
||||
ptpTodayCount: number;
|
||||
ptpTodayEmi: number;
|
||||
}
|
||||
|
||||
@@ -517,6 +517,14 @@ export const CLICKSTREAM_EVENT_NAMES = Object.freeze({
|
||||
LH_FIELD_DASHBOARD_SIDE_PANEL_CLICK: {
|
||||
name: 'LH_FIELD_DASHBOARD_SIDE_PANEL_CLICK',
|
||||
description: 'Field dashboard side panel click'
|
||||
},
|
||||
LH_TELE_SENSEI_SIDE_PANEL_CLICK: {
|
||||
name: 'LH_TELE_SENSEI_SIDE_PANEL_CLICK',
|
||||
description: 'Tele sensei side panel click'
|
||||
},
|
||||
LH_TELE_SENSEI_FILTERS: {
|
||||
name: 'LH_TELE_SENSEI_FILTERS',
|
||||
description: 'Tele sensei applied filters'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -153,7 +153,8 @@ export enum ApiKeys {
|
||||
GET_AGENT_PERFORMANCE,
|
||||
GET_AGENT_INPUT,
|
||||
CONVERT_TIFF_TO_JPEG,
|
||||
LEGAL_DOCS
|
||||
LEGAL_DOCS,
|
||||
GET_DEPOSITIONS
|
||||
}
|
||||
|
||||
// TODO: try to get rid of `as`
|
||||
@@ -291,6 +292,7 @@ API_URLS[ApiKeys.SET_CHAT_AGENT_STATUS] = '/human-reminder-chat/{status}';
|
||||
API_URLS[ApiKeys.GET_AGENT_INPUT] = '/team-lead/dashboard/reportees-inputs';
|
||||
API_URLS[ApiKeys.GET_AGENT_PERFORMANCE] = '/team-lead/dashboard/reportees-performance';
|
||||
API_URLS[ApiKeys.CONVERT_TIFF_TO_JPEG] = '/v1/documents/convert/tiff-to-jpeg';
|
||||
API_URLS[ApiKeys.GET_DEPOSITIONS] = '/team-lead/dashboard/reportees-disposition-summary';
|
||||
|
||||
// TODO: try to get rid of `as`
|
||||
const MOCK_API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;
|
||||
|
||||
@@ -67,7 +67,6 @@ export const shortNumberNotation = (value: any = 0, decimals = 2, separator = ''
|
||||
if (!value) return '';
|
||||
|
||||
const parsedValue = Number(value);
|
||||
|
||||
const abbreviations = [
|
||||
{ threshold: 1e7, symbol: 'Cr' },
|
||||
{ threshold: 1e5, symbol: 'L' },
|
||||
|
||||
Reference in New Issue
Block a user