From aaf7220d4b1f4abfc8a12dd28e0a86bbb4979a98 Mon Sep 17 00:00:00 2001 From: Ashish Deo Date: Wed, 12 Feb 2025 19:28:39 +0530 Subject: [PATCH] NTP-30201 | Upgrading Longhorn for CIU Team (#1379) * NTP-30201 | Upgrading Longhorn for CIU Team * NTP-30201 | Upgrading Longhorn for CIU Team * NTP-30201 | Upgrading Longhorn for CIU Team * NTP-30201 | Upgrading Longhorn for CIU Team * NTP-30201 | Upgrading Longhorn for CIU Team | PR comments resolved * NTP-30201 | Upgrading Longhorn for CIU Team | web-ui update * NTP-30201 | Upgrading Longhorn for CIU Team | pr fix --- .../actions/casesDetailsActions.ts | 14 ++- .../CaseDetails/actions/moreDetailsActions.ts | 16 ++-- .../bankStatement/AddNumberPopper.tsx | 7 +- .../MoreDetails/AddNewNumberOverlay.tsx | 87 ++++++++++++++----- .../components/MoreDetails/index.module.scss | 17 +++- .../components/MoreDetails/utils/utils.ts | 14 +++ .../PhoneNumber/PhoneNumberContactCard.tsx | 42 ++++++++- .../components/PhoneNumber/index.tsx | 2 +- .../constants/MoreDetails.constant.tsx | 2 + .../CaseDetails/interfaces/CaseDetail.type.ts | 13 +++ .../CaseDetails/reducers/caseDetailSlice.ts | 11 ++- src/utils/ApiHelper.ts | 19 ++-- 12 files changed, 193 insertions(+), 51 deletions(-) create mode 100644 src/pages/CaseDetails/components/MoreDetails/utils/utils.ts diff --git a/src/pages/CaseDetails/actions/casesDetailsActions.ts b/src/pages/CaseDetails/actions/casesDetailsActions.ts index 1582c785..29cf43bd 100644 --- a/src/pages/CaseDetails/actions/casesDetailsActions.ts +++ b/src/pages/CaseDetails/actions/casesDetailsActions.ts @@ -1,9 +1,10 @@ import { Dispatch } from '@reduxjs/toolkit'; -import axiosInstance, { ApiKeys, getApiUrl } from '../../../utils/ApiHelper'; +import axiosInstance, { ApiKeys, getApiUrl, logError } from '../../../utils/ApiHelper'; import { setCaseDetail, setCaseDetailLoading, setContacts, + setCustomerConfidence, setCustomerInfo, setEmiBreakup, setEmiBreakupError, @@ -258,3 +259,14 @@ export const removeContact = ( toast('Unable to remove contact', { type: 'error' }); }); }; + +export const getCustomerConfidence = () => (dispatch: Dispatch) => { + axiosInstance + .get(getApiUrl(ApiKeys.CUSTOMER_CONFIDENCE)) + .then(res => { + dispatch(setCustomerConfidence(res.data)); + }) + .catch(() => { + logError('Error fetching customer confidence'); + }); +}; diff --git a/src/pages/CaseDetails/actions/moreDetailsActions.ts b/src/pages/CaseDetails/actions/moreDetailsActions.ts index 425a64f0..11f98d13 100644 --- a/src/pages/CaseDetails/actions/moreDetailsActions.ts +++ b/src/pages/CaseDetails/actions/moreDetailsActions.ts @@ -4,8 +4,7 @@ import { STORE_KEYS, ToastMessage } from '../constants'; import { setCaseDetail, setCaseDetailError, - setCaseDetailLoading, - setSecondarySources + setCaseDetailLoading } from '../reducers/caseDetailSlice'; import { Dispatch } from '@reduxjs/toolkit'; @@ -68,6 +67,7 @@ export const postAlternateNumber = caseReferenceId, caseBusinessVertical, alternateNumber, + customerConfidence, source, successCallback, cb, @@ -80,6 +80,7 @@ export const postAlternateNumber = caseReferenceId: string; caseBusinessVertical: string; alternateNumber: string; + customerConfidence?: string; source: string; successCallback?: () => void; cb?: (isModalOpen: boolean) => void; @@ -98,7 +99,8 @@ export const postAlternateNumber = secondarySource: source, remarkForSecondarySource: otherSource, type: 'Alternate Contact', - numberCustomId: identifier + numberCustomId: identifier, + telephoneCustomerConfidence: customerConfidence } ], { @@ -119,14 +121,6 @@ export const postAlternateNumber = }); }; -export const getSecondarySources = () => (dispatch: Dispatch) => { - const url = getApiUrl(ApiKeys.PHONENUMBER_SECONDARY_SOURCES); - - axiosInstance.get(url).then(response => { - dispatch(setSecondarySources(response.data)); - }); -}; - export const getValidateNumber = async (number: string, customerRefId: string) => { const url = getApiUrl(ApiKeys.VALIDATE_PHONE_NUMBER, { number }); try { diff --git a/src/pages/CaseDetails/components/AllDocuments/bankStatement/AddNumberPopper.tsx b/src/pages/CaseDetails/components/AllDocuments/bankStatement/AddNumberPopper.tsx index a5a1eb26..733194f8 100644 --- a/src/pages/CaseDetails/components/AllDocuments/bankStatement/AddNumberPopper.tsx +++ b/src/pages/CaseDetails/components/AllDocuments/bankStatement/AddNumberPopper.tsx @@ -40,6 +40,7 @@ const AddNumberPopper = (props: IAddNumberPopperProps) => { const [isExisting, setIsExisting] = useState(false); const [numberInvalid, setNumberInvalid] = useState(false); const slashCallStatus = useSelector((state: RootState) => state.common.slashCallStatus); + const isCiuAgent = useSelector((state: RootState) => state.common.isCiuAgent); useEffect(() => { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_DOCUMENTS_BANK_STATEMENT_HYPERLINKING_POPPER); @@ -126,7 +127,11 @@ const AddNumberPopper = (props: IAddNumberPopperProps) => { } }; - return ( + return isCiuAgent ? ( + <> + {children} + + ) : ( diff --git a/src/pages/CaseDetails/components/MoreDetails/AddNewNumberOverlay.tsx b/src/pages/CaseDetails/components/MoreDetails/AddNewNumberOverlay.tsx index 9f606513..4bff0912 100644 --- a/src/pages/CaseDetails/components/MoreDetails/AddNewNumberOverlay.tsx +++ b/src/pages/CaseDetails/components/MoreDetails/AddNewNumberOverlay.tsx @@ -4,13 +4,9 @@ import AddIcon from '@icons/AddIcon'; import styles from './index.module.scss'; import { useParams } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; -import { useCallback } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import Typography from '@navi/web-ui/lib/primitives/Typography/Typography'; -import { - getSecondarySources, - getValidateNumber, - postAlternateNumber -} from '../../actions/moreDetailsActions'; +import { getValidateNumber, postAlternateNumber } from '../../actions/moreDetailsActions'; import { createKey } from 'src/utils/CaseDetail.utils'; import { addClickstreamEvent } from '../../../../service/clickStreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from '../../../../service/clickStream.constant'; @@ -21,20 +17,25 @@ import { Roles } from '@cp/pages/auth/constants/AuthConstants'; import { useForm } from 'react-hook-form'; import cx from 'classnames'; import BorderedInputTextBox from './BorderedInputTextBox'; -import { getTelephonesV2 } from '../../actions/casesDetailsActions'; +import { getCustomerConfidence, getTelephonesV2 } from '../../actions/casesDetailsActions'; import SingleAutoDropdown from './SingleAutoDropdown'; import { + CUSTOMER_CONFIDENCE, OTHER_SOURCE, OtherSourceValidations, PHONE_NUMBER, PhoneNumberValidations, SOURCE } from '../../constants/MoreDetails.constant'; +import { ICustomerConfidenceLevel } from '../../interfaces/CaseDetail.type'; +import { Chip } from '@navi/web-ui/lib/primitives'; +import { convertToRadioOptions } from './utils/utils'; export interface AddNumberProps { phoneNumber: string; source: string; otherSource: string; + customerConfidence: string; } const AddNewNumberOverlay: React.FC = () => { @@ -50,12 +51,15 @@ const AddNewNumberOverlay: React.FC = () => { ); const user = useSelector((state: RootState) => state.common.userData); const isGlobalAccessRoleGiven = user?.roles?.includes(Roles.ROLE_GLOBAL_ACCESS); + const isCiuAgent = useSelector((state: RootState) => state.common.isCiuAgent); const disableCTAs = isGlobalAccessRoleGiven ? !editAccessFlag : false; - const telephones = useSelector( - (state: RootState) => - state.caseDetail.pageData?.[createKey(loanId, customerId)]?.telephonesv2?.data - ); - const secondarySources = useSelector((state: RootState) => state.caseDetail.secondarySources); + const [isDisabledSubmitButton, setIsDisabledSubmitButton] = useState(false); + + const customerConfidence = useSelector((state: RootState) => state.caseDetail.customerConfidence); + const telephoneCustomerConfidence = customerConfidence?.telephoneCustomerConfidence; + const secondarySources = customerConfidence?.longhornSecondarySources; + + const radioOptionsList = convertToRadioOptions(telephoneCustomerConfidence); const optionsList = secondarySources?.map(source => ({ @@ -63,7 +67,7 @@ const AddNewNumberOverlay: React.FC = () => { value: source.value })) || []; - const [isModalOpen, setIsModalOpen] = React.useState(false); + const [isModalOpen, setIsModalOpen] = useState(false); const { handleSubmit, @@ -79,7 +83,6 @@ const AddNewNumberOverlay: React.FC = () => { const openModal = () => { setIsModalOpen(true); - dispatch(getSecondarySources()); addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_Phone_AddNewNumberCTA); }; @@ -90,6 +93,7 @@ const AddNewNumberOverlay: React.FC = () => { const handleFormSubmit = data => {}; const handleAddNumber = async () => { if (!(!isDirty || !isValid)) { + setIsDisabledSubmitButton(true); dispatch( postAlternateNumber({ loanaccountnumber: loanId, @@ -100,7 +104,8 @@ const AddNewNumberOverlay: React.FC = () => { source: getValues(SOURCE).value, successCallback: successCallback, cb: setIsModalOpen, - otherSource: getValues(OTHER_SOURCE) + otherSource: getValues(OTHER_SOURCE), + customerConfidence: watch('customerConfidence') }) ); } @@ -111,6 +116,7 @@ const AddNewNumberOverlay: React.FC = () => { const successCallback = useCallback(() => { dispatch(getTelephonesV2(loanId, customerId, caseReferenceId, caseBusinessVertical)); + setIsDisabledSubmitButton(false); reset(); }, []); @@ -120,6 +126,7 @@ const AddNewNumberOverlay: React.FC = () => { const selectedSource = watch(SOURCE, ''); const enteredOtherSource = watch(OTHER_SOURCE, ''); + const selectedValue = watch(CUSTOMER_CONFIDENCE, ''); const validateNumber = async (value: string) => { if (value.length == 10) { @@ -148,12 +155,20 @@ const AddNewNumberOverlay: React.FC = () => { setValue(SOURCE, selectedOption.value); }; + const isButtonDisabled = + !isDirty || + !isValid || + !isValidPhoneNumber(getValues(PHONE_NUMBER)) || + (isCiuAgent && !selectedValue) || + isDisabledSubmitButton; + return (
- +
@@ -231,13 +251,34 @@ const AddNewNumberOverlay: React.FC = () => { /> )} -
-
diff --git a/src/pages/CaseDetails/components/MoreDetails/index.module.scss b/src/pages/CaseDetails/components/MoreDetails/index.module.scss index e6c6b1a4..2da6fb30 100644 --- a/src/pages/CaseDetails/components/MoreDetails/index.module.scss +++ b/src/pages/CaseDetails/components/MoreDetails/index.module.scss @@ -86,13 +86,28 @@ .addNewNumberOverlay { .addNewNumberCard { box-sizing: border-box; - width: 302px; display: flex; flex-direction: column; border-radius: 8px; background-color: var(--bg-primary); transition: height 200ms; position: relative; + + .chip { + padding: 4px 12px; + font-size: 16px; + width: max-content; + } + + .chipActiveColor { + background-color: var(--blue-bg); + border: 1px solid var(--blue-border); + + .text { + color: var(--blue-base); + } + } + header { display: flex; justify-content: space-between; diff --git a/src/pages/CaseDetails/components/MoreDetails/utils/utils.ts b/src/pages/CaseDetails/components/MoreDetails/utils/utils.ts new file mode 100644 index 00000000..95822e01 --- /dev/null +++ b/src/pages/CaseDetails/components/MoreDetails/utils/utils.ts @@ -0,0 +1,14 @@ +import { ICustomerConfidenceLevel } from '../../../interfaces/CaseDetail.type'; + +export const convertToRadioOptions = (telephoneCustomerConfidence: ICustomerConfidenceLevel[]) => { + return telephoneCustomerConfidence.map((item, index) => ({ + id: (index + 1).toString(), + value: item.name, + label: `${item.label} (${item.level})`, + otherProps: { + label: item.label, + level: item.level, + name: item.name + } + })); +}; diff --git a/src/pages/CaseDetails/components/PhoneNumber/PhoneNumberContactCard.tsx b/src/pages/CaseDetails/components/PhoneNumber/PhoneNumberContactCard.tsx index ab0f4bf8..a380085a 100644 --- a/src/pages/CaseDetails/components/PhoneNumber/PhoneNumberContactCard.tsx +++ b/src/pages/CaseDetails/components/PhoneNumber/PhoneNumberContactCard.tsx @@ -223,7 +223,7 @@ const PhoneNumberContactCard = ({ callData }: { callData: ITelePhoneData }) => {
{isGlobalRoleAndNnumberformLonghorn ? ( - + @@ -265,11 +265,49 @@ const PhoneNumberContactCard = ({ callData }: { callData: ITelePhoneData }) => {
+ {callData?.telephoneCustomerConfidence?.label ? ( + <> +
+ + Confidence + +
+ + {callData?.telephoneCustomerConfidence?.label || 'NA'} + + +
+
+ + ) : null}
) : ( - isAddedFromLonghorn && + isAddedFromLonghorn && + (callData?.telephoneCustomerConfidence?.label ? ( + + + + + + {callData.telephoneCustomerConfidence.label} + + + ) : ( + + )) )} {callData?.sources.slice(0, 2).map((source, index) => ( diff --git a/src/pages/CaseDetails/components/PhoneNumber/index.tsx b/src/pages/CaseDetails/components/PhoneNumber/index.tsx index 8fefb6e3..b4244136 100644 --- a/src/pages/CaseDetails/components/PhoneNumber/index.tsx +++ b/src/pages/CaseDetails/components/PhoneNumber/index.tsx @@ -4,7 +4,7 @@ import { useParams } from 'react-router-dom'; import { createKey } from 'src/utils/CaseDetail.utils'; import { RootState } from 'src/store'; import AddNewNumberOverlay from '../MoreDetails/AddNewNumberOverlay'; -import { getTelephonesV2 } from '../../actions/casesDetailsActions'; +import { getCustomerConfidence, getTelephonesV2 } from '../../actions/casesDetailsActions'; import CustomerDeviceData from './CustomerDeviceData'; import { addClickstreamEvent } from '../../../../service/clickStreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from '../../../../service/clickStream.constant'; diff --git a/src/pages/CaseDetails/constants/MoreDetails.constant.tsx b/src/pages/CaseDetails/constants/MoreDetails.constant.tsx index 5759aa3c..859f46a2 100644 --- a/src/pages/CaseDetails/constants/MoreDetails.constant.tsx +++ b/src/pages/CaseDetails/constants/MoreDetails.constant.tsx @@ -164,6 +164,8 @@ export const SOURCE = 'source'; export const OTHER_SOURCE = 'otherSource'; +export const CUSTOMER_CONFIDENCE = 'customerConfidence'; + export enum PhoneNumberValidations { VALID_PHONE_NUMBER = 'Enter a valid 10 digit mobile number', PHONE_NUMBER_EXISTS = 'Phone number already exists', diff --git a/src/pages/CaseDetails/interfaces/CaseDetail.type.ts b/src/pages/CaseDetails/interfaces/CaseDetail.type.ts index c5e526b5..782533a8 100644 --- a/src/pages/CaseDetails/interfaces/CaseDetail.type.ts +++ b/src/pages/CaseDetails/interfaces/CaseDetail.type.ts @@ -409,6 +409,7 @@ export interface ITelePhoneData { showCreatedBy: boolean; createdByName?: string; createdByEmail?: string; + telephoneCustomerConfidence?: ICustomerConfidenceLevel; } interface IAddresses extends IApiData { @@ -570,6 +571,18 @@ export interface ReduxCaseDetail { pageData: PageData; caseDetailTabsFeatureFlag: Record; secondarySources: ISecondarySources[]; + customerConfidence: ICustomerConfidence; +} + +export interface ICustomerConfidenceLevel { + label: string; + level: string; + name: string; +} + +export interface ICustomerConfidence { + telephoneCustomerConfidence: ICustomerConfidenceLevel[]; + longhornSecondarySources: ISecondarySources[]; } export interface ISecondarySources { diff --git a/src/pages/CaseDetails/reducers/caseDetailSlice.ts b/src/pages/CaseDetails/reducers/caseDetailSlice.ts index e01e6a46..33401021 100644 --- a/src/pages/CaseDetails/reducers/caseDetailSlice.ts +++ b/src/pages/CaseDetails/reducers/caseDetailSlice.ts @@ -16,7 +16,10 @@ const initialState: ReduxCaseDetail = { detailsApiLoading: false, pageData: {}, caseDetailTabsFeatureFlag: {}, - secondarySources: [] + customerConfidence: { + telephoneCustomerConfidence: [], + longhornSecondarySources: [] + } }; const caseDetailsSlice = createSlice({ @@ -68,8 +71,8 @@ const caseDetailsSlice = createSlice({ } }; }, - setSecondarySources: (state, action) => { - state.secondarySources = action.payload; + setCustomerConfidence: (state, action) => { + state.customerConfidence = action.payload; }, setCustomerInfo: (state, action) => { const { lan, customerRefrenceId, data } = action.payload; @@ -482,7 +485,7 @@ export const { setTelephones, setMonthlyEnach, setContacts, - setSecondarySources, + setCustomerConfidence, setCustomerInfo, setRepaymentHistory, setTelephonesV2, diff --git a/src/utils/ApiHelper.ts b/src/utils/ApiHelper.ts index 1d112212..a575f2fa 100644 --- a/src/utils/ApiHelper.ts +++ b/src/utils/ApiHelper.ts @@ -28,9 +28,14 @@ import getPathParams from '@cp/utils/getPathParams'; import logger from './logger'; import { v4 as uuidv4 } from 'uuid'; -//Will add the error to sentry later -export const logError = (error?: Error | unknown, message?: string) => {}; -// Sentry.captureException({ error, message }); +export const logError = (error?: Error | unknown, message?: string) => { + logger({ + msg: message ?? 'Error', + extras: { + error: String(error) ?? 'Error not found' + } + }); +}; const MOCK_DIR = '../../__mocks__'; @@ -261,7 +266,6 @@ export enum ApiKeys { HUMAN_REMINDER_DISCONNECT_V2, HUMAN_REMINDER_GET_CASE_DETAILS_V2, HUMAN_REMNINDER_DISPOSE_CALL_V2, - PHONENUMBER_SECONDARY_SOURCES, VALIDATE_PHONE_NUMBER, REMOVE_CONTACT, USER_ENACH_UPLOAD_HISTORY, @@ -307,7 +311,8 @@ export enum ApiKeys { UPLOAD_NDRA, DOWNLOAD_NDRA_FAILURE_FILE, PAUSED_CASES, - UPLOAD_MEDIA + UPLOAD_MEDIA, + CUSTOMER_CONFIDENCE } // TODO: try to get rid of `as` @@ -572,7 +577,6 @@ API_URLS[ApiKeys.HUMAN_REMINDER_GET_CASE_DETAILS_V2] = '/hrc/case-details'; API_URLS[ApiKeys.HUMAN_REMNINDER_DISPOSE_CALL_V2] = '/hrc/dispose'; API_URLS[ApiKeys.HUMAN_REMINDER_AVAILABLITY_V2] = '/hrc/agent-activity/{agentAvailabilityStatus}/{callId}'; -API_URLS[ApiKeys.PHONENUMBER_SECONDARY_SOURCES] = '/list-longhorn-secondary-sources'; API_URLS[ApiKeys.VALIDATE_PHONE_NUMBER] = '/validate-existing-telephone/{number}'; API_URLS[ApiKeys.REMOVE_CONTACT] = '/global-access/telephone/remove'; API_URLS[ApiKeys.USER_ENACH_UPLOAD_HISTORY] = 'v2/file-uploads/history'; @@ -615,6 +619,7 @@ API_URLS[ApiKeys.UPLOAD_NDRA] = '/agent/bulk/ndra'; API_URLS[ApiKeys.DOWNLOAD_NDRA_FAILURE_FILE] = '/uploads/v2/{referenceId}/failure-report'; API_URLS[ApiKeys.PAUSED_CASES] = '/allocated-cases/paused'; API_URLS[ApiKeys.UPLOAD_MEDIA] = 'v2/interactions/upload/media'; +API_URLS[ApiKeys.CUSTOMER_CONFIDENCE] = '/telephone-configurations'; // TODO: try to get rid of `as` const MOCK_API_URLS: Record = {} as Record; @@ -1036,8 +1041,8 @@ axiosInstance.interceptors.response.use( axiosInstance.defaults.headers.common['Content-Type'] = 'application/json'; axiosInstance.defaults.baseURL = `${window?.config?.BFF_SERVICE_BASE_URL || '/api'}`; - axiosInstance.defaults.headers.common['routing_key'] = ''; + if (USE_MOCK && !import.meta.env.PROD) { axiosInstance.defaults.baseURL = '/'; }