TP-77180 | Self serve UI for pausing collections (#1119)

* TP-77180 | Self serve UI for pausing collections

* TP-77180 | Self serve UI for pausing collections

* TP-77180 | Self serve UI for pausing collections

* TP-77180 | Self serve UI for pausing collections

* TP-77180 | Self serve UI for pausing collections

* TP-77180 | Hover button color change fix

* TP-77180 | Hover button color change fix

* TP-77180 | Hover button color change fix

* TP-77180 | UAT FIxes

* TP-77180 | UAT FIxes 1

* TP-77180 | UAT FIxes 1

* TP-77180 | UAT FIxes 2

* TP-77180 | ready for PR review

* TP-77180 | prettier

* TP-77180 | Merge to master fix

* TP-77180 | Merge to master fix

* TP-77180 | lint fix

* TP-77180 | self serve UI ready for PR review

* TP-77180 | self serve UI ready for PR review

* TP-77180 | pr review fix v1

* TP-77180 | pr review fix v2

* TP-77180 | added feature flag check

* TP-77180 | added feature flag check

* TP-77180 | PR review fixes

* TP-77180 | submodule update
This commit is contained in:
Ashish Deo
2024-09-25 17:30:15 +05:30
committed by GitHub
parent 24b5c0f420
commit bf69705ee6
21 changed files with 522 additions and 25 deletions

View File

@@ -0,0 +1,23 @@
import { IconProps } from './types';
const PauseIcon = (props: IconProps) => {
const { width = '21', height = '20' } = props;
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={height}
viewBox="0 0 21 20"
fill="none"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.16667 15.8327C8.08333 15.8327 8.83333 15.0827 8.83333 14.166V5.83268C8.83333 4.91602 8.08333 4.16602 7.16667 4.16602C6.25 4.16602 5.5 4.91602 5.5 5.83268V14.166C5.5 15.0827 6.25 15.8327 7.16667 15.8327ZM12.1667 5.83268V14.166C12.1667 15.0827 12.9167 15.8327 13.8333 15.8327C14.75 15.8327 15.5 15.0827 15.5 14.166V5.83268C15.5 4.91602 14.75 4.16602 13.8333 4.16602C12.9167 4.16602 12.1667 4.91602 12.1667 5.83268Z"
fill="white"
/>
</svg>
);
};
export default PauseIcon;

View File

