Revert "TP-69782|Kunal|Revert "TP-69872|Kunal|daily-limit on ameyo and slash calls"" (#1055)

* Revert "Revert "TP-69872|Kunal|daily-limit on ameyo and slash calls (#1042)" …"

This reverts commit 17154ca5a5.

* TP-69872|Kunal|daily limit
This commit is contained in:
Kunal Sharma
2024-07-24 21:05:00 +05:30
committed by GitHub
parent f0daed6d17
commit dfd798ac64
19 changed files with 521 additions and 184 deletions

View File

@@ -0,0 +1,14 @@
const ClockIcon = () => (
<svg width={16} height={16} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_9198_56912" maskUnits="userSpaceOnUse" x={0} y={0} width={16} height={16}>
<rect width={16} height={16} fill="#D9D9D9" />
</mask>
<g>
<path
d="M10.2002 11.1335L11.1335 10.2002L8.66683 7.7335V4.66683H7.3335V8.26683L10.2002 11.1335ZM8.00016 14.6668C7.07794 14.6668 6.21127 14.4917 5.40016 14.1415C4.58905 13.7917 3.8835 13.3168 3.2835 12.7168C2.6835 12.1168 2.20861 11.4113 1.85883 10.6002C1.50861 9.78905 1.3335 8.92239 1.3335 8.00016C1.3335 7.07794 1.50861 6.21127 1.85883 5.40016C2.20861 4.58905 2.6835 3.8835 3.2835 3.2835C3.8835 2.6835 4.58905 2.20838 5.40016 1.85816C6.21127 1.50838 7.07794 1.3335 8.00016 1.3335C8.92239 1.3335 9.78905 1.50838 10.6002 1.85816C11.4113 2.20838 12.1168 2.6835 12.7168 3.2835C13.3168 3.8835 13.7917 4.58905 14.1415 5.40016C14.4917 6.21127 14.6668 7.07794 14.6668 8.00016C14.6668 8.92239 14.4917 9.78905 14.1415 10.6002C13.7917 11.4113 13.3168 12.1168 12.7168 12.7168C12.1168 13.3168 11.4113 13.7917 10.6002 14.1415C9.78905 14.4917 8.92239 14.6668 8.00016 14.6668ZM8.00016 13.3335C9.47794 13.3335 10.7364 12.8142 11.7755 11.7755C12.8142 10.7364 13.3335 9.47794 13.3335 8.00016C13.3335 6.52239 12.8142 5.26394 11.7755 4.22483C10.7364 3.18616 9.47794 2.66683 8.00016 2.66683C6.52239 2.66683 5.26416 3.18616 4.2255 4.22483C3.18638 5.26394 2.66683 6.52239 2.66683 8.00016C2.66683 9.47794 3.18638 10.7364 4.2255 11.7755C5.26416 12.8142 6.52239 13.3335 8.00016 13.3335Z"
fill="#97A1AC"
/>
</g>
</svg>
);
export default ClockIcon;

View File

@@ -12,5 +12,5 @@
letter-spacing: -0.12px;
}
.toolTipStyle {
max-width: 212px !important;
max-width: 270px !important;
}

View File

@@ -3,7 +3,7 @@ import RedCrossIcon from '../../assets/images/icons/RedCrossIcon';
import GreyCircleIcon from '../../assets/images/icons/GreyCircleIcon';
import styles from './CallHistory.module.scss';
import { CallBasedInteractions } from 'src/pages/CaseDetails/interfaces/CaseDetail.type';
import { dateFormat } from 'src/utils/DateHelper';
import { DateFormat, dateFormat, utcToIST } from 'src/utils/DateHelper';
import { FeedbackTypesCodeMap } from 'src/pages/Cases/constants/CasesConstants';
import { Tooltip, TooltipContent, TooltipTrigger } from '../TooltipV2/TooltipV2';
import cx from 'classnames';
@@ -65,11 +65,18 @@ const CallStatusList = ({
showTooltip ? (
<div>
<Typography variant="p1" className={styles.tooltipText}>
Call Date: {dateFormat(new Date(callRecord.interactionTimestamp), 'DD MMM, YYYY')}{' '}
Call Date and Time:{' '}
{dateFormat(
utcToIST(new Date(callRecord.interactionTimestamp)),
DateFormat.LONG_DATE_FORMAT_WITH_TIME
)}{' '}
</Typography>
<Typography variant="p1" className={styles.tooltipText}>
Disposition: {FeedbackTypesCodeMap[callRecord?.tag]?.label || callRecord?.tag}
</Typography>
<Typography variant="p1" className={styles.tooltipText}>
Phone Number: {callRecord?.recipientNumber}
</Typography>
</div>
) : (
''

View File

@@ -15,6 +15,7 @@ const SelectPicker: React.FC<SelectPickerProps> = ({
showSourceAtBottom = false,
optionPosition = OptionPosition.bottom,
customOptionTemplate,
selectPickerItemClass,
...restProps
}) => {
const renderOptions = useMemo(() => {
@@ -34,6 +35,7 @@ const SelectPicker: React.FC<SelectPickerProps> = ({
multiSelect={multiSelect}
showSourceAtBottom={showSourceAtBottom}
customOptionTemplate={customOptionTemplate}
selectPickerItemClass={selectPickerItemClass}
/>
));
}, [options, selectedValue, showSourceAtBottom]);

View File

@@ -10,9 +10,10 @@ const SelectPickerItem = ({
onOptionClick,
multiSelect,
showSourceAtBottom = false,
customOptionTemplate
customOptionTemplate,
selectPickerItemClass
}: SelectPickerItemProps) => {
const { label, source, isDisabled, rightAdornment } = optionInfo;
const { label, source, isDisabled, rightAdornment, dailyLimit, hourlyLimit } = optionInfo;
const handleClick = () => {
onOptionClick(optionInfo);
@@ -25,7 +26,9 @@ const SelectPickerItem = ({
<div
className={cx(styles['selectPicker--item'], {
[styles['selectPicker--selected']]: selected,
[styles['selectPicker--disabled']]: isDisabled
[styles['selectPicker--disabled']]: isDisabled,
[`${selectPickerItemClass}`]:
(dailyLimit?.callRemaining === 0 || hourlyLimit?.callRemaining === 0) && !selected
})}
onClick={handleClick}
>

View File

@@ -60,6 +60,7 @@ const Dropdown: React.FC<DropdownProps> = ({
pickerContainerClasses,
showTextCursorOnDisabled = false,
tabIndexEnabled = false,
selectPickerItemClass = '',
...restProps
}) => {
const [showPicker, setShowPicker] = useState<boolean>(false);
@@ -265,6 +266,7 @@ const Dropdown: React.FC<DropdownProps> = ({
showSourceAtBottom={showSourceAtBottom}
optionPosition={optionPosition}
customOptionTemplate={customOptionTemplate}
selectPickerItemClass={selectPickerItemClass}
{...restProps}
/>
)}

View File

@@ -1,5 +1,6 @@
import { GenericObject } from '@cp/src/types/CommonConstans';
import { ReactNode } from 'react';
import { IPhoneNumberLimit } from '@cp/pages/CaseDetails/interfaces/CaseDetail.type';
export interface DropdownProps {
/** Dropdown Options */
@@ -27,6 +28,7 @@ export interface DropdownProps {
pickerContainerClasses?: string;
showTextCursorOnDisabled?: boolean;
tabIndexEnabled?: boolean;
selectPickerItemClass?: string;
}
export enum OptionPosition {
@@ -40,6 +42,8 @@ export interface SelectPickerOptionProps {
source?: string | ReactNode;
isDisabled?: boolean;
rightAdornment?: ReactNode;
dailyLimit?: IPhoneNumberLimit;
hourlyLimit?: IPhoneNumberLimit;
}
export interface SelectPickerProps extends React.ComponentPropsWithoutRef<'div'> {
@@ -60,6 +64,7 @@ export interface SelectPickerProps extends React.ComponentPropsWithoutRef<'div'>
showSourceAtBottom?: boolean;
optionPosition?: OptionPosition;
customOptionTemplate?: (option: string) => ReactNode;
selectPickerItemClass?: string;
}
export type SelectPickerValue = string | number;
@@ -71,6 +76,7 @@ export interface SelectPickerItemProps {
multiSelect: boolean;
showSourceAtBottom?: boolean;
customOptionTemplate?: (option: string, optionInfo?: GenericObject) => ReactNode;
selectPickerItemClass?: string;
}
export type SelectionChangeType = (

View File

@@ -9,11 +9,14 @@ import { DateFormat, UnitsEnum, dateDiff, dateFormat, utcToIST } from 'src/utils
import GridContainer from '@navi/web-ui/lib/layouts/Grid/GridContainer/GridContainer';
import GridRow from '@navi/web-ui/lib/layouts/Grid/GridRow/GridRow';
import GridColumn from '@navi/web-ui/lib/layouts/Grid/GridColumn/GridColumn';
import { CallBasedInteractions, PtpStatus } from '../../interfaces/CaseDetail.type';
import { CallBasedInteractions, LIMIT_TYPE, PtpStatus } from '../../interfaces/CaseDetail.type';
import { PreviousPTPs } from './PreviousPTPs';
import cx from 'classnames';
import CallInteractionHistory from 'src/components/CallHistory/CallInteractionHistory';
import { IconProps } from '@navi/web-ui/lib/icons/types';
import CircularProgress from '@cp/components/ProgressBars/circularProgress/CircularProgress';
import { useMemo } from 'react';
import dayjs from 'dayjs';
const NO_OF_RECORDS = 5;
@@ -28,6 +31,7 @@ const CallHistoryBox = () => {
const pageData = useSelector(
(state: RootState) => state.caseDetail.pageData?.[createKey(loanId, customerId)]
);
const { data: telephones } = pageData?.telephonesv2 || {};
const { data: caseDetails } = pageData?.details || {};
const { callBasedInteractions = [], ptpStatuses = [] } = caseDetails || {};
@@ -36,7 +40,6 @@ const CallHistoryBox = () => {
return new Date(a.interactionTimestamp) > new Date(b.interactionTimestamp) ? 1 : -1;
})
.pop()?.interactionTimestamp;
const currentDate = dateFormat(new Date(), DateFormat.ISO_DATE_FORMAT);
const previousDayDate = dateFormat(getPreviousDate(), DateFormat.ISO_DATE_FORMAT);
const difference = lastCalledTime
@@ -44,29 +47,34 @@ const CallHistoryBox = () => {
: undefined;
const iconProps: IconProps = { width: 24, height: 24 };
const LastCalled = () => {
const lastCalledDate = dateFormat(
new Date(utcToIST(lastCalledTime)),
DateFormat.ISO_DATE_FORMAT
);
const dateInIST = new Date(utcToIST(lastCalledTime));
const lastCalledDate = dateFormat(dateInIST, DateFormat.ISO_DATE_FORMAT);
const lastCalledDateLong = dateFormat(dateInIST, DateFormat.LONG_DATE_FORMAT_WITHOUT_TIME);
const lastCalledTimeHHMM = dateFormat(dateInIST, DateFormat.HH_mm_ampm);
if (lastCalledTime) {
switch (lastCalledDate) {
case currentDate:
return (
<Typography as="span" variant="h3">
Today
Today at {lastCalledTimeHHMM}
</Typography>
);
case previousDayDate:
return (
<Typography as="span" variant="h3">
Yesterday
Yesterday at {lastCalledTimeHHMM}
</Typography>
);
default:
return (
<Typography as="span" variant="h3">
Not called since last {difference} days
</Typography>
<>
<Typography as="span" variant="h3">
Not called since last {difference} days{' '}
</Typography>
<Typography as="span" variant="h5" color="var(--grayscale-content-3)">
(Last call attempt: {lastCalledDateLong})
</Typography>
</>
);
}
} else {
@@ -77,6 +85,35 @@ const CallHistoryBox = () => {
);
}
};
const { totalCallsRemaining, nextCallTime, callRemainingPercentage } = useMemo(() => {
let newTotalCallsRemaining = 0;
let newLastCallTime: dayjs.Dayjs | null = null;
let nextCallTime;
let totalMaxCalls = 0;
telephones?.forEach(telephone => {
telephone?.limit.forEach(limitRecord => {
if (limitRecord?.type === LIMIT_TYPE.DAILY) {
newTotalCallsRemaining += limitRecord?.callRemaining || 0;
totalMaxCalls += limitRecord?.maxCalls || 0;
}
if (limitRecord?.callRemaining === 0 && limitRecord.nextCallingTime) {
const nextCallAvailableTime = dayjs(limitRecord.nextCallingTime);
newLastCallTime =
newLastCallTime && nextCallAvailableTime.isBefore(newLastCallTime)
? nextCallAvailableTime
: newLastCallTime;
nextCallTime = newLastCallTime?.format('HH:mm A');
}
});
});
const callRemainingPercentage = Math.round((newTotalCallsRemaining / totalMaxCalls) * 100);
return {
totalCallsRemaining: newTotalCallsRemaining,
nextCallTime,
callRemainingPercentage:
callRemainingPercentage > 90 && callRemainingPercentage < 100 ? 90 : callRemainingPercentage
};
}, [telephones]);
return (
<GridContainer className={styles.callHistoryWrapper}>
@@ -87,7 +124,7 @@ const CallHistoryBox = () => {
padding: '0 8px'
}}
>
<Card className={styles.cardWrapper}>
<Card className={cx(styles.cardWrapper, 'mb-[24px]')}>
<div className={styles.cardHeader}>
<Typography as="div" variant="h3" className={styles.heading}>
Last Called : <LastCalled />
@@ -101,16 +138,43 @@ const CallHistoryBox = () => {
</div>
</div>
<div className={styles.cardHeader}>
<Typography as="div" variant="h4" className={cx(styles.subtext, styles.pv0)}>
Total Call Attempts
</Typography>
<div className="flex gap-[8px] items-center">
<CircularProgress
sqSize={20}
percentage={callRemainingPercentage}
strokeWidth={3}
withAnimation
/>
<Typography variant="h5" color="var(--grayscale-2)">
{totalCallsRemaining} calls remaining
</Typography>
{nextCallTime ? (
<>
<Typography variant="h5" color="var(--grayscale-3)">
|
</Typography>
<Typography variant="h5" color="var(--grayscale-2)">
Next call attempt available
<Typography
as="span"
variant="h5"
className="font-medium"
color="var(--orange-darkbase)"
>
{totalCallsRemaining === 0 ? 'tommorow' : ` at ${nextCallTime}`}
</Typography>
</Typography>
</>
) : null}
</div>
<div>
<Typography as="div" variant="h4" className={cx(styles.subtext, styles.pv0)}>
Last {NO_OF_RECORDS} calls Status
</Typography>
</div>
</div>
<div className={styles.seperator} />
</Card>
<Card className={styles.cardWrapper}>
<div className="overviewAccordion">
{!ptpStatuses?.length ? (
<div className={styles.accordionHeader}>

View File

@@ -76,6 +76,9 @@
align-items: center;
padding: 8px 16px;
flex-basis: 100%;
.progress {
stroke: var(--blue-base);
}
}
}

View File

@@ -1,107 +1,15 @@
import React from 'react';
import Typography from '@navi/web-ui/lib/primitives/Typography';
import styles from './index.module.scss';
import CallHistory from 'src/components/CallHistory/CallHistory';
import { ITelephones } from '../../interfaces/CaseDetail.type';
import { DateFormat, getFormatDate, utcToIST } from 'src/utils/DateHelper';
import OpenInNewTabIcon from 'src/assets/icons/OpenInNewTabIcon';
import { Tooltip, TooltipContent, TooltipTrigger } from 'src/components/TooltipV2/TooltipV2';
import { PhoneNumberSourceText } from '@cp/src/components/Ameyo/interfaces';
import cx from 'classnames';
import PocNumbersTag from '../PocTag/Index';
import { POC_TAG_SOURCES } from '@cp/src/pages/CaseDetails/components/PocTag/constants';
const NO_OF_RECORDS = 5;
import PhoneNumberContactCard from '@cp/pages/CaseDetails/components/PhoneNumber/PhoneNumberContactCard';
const CustomerDeviceData: React.FC<ITelephones> = props => {
const { data = [] } = props;
const openTrueCallerTab = (number: string) => {
window.open(`tel:${number}`, '_blank');
};
return (
<div className={styles.customerDeviceContainer}>
{data?.map(callData => {
return callData?.number ? (
<div className={styles.callHistoryCard} key={callData?.number}>
<div className={styles.dateWrapper}>
<div className={styles.dateText}>
{getFormatDate(
utcToIST(callData?.createdAt as unknown as Date),
DateFormat.DD_MMM_YYYY
)}
</div>
</div>
<div className={styles.phoneNumberWrapperStyles}>
{callData?.isPoc ? (
<PocNumbersTag source={POC_TAG_SOURCES.CONTACT_TAB} number={callData?.number} />
) : (
<div className={styles.phoneNumber}> {callData?.number} </div>
)}
{callData?.new && (
<div className={cx(styles.newNumberTag, 'absolute top-[4px] right-0')}>New</div>
)}
</div>
<div className={styles.callHistoryTruecallerWrapper}>
<CallHistory
list={callData?.callHistory || []}
noOfRecords={NO_OF_RECORDS}
showTooltip
/>
<Tooltip>
<TooltipTrigger>
<div
className={styles.trueCallerWrapper}
onClick={() => openTrueCallerTab(callData?.number)}
>
<OpenInNewTabIcon />
<div className={styles.truecallerText}>Truecaller ID</div>
</div>
</TooltipTrigger>
<TooltipContent className={styles.sourceTooltipContent}>
Open Truecaller
</TooltipContent>
</Tooltip>
</div>
<div className={styles.sourcesWrapper}>
<span className={styles.sourceTextNames}>
{callData?.sources.slice(0, 2).map((source, index) => (
<>
<span>
{PhoneNumberSourceText[source] ?? source}
{index == 0 && callData?.sources?.length > 1 ? ', ' : ''}
</span>
</>
))}
</span>
{callData?.sources?.length > 2 && (
<>
<Tooltip>
<TooltipTrigger>
<span className={styles.triggerText}>
{' +' + (callData?.sources?.length - 2) + ' more'}
</span>
</TooltipTrigger>
<TooltipContent className={styles.sourceTooltipContent}>
{callData?.sources
?.slice(2, callData?.sources?.length)
.map((source, index) => (
<span key={index}>
{PhoneNumberSourceText[source] ?? source}{' '}
{index < callData.sources.length - 3 ? ', ' : ''}
</span>
))}
</TooltipContent>
</Tooltip>
</>
)}
</div>
</div>
) : null;
})}
{data?.map(callData => (
<PhoneNumberContactCard key={callData.number} callData={callData} />
))}
</div>
);
};

View File

@@ -0,0 +1,170 @@
import styles from '@cp/pages/CaseDetails/components/PhoneNumber/index.module.scss';
import { DateFormat, getFormatDate, utcToIST } from '@cp/utils/DateHelper';
import { POC_TAG_SOURCES } from '@cp/pages/CaseDetails/components/PocTag/constants';
import cx from 'classnames';
import CircularProgress from '@cp/components/ProgressBars/circularProgress/CircularProgress';
import Typography from '@primitives/Typography';
import CallHistory from '@cp/components/CallHistory/CallHistory';
import { Tooltip, TooltipContent, TooltipTrigger } from '@cp/components/TooltipV2/TooltipV2';
import OpenInNewTabIcon from '@cp/assets/icons/OpenInNewTabIcon';
import { PhoneNumberSourceText } from '@cp/components/Ameyo/interfaces';
import React, { useMemo } from 'react';
import { ITelePhoneData, LIMIT_TYPE } from '@cp/pages/CaseDetails/interfaces/CaseDetail.type';
import PocNumbersTag from '../PocTag/Index';
import dayjs from 'dayjs';
import InfoIconOutlined from '@cp/assets/images/icons/InfoIconOutlined';
const NO_OF_RECORDS = 5;
const PhoneNumberContactCard = ({ callData }: { callData: ITelePhoneData }) => {
const openTrueCallerTab = (number: string) => {
window.open(`tel:${number}`, '_blank');
};
const { dailyLimit, hourlyLimit, callRemainingPercentage } = useMemo(() => {
const dailyLimit = callData?.limit?.find(limitRecord => limitRecord.type === LIMIT_TYPE.DAILY);
const hourlyLimit = callData?.limit?.find(
limitRecord => limitRecord.type === LIMIT_TYPE.HOURLY
);
return {
dailyLimit,
hourlyLimit,
callRemainingPercentage: dailyLimit
? Math.floor((dailyLimit?.callRemaining / dailyLimit?.maxCalls) * 100)
: 0
};
}, [callData]);
return callData?.number ? (
<div className={styles.callHistoryCard} key={callData?.number}>
<div className={styles.dateWrapper}>
<div className={styles.dateText}>
{getFormatDate(utcToIST(callData?.createdAt as unknown as Date), DateFormat.DD_MMM_YYYY)}
</div>
</div>
<div className={styles.phoneNumberWrapperStyles}>
{callData?.isPoc ? (
<PocNumbersTag source={POC_TAG_SOURCES.CONTACT_TAB} number={callData?.number} />
) : (
<div className={styles.phoneNumber}> {callData?.number} </div>
)}
{callData?.new && (
<div className={cx(styles.newNumberTag, 'absolute top-[4px] right-0')}>New</div>
)}
</div>
<div className={styles.callHistoryTruecallerWrapper}>
<div className={styles.limitsContainer}>
<div className={styles.limitsProgress}>
<CircularProgress
sqSize={20}
percentage={
callRemainingPercentage > 90 && callRemainingPercentage < 100
? 90
: callRemainingPercentage
}
strokeWidth={3}
withAnimation
/>
<Typography className="flex items-center" variant="h5" color="var(--grayscale-2)">
<Typography
variant="h5"
as="span"
color={dailyLimit?.callRemaining === 0 ? 'var(--red-base)' : 'var(--grayscale-2)'}
className="mr-[2px]"
>
{dailyLimit?.callRemaining}
</Typography>
calls remaining
{dailyLimit?.remarks ? (
<Tooltip>
<TooltipTrigger>
<InfoIconOutlined className="ml-[4px]" fillColor="var(--grayscale-3)" />
</TooltipTrigger>
<TooltipContent className={styles.sourceTooltipContent}>
{dailyLimit?.remarks}
</TooltipContent>
</Tooltip>
) : null}
</Typography>
</div>
{dailyLimit?.callRemaining === 0 ? (
<div className={styles.limitswrapper}>
<Typography variant="h5" color="var(--grayscale-2)">
Next call attempt:{' '}
<Typography
as="span"
variant="h5"
className="font-medium"
color="var(--orange-darkbase)"
>
tomorrow
</Typography>
</Typography>
</div>
) : hourlyLimit?.nextCallingTime ? (
<Typography variant="h5" color="var(--grayscale-2)">
Next call attempt available at
<Typography
as="span"
variant="h5"
className="font-medium"
color="var(--orange-darkbase)"
>
{dayjs(hourlyLimit?.nextCallingTime).format('HH:mm A')}
</Typography>
</Typography>
) : null}
</div>
<div>
<CallHistory list={callData?.callHistory || []} noOfRecords={NO_OF_RECORDS} showTooltip />
<Tooltip>
<TooltipTrigger>
<div
className={styles.trueCallerWrapper}
onClick={() => openTrueCallerTab(callData?.number)}
>
<OpenInNewTabIcon />
<div className={styles.truecallerText}>Truecaller ID</div>
</div>
</TooltipTrigger>
<TooltipContent className={styles.sourceTooltipContent}>Open Truecaller</TooltipContent>
</Tooltip>
</div>
</div>
<div className={styles.sourcesWrapper}>
<span className={styles.sourceTextNames}>
{callData?.sources.slice(0, 2).map((source, index) => (
<>
<span>
{PhoneNumberSourceText[source] ?? source}
{index == 0 && callData?.sources?.length > 1 ? ', ' : ''}
</span>
</>
))}
</span>
{callData?.sources?.length > 2 && (
<>
<Tooltip>
<TooltipTrigger>
<span className={styles.triggerText}>
{' +' + (callData?.sources?.length - 2) + ' more'}
</span>
</TooltipTrigger>
<TooltipContent className={styles.sourceTooltipContent}>
{callData?.sources?.slice(2, callData?.sources?.length).map((source, index) => (
<span key={index}>
{PhoneNumberSourceText[source] ?? source}{' '}
{index < callData.sources.length - 3 ? ', ' : ''}
</span>
))}
</TooltipContent>
</Tooltip>
</>
)}
</div>
</div>
) : null;
};
export default PhoneNumberContactCard;

View File

@@ -2,7 +2,7 @@
padding: 0px 6px 20px 0px;
display: flex;
flex-wrap: wrap;
margin: 0px 24px;
margin: 0px 16px;
}
.header {
display: flex;
@@ -54,6 +54,7 @@
top: 0px;
border-bottom-left-radius: 11px;
background: var(--bg-secondary);
z-index: 1;
.dateText {
color: var(--grayscale-2);
font-weight: 400;
@@ -64,7 +65,7 @@
}
.sourcesWrapper {
background: var(--bg-secondary);
background: transparent;
padding: 9px 16px;
font-weight: 500;
font-size: 12px;
@@ -83,7 +84,24 @@
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 12px 16px 12px;
padding: 12px 16px;
background-color: var(--blue-light-bg);
gap: 8px 4px;
}
.limitsContainer {
.limitsProgress {
display: flex;
align-items: center;
gap: 4px;
flex-basis: 50%;
}
.limitswrapper {
margin-top: 8px;
display: flex;
align-items: center;
background-color: var(--blue-light-bg);
gap: 4px;
}
}
.trueCallerWrapper {
background: var(--bg-blue);
@@ -92,6 +110,9 @@
align-items: center;
padding: 4px;
cursor: pointer;
width: fit-content;
margin-top: 6px;
margin-left: auto;
.truecallerText {
font-weight: 500;
font-size: 12px;
@@ -103,9 +124,10 @@
.phoneNumberWrapperStyles {
display: flex;
align-items: center;
padding: 12px 12px 12px 12px;
padding: 12px 16px;
position: relative;
width: 68%;
width: 100%;
background-color: transparent;
}
.heading {

View File

@@ -3,19 +3,31 @@ import Typography from '@navi/web-ui/lib/primitives/Typography/Typography';
import styles from './styles.module.scss';
import cx from 'classnames';
import { POC_TAG_SOURCES } from './constants';
import ClockIcon from '@cp/assets/icons/ClockIcon';
import React from 'react';
import { ICallHistory } from '@cp/components/CallHistory/interfaces';
import CallHistory from '@cp/components/CallHistory/CallHistory';
import { IPhoneNumberLimit } from '@cp/pages/CaseDetails/interfaces/CaseDetail.type';
import dayjs from 'dayjs';
const PocNumbersTag = ({
source,
number,
option
option,
callHistory,
dailyLimit,
hourlyLimit
}: {
source: POC_TAG_SOURCES;
number?: string;
option?: string;
callHistory?: ICallHistory[];
dailyLimit?: IPhoneNumberLimit;
hourlyLimit?: IPhoneNumberLimit;
}) => {
if (source === POC_TAG_SOURCES.CONTACT_TAB) {
return (
<div className="relative w-[153px] h-[26px] bg-bg-secondary ml-[-12px] rounded-r-[1px]">
<div className="relative w-[153px] h-[26px] bg-bg-secondary ml-[-16px] rounded-r-[1px]">
<Tooltip placement="top" hiddenPadding={10} hideStrategy="referenceHidden">
<TooltipTrigger tooltipTriggerClassName="tooltipTriggerWrapper">
<div className="flex justify-between gap-[10px]">
@@ -39,26 +51,52 @@ const PocNumbersTag = ({
);
} else if (source === POC_TAG_SOURCES.FEEDBACK_FORM) {
return (
<div className="relative w-[153px] h-[26px] bg-bg-secondary ml-[-12px] rounded-r-[1px]">
<Tooltip placement="left" hiddenPadding={10} hideStrategy="referenceHidden">
<TooltipTrigger tooltipTriggerClassName="tooltipTriggerWrapper">
<div className="flex justify-between gap-[10px]">
<div className="w-[124x] h-[26px] bg-[#EFE3FC] pl-[15px] pr-[19px] flex items-center">
<div className="w-[88px] text-grayscale-1 pr-[5px] text-sm">{option}</div>
<div className={cx(styles.triangleDown)} />
</div>
<Typography
variant="p5"
className=" flex items-center justify-start w-full text-grayscale-2 ml-[-9px]"
>
Try
<div className="flex flex-wrap gap-x-[10px] gap-y-0 w-[100%]">
<div className="flex justify-between items-center w-[100%]">
<div className="relative w-[153px] h-[26px] bg-bg-secondary ml-[-12px] rounded-r-[1px]">
<Tooltip placement="left" hiddenPadding={10} hideStrategy="referenceHidden">
<TooltipTrigger tooltipTriggerClassName="tooltipTriggerWrapper">
<div className="flex justify-between gap-[10px]">
<div className="w-[124x] h-[26px] bg-[#EFE3FC] pl-[15px] pr-[19px] flex items-center">
<div className="w-[88px] text-grayscale-1 pr-[5px] text-sm">{option}</div>
<div className={cx(styles.triangleDown)} />
</div>
<Typography
variant="p5"
className=" flex items-center justify-start w-full text-grayscale-2 ml-[-9px]"
>
Try
</Typography>
</div>
</TooltipTrigger>
<TooltipContent className={styles.tooltipWrapper}>
New number added. Try calling on them
</TooltipContent>
</Tooltip>
</div>
{callHistory && <CallHistory list={callHistory} noOfRecords={5} />}
</div>
{dailyLimit?.callRemaining === 0 ? (
<div className="flex items-center gap-[4px] mt-[8px]">
<ClockIcon />
<Typography variant="h5" color="var(--grayscale-2)">
Next call attempt available{' '}
<Typography variant="h5" as="span" color="var(--orange-darkbase)">
tomorrow
</Typography>
</div>
</TooltipTrigger>
<TooltipContent className={styles.tooltipWrapper}>
New number added. Try calling on them
</TooltipContent>
</Tooltip>
</Typography>
</div>
) : hourlyLimit?.nextCallingTime ? (
<div className="flex items-center gap-[4px] mt-[8px]">
<ClockIcon />
<Typography variant="h5" color="var(--grayscale-2)">
Next call attempt available
<Typography variant="h5" as="span" color="var(--orange-darkbase)">
{dayjs(hourlyLimit?.nextCallingTime).format('HH:mm A')}
</Typography>
</Typography>
</div>
) : null}
</div>
);
} else {

View File

@@ -19,3 +19,5 @@ export const DUE_PAID_STATUS = {
ON_TIME: 'On-time',
LATE: 'Late'
} as { [key: string]: string };
export const CALL_SOURCE_LIMIT = 2;

View File

@@ -54,6 +54,7 @@ interface CallBridgeProps {
telephoneSource?: string;
telephoneRefId?: string;
currentCallType?: ECallingType;
disableCallButton?: boolean;
}
const buildCreateCallBridgeRequestBody = (
@@ -83,7 +84,8 @@ const CallBridge = (props: CallBridgeProps) => {
interactionType,
telephoneSource,
telephoneRefId,
currentCallType
currentCallType,
disableCallButton
} = props;
const dispatch = useDispatch();
const navigate = useNavigate();
@@ -355,7 +357,7 @@ const CallBridge = (props: CallBridgeProps) => {
<>
<div>
<Button
disabled={disableButtons}
disabled={disableButtons || disableCallButton}
onClick={() => {
if (disableButtons) return;
handleCallClick(feedback.telephone || '');

View File

@@ -1,7 +1,7 @@
import { Button, Typography } from '@navi/web-ui/lib/primitives';
import React, { useCallback, useMemo, useState, useRef } from 'react';
import React, { useCallback, useMemo, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams, useSearchParams } from 'react-router-dom';
import { useParams } from 'react-router-dom';
import CallHistory from 'src/components/CallHistory/CallHistory';
import Dropdown from 'src/components/Dropdown';
import { SelectPickerOptionProps } from 'src/components/interfaces';
@@ -17,12 +17,11 @@ import {
} from '../../interfaces/index.type';
import CallBridge from '../CallBridge/CallBridge';
import styles from './index.module.scss';
import { readQueryParams } from '../../../../../utils/QueryParamsHelper';
import { addClickstreamEvent } from '../../../../../service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '../../../../../service/clickStream.constant';
import { ITelePhoneData } from '../../../interfaces/CaseDetail.type';
import { readQueryParams } from '@cp/utils/QueryParamsHelper';
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@cp/src/service/clickStream.constant';
import { IPhoneNumberLimit, LIMIT_TYPE } from '../../../interfaces/CaseDetail.type';
import { Tooltip, TooltipContent, TooltipTrigger } from 'src/components/TooltipV2/TooltipV2';
import { Span } from '@elastic/apm-rum';
import cx from 'classnames';
import { setIsFeedbackFormDirty } from '../../reducer/feedbackSlice';
import PocNumbersTag from '../../../components/PocTag/Index';
@@ -31,6 +30,9 @@ import GoogleSearchIcon from '@cp/assets/images/icons/GoogleSearchIcon';
import { toast } from '@primitives/Toast';
import { searchGoogle } from '@cp/utils/commonUtils';
import { Roles } from '@cp/pages/auth/constants/AuthConstants';
import ClockIcon from '@cp/assets/icons/ClockIcon';
import dayjs from 'dayjs';
import { CALL_SOURCE_LIMIT } from '@cp/pages/CaseDetails/constants/Overview.constant';
interface DefaultContactsProps {
feedback: IFeedbackForm;
@@ -43,6 +45,8 @@ interface Telephone {
label: string;
source: JSX.Element;
rightAdornment: JSX.Element;
dailyLimit: IPhoneNumberLimit;
hourlyLimit: IPhoneNumberLimit;
}
const DefaultContacts: React.FC<DefaultContactsProps> = props => {
@@ -57,6 +61,7 @@ const DefaultContacts: React.FC<DefaultContactsProps> = props => {
editAccessFlag:
state?.caseDetail?.pageData?.[createKey(loanId, customerId)]?.details?.data?.editAccessFlag
}));
const [isOptionDisabled, setIsOptionDisabled] = React.useState(false);
const isGlobalAccessGiven = user?.roles?.includes(Roles.ROLE_GLOBAL_ACCESS);
const disableCTAs = isGlobalAccessGiven ? !editAccessFlag : false;
@@ -93,31 +98,38 @@ const DefaultContacts: React.FC<DefaultContactsProps> = props => {
source: (
<Typography className={styles.sourceText} variant="p3">
{displaySourceText(tel?.sources)}
{tel.sources.length > 2 && (
{tel.sources.length > CALL_SOURCE_LIMIT && (
<>
<Tooltip>
<TooltipTrigger>
<span className={styles.sourceTextExtras}>
{' +' + (tel.sources.length - 2) + ' more'}
</span>
{')'}
<div>
<span className={styles.sourceTextExtras}>
{' +' + (tel.sources.length - CALL_SOURCE_LIMIT) + ' more'}
</span>
{')'}
</div>
</TooltipTrigger>
<TooltipContent className={styles.tooltip}>
{[...tel.sources.slice(2, tel.sources.length)].map((i, index) => (
<>
<span>
{i} {index < tel.sources.length - 3 ? ', ' : ''}
</span>
</>
))}
{[...tel.sources.slice(CALL_SOURCE_LIMIT, tel.sources.length)].map(
(i, index) => (
<>
<span>
{i} {index < tel.sources.length - CALL_SOURCE_LIMIT - 1 ? ', ' : ''}
</span>
</>
)
)}
</TooltipContent>
</Tooltip>
</>
)}
</Typography>
),
rightAdornment: <CallHistory list={tel.callHistory} noOfRecords={5} />
callHistory: tel.callHistory,
rightAdornment: null,
dailyLimit: tel.limit.find(l => l.type === LIMIT_TYPE.DAILY),
hourlyLimit: tel.limit.find(l => l.type === LIMIT_TYPE.HOURLY)
})) || [];
if (feedback.telephoneNotRequired) {
@@ -136,23 +148,39 @@ const DefaultContacts: React.FC<DefaultContactsProps> = props => {
return newTelephoneList;
}, [telephones?.data, feedback.telephoneNotRequired]);
const previousSelectedTelephone = useRef<Telephone | undefined>(telephoneList?.[0]);
// const previousSelectedTelephone = useRef<Telephone | undefined>(telephoneList?.[0]);
const firstAvailableNumber = useMemo(
() =>
telephoneList?.find(
t => t?.dailyLimit?.callRemaining !== 0 && t?.hourlyLimit?.callRemaining !== 0
) || telephoneList?.[0],
[telephoneList]
);
const previousSelectedTelephone = useRef<Telephone | undefined>(firstAvailableNumber);
const getDefaultPhoneNumber = useMemo(() => {
if (ameyoQueryParams?.phoneNumber) {
return telephoneList?.find(t => t.value === ameyoQueryParams?.phoneNumber);
}
const firstAvailableNumber = telephoneList?.find(
t => t?.dailyLimit?.callRemaining !== 0 && t?.hourlyLimit?.callRemaining !== 0
);
const currentSelectedTelephone = previousSelectedTelephone.current
? previousSelectedTelephone.current
: telephoneList?.[0];
: firstAvailableNumber;
!feedback.telephoneNotRequired &&
dispatchFeedback({
type: FeedbackFormKind.UPDATE_TELEPHONES,
payload: { telephone: currentSelectedTelephone?.value as string }
});
return previousSelectedTelephone.current
? previousSelectedTelephone.current
: telephoneList?.[0];
if (
currentSelectedTelephone?.dailyLimit?.callRemaining === 0 ||
currentSelectedTelephone?.hourlyLimit?.callRemaining === 0
) {
setIsOptionDisabled(true);
} else {
setIsOptionDisabled(false);
}
return currentSelectedTelephone ? currentSelectedTelephone : telephoneList?.[0];
}, [telephoneList, ameyoQueryParams?.phoneNumber, feedback.telephone]);
const handleTelephoneChange = useCallback(
@@ -164,6 +192,14 @@ const DefaultContacts: React.FC<DefaultContactsProps> = props => {
type: FeedbackFormKind.UPDATE_TELEPHONES,
payload: { telephone: option.value as string }
});
if (
previousSelectedTelephone.current?.dailyLimit?.callRemaining === 0 ||
previousSelectedTelephone.current?.hourlyLimit?.callRemaining === 0
) {
setIsOptionDisabled(true);
} else {
setIsOptionDisabled(false);
}
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_Feedback_PhoneNumberDropdown, {
phone: option?.value,
source: option?.source
@@ -172,15 +208,20 @@ const DefaultContacts: React.FC<DefaultContactsProps> = props => {
},
[telephoneList]
);
const getSelectedSourceLabels = () => {
const getSelectedSourceLabels = useCallback(() => {
if (feedback.telephoneNotRequired) {
return '';
}
const firstAvailableNumber =
telephoneList?.find(
t => t?.dailyLimit?.callRemaining !== 0 && t?.hourlyLimit?.callRemaining !== 0
) || telephoneList?.[0];
const selectedSourceLabels =
telephones?.data?.find(t => t.number === previousSelectedTelephone.current?.value)?.sources ||
telephones?.data?.[0]?.sources;
telephones?.data?.find(t => t.number === firstAvailableNumber?.value)?.sources;
return selectedSourceLabels?.join(', ');
};
}, [previousSelectedTelephone.current]);
const handleGoogleSearch = () => {
if (feedback.telephoneNotRequired) {
@@ -234,15 +275,48 @@ const DefaultContacts: React.FC<DefaultContactsProps> = props => {
return (
<>
{metadata?.isPocNumber ? (
<PocNumbersTag source={POC_TAG_SOURCES.FEEDBACK_FORM} option={option} />
<PocNumbersTag
callHistory={metadata?.callHistory}
source={POC_TAG_SOURCES.FEEDBACK_FORM}
option={option}
dailyLimit={metadata?.dailyLimit}
hourlyLimit={metadata?.hourlyLimit}
/>
) : (
<Typography variant="p3" className="text-sm">
{option}
</Typography>
<div className="w-[100%]">
<div className="flex items-center justify-between">
<Typography variant="p3" className="text-sm">
{option}
</Typography>
<CallHistory list={metadata?.callHistory} noOfRecords={5} />
</div>
{metadata?.dailyLimit?.callRemaining === 0 ? (
<div className="flex items-center gap-[4px] mt-[8px]">
<ClockIcon />
<Typography variant="h5" color="var(--grayscale-2)">
Next call attempt available{' '}
<Typography variant="h5" as="span" color="var(--orange-darkbase)">
tomorrow
</Typography>
</Typography>
</div>
) : metadata?.hourlyLimit?.nextCallingTime ? (
<div className="flex items-center gap-[4px] mt-[8px]">
<ClockIcon />
<Typography variant="h5" color="var(--grayscale-2)">
Next call attempt available from{' '}
<Typography variant="h5" as="span" color="var(--orange-darkbase)">
{dayjs(metadata?.hourlyLimit?.nextCallingTime).format('HH:mm A')}
</Typography>
</Typography>
</div>
) : null}
</div>
)}
</>
);
}}
selectPickerItemClass={styles.selectedPickerItemLimits}
/>
<Tooltip placement="top" tooltipGap={10}>
<TooltipTrigger>
@@ -271,6 +345,7 @@ const DefaultContacts: React.FC<DefaultContactsProps> = props => {
customerId={customerId}
interactionType={InteractionType.CALL_BRIDGE}
currentCallType={currentCallType}
disableCallButton={isOptionDisabled}
/>
</div>
);

View File

@@ -47,6 +47,9 @@
}
}
.selectedPickerItemLimits {
background-color: rgba(246, 247, 249, 0.8);
}
.defaultContacts {
.phoneNumberSelectContainer {
margin-bottom: 15px;

View File

@@ -38,7 +38,7 @@ import {
import Loader from '../../../components/Loader/Loader';
import CancelCircleFilled from '../../../assets/icons/CancelCircledFilled';
import { fetchFeedbackHistory } from '../actions/overviewActions';
import { LOCAL_STORAGE_KEYS, SESSION_STORAGE_KEYS } from '../../../constants/StorageKeys';
import { LOCAL_STORAGE_KEYS, SESSION_STORAGE_KEYS } from '@cp/constants/StorageKeys';
import { FeedbackTabsEnum } from './component/FeedbackTabs/FeedbackTabs';
import SkipTracing from './component/Contacts/SkipTracing';
import Spade from './component/Contacts/Spade';
@@ -46,9 +46,9 @@ import DefaultContacts from './component/Contacts/DefaultContacts';
import NextPreviousCaseActions from './component/NextPreviousCaseActions';
import { toast } from '@navi/web-ui/lib/primitives/Toast';
import { setAmeyoCallDetails, setBlankDisposition } from 'src/reducers/commonSlice';
import { createQueryParams, readQueryParams } from '../../../utils/QueryParamsHelper';
import { addClickstreamEvent } from '../../../service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '../../../service/clickStream.constant';
import { createQueryParams, readQueryParams } from '@cp/utils/QueryParamsHelper';
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@cp/src/service/clickStream.constant';
import RadioGroup from '@navi/web-ui/lib/components/RadioGroup';
import { OLD_CALLING_TYPES, REVAMPED_CALLING_TYPES } from '../constants';
import {
@@ -65,6 +65,7 @@ import {
import isLitmusExperimentEnabled from '@cp/utils/isLitmusExperimentEnabled';
import { LITMUS_EXPERIMENT_NAMES } from '@cp/constants/litmusExperimentNames';
import { Roles } from '@cp/pages/auth/constants/AuthConstants';
import { getTelephonesV2 } from '@cp/pages/CaseDetails/actions/casesDetailsActions';
interface FeedbackFromProps {
isDisableNextPreviousCaseAction?: boolean;
@@ -602,11 +603,11 @@ const FeedbackFrom = (props: FeedbackFromProps) => {
dispatch(fetchFeedbackHistory(loanId, customerId, payload));
dispatch(resetFeedbackQuestionTypes());
dispatch(setSkipTracingInitialQuestions([]));
dispatch(getTelephonesV2(loanId, customerId));
setSkipTracingType(null);
localStorage.removeItem(LOCAL_STORAGE_KEYS.AMEYO_INTERACTION_ID);
sessionStorage.removeItem(SESSION_STORAGE_KEYS.INTERACTION_ID);
setAmeyoCallDetails({ interactionId: '', recipientNumber: '', crtObjectId: '' });
toggleLoading();
reset();
if (ameyoQueryParams) {

View File

@@ -341,6 +341,20 @@ export interface ILegalDocument extends IApiData {
loading: boolean;
}
export enum LIMIT_TYPE {
DAILY = 'DAILY',
HOURLY = 'HOURLY'
}
export interface IPhoneNumberLimit {
type: LIMIT_TYPE;
callsMade: number;
callRemaining: number;
maxCalls: number;
nextCallingTime: string;
remarks: string;
}
export interface ITelePhoneData {
type: string;
source: string;
@@ -355,6 +369,7 @@ export interface ITelePhoneData {
oldestSource: string;
createdAt: string;
isPoc: boolean;
limit: IPhoneNumberLimit[];
}
interface IAddresses extends IApiData {