@@ -0,0 +1,27 @@
import { IconProps } from '../../icons/types';
const InfoNotification = (props: IconProps) => {
const { width = 36, height = 36 } = props;
return (
<svg
width={width}
height={height}
viewBox="0 0 36 36"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="36" height="36" rx="18" fill="#E6F1FF" />
<mask id="mask0_2225_23276" maskUnits="userSpaceOnUse" x="8" y="8" width="20" height="20">
<rect x="8" y="8" width="20" height="20" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_2225_23276)">
<path
d="M17.9998 22.166C18.2359 22.166 18.434 22.086 18.594 21.926C18.7534 21.7666 18.8332 21.5688 18.8332 21.3327V17.9785C18.8332 17.7424 18.7534 17.548 18.594 17.3952C18.434 17.2424 18.2359 17.166 17.9998 17.166C17.7637 17.166 17.5659 17.2457 17.4065 17.4052C17.2465 17.5652 17.1665 17.7632 17.1665 17.9993V21.3535C17.1665 21.5896 17.2465 21.7841 17.4065 21.9368C17.5659 22.0896 17.7637 22.166 17.9998 22.166ZM17.9998 15.4993C18.2359 15.4993 18.434 15.4193 18.594 15.2593C18.7534 15.0999 18.8332 14.9021 18.8332 14.666C18.8332 14.4299 18.7534 14.2318 18.594 14.0718C18.434 13.9124 18.2359 13.8327 17.9998 13.8327C17.7637 13.8327 17.5659 13.9124 17.4065 14.0718C17.2465 14.2318 17.1665 14.4299 17.1665 14.666C17.1665 14.9021 17.2465 15.0999 17.4065 15.2593C17.5659 15.4193 17.7637 15.4993 17.9998 15.4993ZM17.9998 26.3327C16.8471 26.3327 15.7637 26.1138 14.7498 25.676C13.7359 25.2388 12.854 24.6452 12.104 23.8952C11.354 23.1452 10.7604 22.2632 10.3232 21.2493C9.88539 20.2355 9.6665 19.1521 9.6665 17.9993C9.6665 16.8466 9.88539 15.7632 10.3232 14.7493C10.7604 13.7355 11.354 12.8535 12.104 12.1035C12.854 11.3535 13.7359 10.7596 14.7498 10.3218C15.7637 9.88463 16.8471 9.66602 17.9998 9.66602C19.1526 9.66602 20.2359 9.88463 21.2498 10.3218C22.2637 10.7596 23.1457 11.3535 23.8957 12.1035C24.6457 12.8535 25.2393 13.7355 25.6765 14.7493C26.1143 15.7632 26.3332 16.8466 26.3332 17.9993C26.3332 19.1521 26.1143 20.2355 25.6765 21.2493C25.2393 22.2632 24.6457 23.1452 23.8957 23.8952C23.1457 24.6452 22.2637 25.2388 21.2498 25.676C20.2359 26.1138 19.1526 26.3327 17.9998 26.3327Z"
fill="#0276FE"
/>
</g>
</svg>
);
};
export default InfoNotification;

View File

@@ -1,13 +1,14 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import styles from './index.module.scss';
import React, { useEffect, useRef, useState } from 'react';
import { CalendarIcon } from '@navi/web-ui/lib/icons';
import { Typography } from '@navi/web-ui/lib/primitives';
import { DATE_TIME_TYPE, DateTimeFormat } from './constants';
import cx from 'classnames';
import dayjs from 'dayjs';
import { DATE_TIME_TYPE } from './constants';
import useOutsideClick from '@hooks/useOutsideClick';
import ClockIcon from '@icons/ClockIcon';
import DateTimePickerLabel from '@cp/components/DateTimePicker/DateTimePickerLabel';
import styles from './index.module.scss';
import cx from 'classnames';
import FooterContainer from './Footer';
import HeadingContainer from './Heading';
interface DatePickerProps extends React.InputHTMLAttributes<HTMLInputElement> {
heading?: string;
@@ -18,6 +19,7 @@ interface DatePickerProps extends React.InputHTMLAttributes<HTMLInputElement> {
inputClasses?: string;
iconColor?: string;
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
footer?: string;
}
const DateTimePickerComponent: React.FC<DatePickerProps> = ({
@@ -31,6 +33,7 @@ const DateTimePickerComponent: React.FC<DatePickerProps> = ({
iconColor,
onChange,
disabled = false,
footer,
...restProps
}) => {
const [isOpen, setIsOpen] = useState(false);
@@ -79,11 +82,7 @@ const DateTimePickerComponent: React.FC<DatePickerProps> = ({
className={cx(styles.datePickerInput, className, { [styles.disabled]: disabled })}
ref={datePickerContainerRef}
>
{heading ? (
<Typography variant={'p4'} className={styles.headerText}>
{heading}
</Typography>
) : null}
{heading ? <HeadingContainer heading={heading} /> : null}
<div
className={cx(styles.datePickerContainer, containerClasses, {
@@ -126,6 +125,7 @@ const DateTimePickerComponent: React.FC<DatePickerProps> = ({
)}
</div>
</div>
{footer ? <FooterContainer footer={footer} /> : null}
</div>
);
};

View File

@@ -0,0 +1,17 @@
import { Typography } from '@navi/web-ui/lib/primitives';
import styles from './index.module.scss';
type FooterContainerProps = {
footer: string;
};
const FooterContainer = (props: FooterContainerProps) => {
const { footer } = props;
return (
<Typography variant={'p4'} className={styles.footerText}>
{footer}
</Typography>
);
};
export default FooterContainer;

View File

@@ -0,0 +1,17 @@
import { Typography } from '@navi/web-ui/lib/primitives';
import styles from './index.module.scss';
type HeadingContainerProps = {
heading: string;
};
const HeadingContainer = (props: HeadingContainerProps) => {
const { heading } = props;
return (
<Typography variant={'p4'} className={styles.headerText}>
{heading}
</Typography>
);
};
export default HeadingContainer;

View File

@@ -49,4 +49,9 @@
}
}
}
.footerText {
margin-top: 8px;
color: var(--navi-color-gray-c3);
}
}

View File

@@ -48,7 +48,8 @@ const NotificationList: React.FC<NotificationListProps> = ({
if (
[
TemplateTypes.CASE_ALLOCATED_NOTIFICATION_TEMPLATE,
TemplateTypes.CASE_DE_ALLOCATED_NOTIFICATION_TEMPLATE
TemplateTypes.CASE_DE_ALLOCATED_NOTIFICATION_TEMPLATE,
TemplateTypes.COLLECTION_PAUSE_EFFORTS_CASE_DEALLOCATED
].includes(templateName)
) {
navigate(APP_ROUTES.CASES.path);

View File

@@ -13,6 +13,7 @@ import AmeyoCallNotPickedIcon from 'src/assets/images/icons/AmyoCallNotPickedIco
import RaiseRequestIcon from '@cp/src/assets/icons/RaiseRequestIcon';
import ReceiveRequestIcon from '@cp/src/assets/icons/ReceiveRequestIcon';
import FeeReappliedIcon from '@cp/src/assets/icons/FeeReappliedIcon';
import InfoNotification from '@cp/src/assets/images/icons/InfoNotification';
export enum NotificationTypes {
LONGHORN_NOTIFICATION = 'LONGHORN_NOTIFICATION',
@@ -59,7 +60,8 @@ export const TemplateTypes = {
'REVISIT_GEN_AI_BOT_SCHEDULED_NOTIFICATION_TEMPLATE',
SUPPORT_REQUEST_RECEIVED: 'SUPPORT_REQUEST_RECEIVED',
SUPPORT_REQUEST_RESOLVED: 'SUPPORT_REQUEST_RESOLVED',
MESSAGE_WAIVE_UNHOLD_NOTIFICATION_TEMPLATE: 'MESSAGE_WAIVE_UNHOLD_NOTIFICATION_TEMPLATE'
MESSAGE_WAIVE_UNHOLD_NOTIFICATION_TEMPLATE: 'MESSAGE_WAIVE_UNHOLD_NOTIFICATION_TEMPLATE',
COLLECTION_PAUSE_EFFORTS_CASE_DEALLOCATED: 'COLLECTION_PAUSE_EFFORTS_CASE_DEALLOCATED'
};
export const PaymentSuccessTemplateTypes = [
@@ -108,6 +110,7 @@ export const NotificationTabTemplateMap = {
TemplateTypes.LONGHORN_ALERTS_NEW_ENQUIRY,
TemplateTypes.CASE_ALLOCATED_NOTIFICATION_TEMPLATE,
TemplateTypes.CASE_DE_ALLOCATED_NOTIFICATION_TEMPLATE,
TemplateTypes.COLLECTION_PAUSE_EFFORTS_CASE_DEALLOCATED,
TemplateTypes.BUSY_SCHEDULED_NOTIFICATION_TEMPLATE,
TemplateTypes.REVISIT_SCHEDULED_NOTIFICATION_TEMPLATE,
TemplateTypes.FIELD_BOT_REVISIT_SCHEDULED_NOTIFICATION_TEMPLATE,
@@ -128,6 +131,7 @@ export const NotificationTabTemplateMap = {
TemplateTypes.LONGHORN_ALERTS_NEW_PHONE_NUMBER_ADDED,
TemplateTypes.CASE_ALLOCATED_NOTIFICATION_TEMPLATE,
TemplateTypes.CASE_DE_ALLOCATED_NOTIFICATION_TEMPLATE,
TemplateTypes.COLLECTION_PAUSE_EFFORTS_CASE_DEALLOCATED,
TemplateTypes.PAYMENT_FAILED_TEMPLATE_V2,
TemplateTypes.PAYMENT_MADE_TEMPLATE_V2,
TemplateTypes.CUSTOMER_CALLED_TEMPLATE_V2,
@@ -215,7 +219,7 @@ export const TemplateInfoMap = {
icon: <ErrorIcon />
},
[TemplateTypes.LONGHORN_ALERTS_CHANGE_IN_DELIQUENCY_ACCOUNTS]: {
label: 'Change in deliquency accounts',
label: 'Change in delinquency accounts',
icon: <ErrorIcon />
},
[TemplateTypes.LONGHORN_ALERTS_NEW_PHONE_NUMBER_ADDED]: {
@@ -231,7 +235,7 @@ export const TemplateInfoMap = {
icon: <ErrorIcon />
},
[TemplateTypes.LONGHORN_ALERTS_CHANGE_IN_DELIQUENCY]: {
label: 'Change in deliquency',
label: 'Change in delinquency',
icon: <ErrorIcon />
},
[TemplateTypes.CASE_ALLOCATED_NOTIFICATION_TEMPLATE]: {
@@ -239,9 +243,13 @@ export const TemplateInfoMap = {
icon: <AllocationIcon />
},
[TemplateTypes.CASE_DE_ALLOCATED_NOTIFICATION_TEMPLATE]: {
label: 'Case Deallocated',
label: 'Case De-allocated',
icon: <AllocationIcon />
},
[TemplateTypes.COLLECTION_PAUSE_EFFORTS_CASE_DEALLOCATED]: {
label: 'Case De-allocated',
icon: <InfoNotification />
},
[TemplateTypes.AMEYO_CALL_DROP]: {
label: 'Ameyo Call Drop',
icon: <AmeyoCallDropIcon />

View File

@@ -46,7 +46,6 @@ import InactiveTextContainer from './components/form/InactiveTextContainer';
import styles from './AgentForm.module.scss';
import cx from 'classnames';
import { Typography } from '@navi/web-ui/lib/primitives';
import UploadComponent from '@cp/components/UploadComponent/UploadComponent';
import AgentFormUploadControl from '@cp/pages/AllAgents/AgentForm/components/form/AgentFormUploadControl';
import AgentFormDownloadDraCertificate from '@cp/pages/AllAgents/AgentForm/components/form/AgentFormDownloadDraCertificate';

View File

@@ -0,0 +1,46 @@
import axiosInstance, { ApiKeys, getApiUrl, logError } from '@cp/src/utils/ApiHelper';
import { toast } from '@navi/web-ui/lib/primitives/Toast';
import { PAUSE_ACTION_MESSAGE } from './const';
import { isFunction } from '@cp/src/utils/commonUtils';
export const getLanPauseStatus = async (caseId: string) => {
try {
const url = getApiUrl(ApiKeys.GET_PAUSE_STATUS, { caseId });
const response = await axiosInstance.get(url);
return response?.data;
} catch (error) {
return logError(error);
}
};
interface PauseActionInput {
caseId: string;
payload: any;
setLoading: (loading: boolean) => void;
successCallback: () => void;
}
export const submitPauseAction = ({
caseId,
payload,
setLoading,
successCallback
}: PauseActionInput) => {
const url = getApiUrl(ApiKeys.PAUSE_ACTION, { caseId });
if (isFunction(setLoading)) setLoading(true);
axiosInstance
.post(url, payload)
.then(() => {
toast.success(PAUSE_ACTION_MESSAGE.COLLECTIONS_PAUSED);
successCallback();
})
.catch(error => {
toast.error(PAUSE_ACTION_MESSAGE.FAILED_TO_PAUSE);
logError(error);
})
.finally(() => {
isFunction(setLoading) && setLoading(false);
});
};

View File

@@ -0,0 +1,51 @@
export enum PauseType {
RBI_ESCALATION = 'RBI_ESCALATION',
CUSTOMER_GOODWILL = 'CUSTOMER_GOODWILL',
OTHERS = 'OTHERS'
}
export const PAUSE_REASON_TYPES = [
{
value: PauseType.RBI_ESCALATION,
label: 'RBI Escalation'
},
{
value: PauseType.CUSTOMER_GOODWILL,
label: 'Customer Goodwill'
},
{
value: PauseType.OTHERS,
label: 'Others'
}
];
export interface PauseFormValues {
pauseDate: string;
reason: PauseType | null;
details?: string;
}
export const defaultValues = {
pauseDate: '',
reason: null,
details: ''
};
export enum PAUSE_MESSAGE {
UPDATE_PAUSE = 'Update pause on this LAN',
PAUSE_COLLECTIONS = 'Pause collections for this LAN',
UPDATE_PAUSE_DAYS = 'Update pause days',
PAUSE_TILL = 'Pause till',
PAUSE_IMMEDIATE_TODAY = 'The pause will be immediate from today',
DATE_ERROR = 'Please enter some other date',
REASON_UPDATE = 'Reason for update',
REASON_PAUSE = 'Reason for pause',
OTHER_REASONS = 'Other reasons',
UPDATE_PAUSE_DATE = 'Update pause date',
PAUSE_COLLECTIONS_EFFORTS = 'Pause collections efforts'
}
export enum PAUSE_ACTION_MESSAGE {
FAILED_TO_PAUSE = 'Failed to pause collections',
COLLECTIONS_PAUSED = 'Collections paused successfully'
}

View File

@@ -0,0 +1,67 @@
.container {
width: 100%;
width: 100%;
display: flex;
height: 100%;
flex-direction: column;
justify-content: space-between;
.feedback {
overflow-y: scroll;
box-sizing: border-box;
width: 100%;
padding: 0 16px 16px 16px;
flex: 1;
.pauseDatePicker {
margin-top: 8px;
width: 100% !important;
}
.pauseDatePickerInput {
border-radius: 8px;
padding-left: 12px;
padding-right: 12px;
font-size: 16px;
color: var(--navi-bordered-input-text-color);
}
.textAreaContainer {
min-width: auto;
overflow: hidden;
overflow-y: auto;
}
.questions {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
align-self: flex-start;
margin-bottom: 12px;
.chip {
padding: 4px 12px;
font-size: 16px;
margin-top: 5px;
margin-bottom: 5px;
margin-right: 10px;
}
.chipActiveColor {
background-color: var(--blue-bg);
border: 1px solid var(--blue-border);
.text {
color: var(--blue-base);
}
}
}
}
.submitButtonSection {
width: -webkit-fill-available;
padding: 16px 16px;
border-top: 1px solid var(--navi-color-gray-border);
height: fit-content;
}
}

View File

@@ -0,0 +1,210 @@
import React, { useEffect, useState } from 'react';
import { Button, Chip, TextArea, Typography } from '@navi/web-ui/lib/primitives';
import DateTimePickerComponent from '@cp/src/components/DateTimePicker/DateTimePickerComponent';
import { DATE_TIME_TYPE } from '@cp/src/components/DateTimePicker/constants';
import { DateFormat, getFormatDate, getFormattedDateWithFullYear } from '@cp/src/utils/DateHelper';
import { defaultValues, PAUSE_MESSAGE, PAUSE_REASON_TYPES, PauseFormValues } from './const';
import InfoFilledIcon from '@cp/src/assets/icons/InfoFilledIcon';
import { getLanPauseStatus, submitPauseAction } from './action';
import { Controller, useForm } from 'react-hook-form';
import { useParams } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { RootState } from '@cp/src/store';
import { createKey } from '@cp/src/utils/CaseDetail.utils';
import { logError } from '@cp/src/utils/ApiHelper';
import PauseIcon from '@cp/src/assets/icons/PauseIcon';
import styles from './index.module.scss';
import cx from 'classnames';
const PauseAction = () => {
const todayDate = getFormatDate(new Date(), DateFormat.YYYY_MM_DD);
const threeMonthsLaterDate = getFormatDate(
new Date(new Date().setMonth(new Date().getMonth() + 3)),
DateFormat.YYYY_MM_DD
);
const [isSelectedActionUpdatePause, setIsSelectedActionUpdatePause] = useState(false);
const [pausedDate, setPausedDate] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const { loanId = '', customerId = '' } = useParams();
const { pageDetail } = useSelector((state: RootState) => ({
pageDetail: state.caseDetail.pageData?.[createKey(loanId, customerId)]
}));
const caseReferenceId = pageDetail?.details?.data?.caseReferenceId || '';
const handleSubmitState = (state: boolean) => {
setIsSubmitting(state);
};
const {
register,
handleSubmit,
reset,
watch,
formState: { errors },
control
} = useForm<PauseFormValues>({
defaultValues
});
const fetchPauseStatus = async () => {
try {
const getPauseStatusData = await getLanPauseStatus(caseReferenceId);
setIsSelectedActionUpdatePause(getPauseStatusData?.isPaused);
setPausedDate(getPauseStatusData?.pauseTillDate);
} catch (error) {
logError(error, 'Error while fetching pause status');
}
};
useEffect(() => {
if (!caseReferenceId) return;
fetchPauseStatus();
}, [caseReferenceId]);
const handleSubmitForm = (data: PauseFormValues) => {
const { pauseDate, reason } = data;
const payload = {
pauseEffortsTillDate: pauseDate,
pauseEffortsReason: reason === 'OTHERS' ? data.details : reason
};
submitPauseAction({
caseId: caseReferenceId,
payload: payload,
setLoading: handleSubmitState,
successCallback: fetchPauseStatus
});
reset(defaultValues);
};
const isFormDisabled =
!caseReferenceId.length ||
isSubmitting ||
!watch('pauseDate') ||
!watch('reason') ||
(watch('reason') === 'OTHERS' && !watch('details'));
return (
<div className={styles.container} key={isSelectedActionUpdatePause as unknown as string}>
<div className={styles.feedback}>
<div className={'mt-6 mb-4'}>
<Typography variant="h3">
{isSelectedActionUpdatePause
? PAUSE_MESSAGE.UPDATE_PAUSE
: PAUSE_MESSAGE.PAUSE_COLLECTIONS}
</Typography>
</div>
{isSelectedActionUpdatePause ? (
<div className="flex bg-[var(--navi-color-gray-bg-secondary)] pt-3 pb-3 pr-3.5 pl-3 rounded">
<InfoFilledIcon width={20} height={20} fill={'var(--blue-base)'} />
<Typography variant="p4" className="ml-2">
Collections efforts have been paused till{' '}
<Typography as="span" variant="h5" color="var(--navi-color-gray-c1)">
{pausedDate ? getFormattedDateWithFullYear(new Date(pausedDate)) : ''}
</Typography>
</Typography>
</div>
) : null}
<Controller
control={control}
name="pauseDate"
rules={{
validate: value =>
!(isSelectedActionUpdatePause && value === pausedDate) || PAUSE_MESSAGE.DATE_ERROR
}}
render={({ field: { onChange, value } }) => (
<DateTimePickerComponent
key={isSubmitting as unknown as string}
type={DATE_TIME_TYPE.DATE}
min={todayDate}
max={threeMonthsLaterDate}
value={value}
className={'mt-4'}
containerClasses={styles.pauseDatePicker}
inputClasses={styles.pauseDatePickerInput}
onChange={onChange}
required
heading={
isSelectedActionUpdatePause
? PAUSE_MESSAGE.UPDATE_PAUSE_DAYS
: PAUSE_MESSAGE.PAUSE_TILL
}
footer={isSelectedActionUpdatePause ? '' : PAUSE_MESSAGE.PAUSE_IMMEDIATE_TODAY}
/>
)}
/>
{errors?.pauseDate && (
<Typography variant="p4" color="var(--navi-color-red-base)">
{errors?.pauseDate?.message}
</Typography>
)}
<div className={'mt-4 mb-1'}>
<Typography as="span" variant="p4" color="var(--navi-color-gray-c2)">
{isSelectedActionUpdatePause ? PAUSE_MESSAGE.REASON_UPDATE : PAUSE_MESSAGE.REASON_PAUSE}
</Typography>
</div>
<Controller
control={control}
name="reason"
rules={{ required: true }}
render={({ field: { onChange, value } }) => (
<div className={styles.questions}>
{PAUSE_REASON_TYPES.map(pauseType => (
<Chip
key={pauseType.value}
label={pauseType.label}
wrapperClasses={cx([
styles.chip,
{ [styles.chipActiveColor]: pauseType.value === value }
])}
labelClasses={styles.text}
onChipClick={() => onChange(pauseType.value)}
/>
))}
</div>
)}
/>
{watch('reason') === 'OTHERS' ? (
<TextArea
{...register('details', { required: true })}
placeholder=""
id="comments"
resize
fullWidth
required
label="Other reasons"
rows={4}
containerClassName={styles.textAreaContainer}
className={cx(styles.textAreaContainer, 'mt-4')}
/>
) : null}
</div>
<div className={styles.submitButtonSection}>
<Button
fullWidth
type={'submit'}
onClick={handleSubmit(handleSubmitForm)}
className="bg-[var(--navi-color-red-base)]"
startAdornment={<PauseIcon />}
disabled={isFormDisabled}
>
{isSelectedActionUpdatePause
? PAUSE_MESSAGE.UPDATE_PAUSE_DATE
: PAUSE_MESSAGE.PAUSE_COLLECTIONS_EFFORTS}
</Button>
</div>
</div>
);
};
export default React.memo(PauseAction);

View File

@@ -17,12 +17,22 @@ const RightSidebar = () => {
}));
const location = useLocation();
const [selectedTabKey, setSelectedTabKey] = useState(TAB_KEYS.FEEDBACK);
const isCollectionsPauseRoleAvailable = featureFlags?.pauseCollectionEffortsFeatureFlag || false;
const getInitialTabKey = () => {
return isCollectionsPauseRoleAvailable ? TAB_KEYS.PAUSE : TAB_KEYS.FEEDBACK;
};
const [selectedTabKey, setSelectedTabKey] = useState(getInitialTabKey);
useEffect(() => {
const selectedTab = location?.state?.rightSideTab;
if (selectedTab && featureFlags?.csaCoOrdinationModuleFeatureFlag)
if (
selectedTab &&
(featureFlags?.csaCoOrdinationModuleFeatureFlag ||
featureFlags?.pauseCollectionEffortsFeatureFlag)
)
setSelectedTabKey(selectedTab);
}, [location.key]);

View File

@@ -1,14 +1,22 @@
import CsaTicketing from '../../CsaTicketing';
import PauseAction from '../components/Pause';
import Payments from '../components/Payments';
import FeedbackForm from '../feedbackForm';
export const TAB_KEYS = {
FEEDBACK: 'feedback',
PAYMENTS: 'payments',
TASKS: 'tasks'
TASKS: 'tasks',
PAUSE: 'pause'
};
export const LEFT_SIDE_TABS = [
{
key: TAB_KEYS.PAUSE,
value: 'Pause',
component: PauseAction,
featureFlagKey: 'pauseCollectionEffortsFeatureFlag'
},
{
key: TAB_KEYS.FEEDBACK,
value: 'Feedback',

View File

@@ -451,3 +451,9 @@ export const getLastMonthDate = () => {
lastMonth.setMonth(lastMonth.getMonth() - 1);
return lastMonth;
};
export const getNextThreeMonthDate = () => {
const lastMonth = new Date();
lastMonth.setMonth(lastMonth.getMonth() + 3);
return lastMonth;
};

View File

@@ -66,7 +66,6 @@
font-weight: 400;
font-size: 13px;
line-height: 20px;
/* identical to box height, or 154% */
letter-spacing: -0.01em;
color: var(--greyscale-2);
margin-bottom: 8px;

View File

@@ -837,7 +837,6 @@ const FeedbackFrom = (props: FeedbackFromProps) => {
</div>
<div className={styles.submitButtonSection}>
{!isDisableNextPreviousCaseAction && !isTeamLead ? <NextPreviousCaseActions /> : null}
{/* {!disableSubmitButton ? ( */}
<Button
disabled={disableSubmitFlag}
fullWidth
@@ -846,7 +845,6 @@ const FeedbackFrom = (props: FeedbackFromProps) => {
>
Submit
</Button>
{/* // ) : null} */}
</div>
<Loader show={feedback.loading || false} className={styles.loadingState} animate={false} />
</div>

View File

@@ -90,7 +90,8 @@ export enum Roles {
ROLE_GLOBAL_ACCESS = 'ROLE_GLOBAL_ACCESS',
ROLE_BANK_STATEMENT_ACCESS = 'ROLE_BANK_STATEMENT_ACCESS',
ROLE_IN_HOUSE_CSA_AGENT = 'ROLE_INHOUSE_CSA',
ROLE_EXTERNAL_CSA = 'ROLE_EXTERNAL_CSA'
ROLE_EXTERNAL_CSA = 'ROLE_EXTERNAL_CSA',
ROLE_COLLECTION_EFFORTS_PAUSE_ACCESS = 'ROLE_COLLECTION_EFFORTS_PAUSE_ACCESS'
}
export const agentStateAndLanguageEditRoles = [

View File

@@ -265,7 +265,9 @@ export enum ApiKeys {
DOCUMENT_UPLOAD_ACK_URL,
GENERATE_DYNAMIC_DOCUMENT_URL,
GET_MASKED_TELEPHONE,
FETCH_AGENT_DOCUMENTS
FETCH_AGENT_DOCUMENTS,
GET_PAUSE_STATUS,
PAUSE_ACTION
}
// TODO: try to get rid of `as`
@@ -532,6 +534,8 @@ API_URLS[ApiKeys.DOWNLOAD_ENACH_FILE] = '/v2/file-uploads/download-url/{referenc
API_URLS[ApiKeys.GET_DOCUMENT_UPLOAD_URL] = 'v2/file-uploads/upload-url';
API_URLS[ApiKeys.DOCUMENT_UPLOAD_ACK_URL] = '/v2/file-uploads/upload-ack';
API_URLS[ApiKeys.GET_MASKED_TELEPHONE] = 'collections/get-masked-telephone';
API_URLS[ApiKeys.GET_PAUSE_STATUS] = '/v1/case-efforts/pause/status/{caseId}';
API_URLS[ApiKeys.PAUSE_ACTION] = '/v1/case-efforts/pause/{caseId}';
API_URLS[ApiKeys.GENERATE_DYNAMIC_DOCUMENT_URL] = 'v1/documents/generate';
// TODO: try to get rid of `as`