TP-55185 Revamp UI for CSA (#943)

* TP-55185 Revamp CSA changes done

* TP-55185 Revamp CSA type fixes done

* TP-55185 fixes done

* TP-55185 PR fixes

* TP-55185 fixes

* TP-55185 CSA changes

* TP-55185 Fixes done
This commit is contained in:
Shri Prakash Bajpai
2024-04-23 18:50:02 +05:30
committed by GitHub
parent 45ae7d42a9
commit 870e6f4497
21 changed files with 608 additions and 43 deletions

View File

@@ -28,6 +28,7 @@
"overallPerformanceAgencyManagerPerformanceTable": true, "overallPerformanceAgencyManagerPerformanceTable": true,
"overallPerformanceAgencyPerformanceTable": true, "overallPerformanceAgencyPerformanceTable": true,
"fcmDashboard": true, "fcmDashboard": true,
"csaUiRevampEnabled": true
"teleGovernancePerformanceDashboard": true, "teleGovernancePerformanceDashboard": true,
"teleGovernanceAgencyPerformanceTable": true, "teleGovernanceAgencyPerformanceTable": true,
"teleGovernanceAgentPerformanceTable": true, "teleGovernanceAgentPerformanceTable": true,

View File

@@ -0,0 +1,30 @@
import React from 'react';
import DateTimePickerFilter from './components/DateTimePickerFilter';
import { IFilterDrawerSchema, ISelectedOptions } from './types';
import { FilterType } from './constants';
const ComponentMap = {
[FilterType.DATE_TIME_PICKER]: DateTimePickerFilter
};
interface IQuestionRenderingEngine {
filterType: string;
filterKey: string;
filterSchema: Record<string, IFilterDrawerSchema>;
selectedOptions: ISelectedOptions;
setSelectedOptions: (selectedOptions: ISelectedOptions) => void;
}
const FilterRenderingEngine: React.FC<IQuestionRenderingEngine> = props => {
const Comp = ComponentMap[props.filterType as FilterType];
if (!Comp) {
return null;
}
return (
<div>
<Comp {...props} />
</div>
);
};
export default FilterRenderingEngine;

View File

@@ -2,6 +2,7 @@ import styles from './index.module.scss';
import cx from 'classnames'; import cx from 'classnames';
import { Typography } from '@navi/web-ui/lib/primitives'; import { Typography } from '@navi/web-ui/lib/primitives';
import { ILeftPanel } from './types'; import { ILeftPanel } from './types';
import { DATE_QUERY_PARAMS, FilterKeys } from './constants';
const LeftPanel = (props: ILeftPanel) => { const LeftPanel = (props: ILeftPanel) => {
const { filterSchemaMap, filterSchema, selectedFilter, selectedOptions, handleFilterChange } = const { filterSchemaMap, filterSchema, selectedFilter, selectedOptions, handleFilterChange } =
@@ -10,6 +11,7 @@ const LeftPanel = (props: ILeftPanel) => {
return ( return (
<div> <div>
{filterSchemaMap?.map(filterKey => { {filterSchemaMap?.map(filterKey => {
if (DATE_QUERY_PARAMS.includes(filterKey as FilterKeys)) return null;
const { label } = filterSchema?.[filterKey] || {}; const { label } = filterSchema?.[filterKey] || {};
return ( return (
<div <div

View File

@@ -7,6 +7,7 @@ import styles from './index.module.scss';
import cx from 'classnames'; import cx from 'classnames';
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService'; import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
import { IOption, IRightPanel } from './types'; import { IOption, IRightPanel } from './types';
import SectionedOptions from './SectionedOptions';
const RightPanel = (props: IRightPanel) => { const RightPanel = (props: IRightPanel) => {
const { filterSchema, selectedFilter, selectedOptions, setSelectedOptions, tableName } = props; const { filterSchema, selectedFilter, selectedOptions, setSelectedOptions, tableName } = props;
@@ -75,21 +76,33 @@ const RightPanel = (props: IRightPanel) => {
/> />
</div> </div>
)} )}
<div className={cx(styles.filteredOptions, { [styles.noSearch]: !isSearchEnabled })}> {filteredOptionsData?.length && filteredOptionsData[0]?.filterSectionLabel ? (
{filteredOptionsData?.map(option => ( <SectionedOptions
<div className={styles.option} key={option.label}> filteredOptionsData={filteredOptionsData}
<Checkbox filterSchema={filterSchema}
onChange={e => handleCheckboxChange(e, option)} selectedOptions={selectedOptions}
title={option.label as string} selectedFilter={selectedFilter}
defaultChecked={isOptionChecked( handleCheckboxChange={handleCheckboxChange}
option?.value as string, setSelectedOptions={setSelectedOptions}
selectedOptions, isSearchEnabled={isSearchEnabled}
selectedFilter />
)} ) : (
/> <div className={cx(styles.filteredOptions, { [styles.noSearch]: !isSearchEnabled })}>
</div> {filteredOptionsData?.map(option => (
))} <div className={styles.option} key={option.label}>
</div> <Checkbox
onChange={e => handleCheckboxChange(e, option)}
title={option.label as string}
defaultChecked={isOptionChecked(
option?.value as string,
selectedOptions,
selectedFilter
)}
/>
</div>
))}
</div>
)}
</div> </div>
); );
}; };

View File

@@ -0,0 +1,61 @@
import { getFilterKey, isOptionChecked } from './utils';
import { Checkbox, Typography } from '@navi/web-ui/lib/primitives';
import styles from './index.module.scss';
import cx from 'classnames';
import { ISectionedOptionValues } from './types';
import FilterRenderingEngine from './FilterRenderingEngine';
import { FILTERS_WITH_SUBFILTERS, subFilterMapping } from './constants';
const SectionedOptions = (props: ISectionedOptionValues) => {
const {
filteredOptionsData,
filterSchema,
selectedOptions,
selectedFilter,
handleCheckboxChange,
setSelectedOptions,
isSearchEnabled
} = props;
return (
<div className={cx(styles.filteredOptions, { [styles.noSearch]: !isSearchEnabled })}>
{filteredOptionsData.map(option => {
const { filterSectionLabel, filterSectionValue } = option;
return (
<div key={filterSectionLabel}>
<Typography variant="p3" className={styles.filterSectionLabel} as="div">
{filterSectionLabel}
</Typography>
{filterSectionValue.map(option => {
const isChecked = isOptionChecked(
option?.value as string,
selectedOptions,
selectedFilter
);
return (
<div className={styles.option} key={option.label}>
<Checkbox
onChange={e => handleCheckboxChange(e, option)}
title={option.label as string}
defaultChecked={isChecked}
/>
{isChecked && FILTERS_WITH_SUBFILTERS.includes(option?.value) ? (
<FilterRenderingEngine
filterType={subFilterMapping.get(option?.value)}
filterKey={getFilterKey(selectedFilter, option?.value as string)}
filterSchema={filterSchema}
selectedOptions={selectedOptions}
setSelectedOptions={setSelectedOptions}
/>
) : null}
</div>
);
})}
</div>
);
})}
</div>
);
};
export default SectionedOptions;

View File

@@ -10,11 +10,19 @@ import {
import { getTableQueryParams } from '@cp/src/utils/QueryParamsHelper'; import { getTableQueryParams } from '@cp/src/utils/QueryParamsHelper';
import { Button, Typography } from '@navi/web-ui/lib/primitives'; import { Button, Typography } from '@navi/web-ui/lib/primitives';
import { ISelectedFilters } from './types'; import { ISelectedFilters } from './types';
import { DATE_QUERY_PARAMS, DEFAULT_QUERY_PARAM_TABLE_IDENTIFIER, FilterKeys } from './constants';
import { getOptionsAndLabels } from './utils';
const SelectedFilters = (props: ISelectedFilters) => { const SelectedFilters = (props: ISelectedFilters) => {
const { filterSchemaMap, filterSchema, handleFilterChange, clearAllFilters } = props; const {
filterSchemaMap,
filterSchema,
handleFilterChange,
clearAllFilters,
queryParamTableIdentifier = DEFAULT_QUERY_PARAM_TABLE_IDENTIFIER
} = props;
const CLEAR_ALL_FILTERS_COUNT = 1; const CLEAR_ALL_FILTERS_COUNT = 1;
const queryParams = getTableQueryParams(filterSchema, 'filters'); const queryParams = getTableQueryParams(filterSchema, queryParamTableIdentifier);
const handleCancelIconClick = (filterKey: string) => { const handleCancelIconClick = (filterKey: string) => {
handleFilterChange(filterKey); handleFilterChange(filterKey);
@@ -23,7 +31,9 @@ const SelectedFilters = (props: ISelectedFilters) => {
const renderSelectedFilters = useMemo(() => { const renderSelectedFilters = useMemo(() => {
let activeFiltersCount = 0; let activeFiltersCount = 0;
const selectedFilters = filterSchemaMap.map((filterKey: string) => { const selectedFilters = filterSchemaMap.map((filterKey: string) => {
const { options, label } = filterSchema[filterKey] || {}; if (DATE_QUERY_PARAMS.includes(filterKey as FilterKeys)) return null;
//Checking if the option is of type sectioned filter option
const { options, label } = getOptionsAndLabels(filterSchema, filterKey);
const activeFilters = queryParams?.[filterKey]; const activeFilters = queryParams?.[filterKey];
const isActiveFilterArray = Array.isArray(activeFilters); const isActiveFilterArray = Array.isArray(activeFilters);
const selectedOptions = isActiveFilterArray const selectedOptions = isActiveFilterArray

View File

@@ -0,0 +1,41 @@
import { useCallback } from 'react';
import DateTimePickerComponent from '../../DateTimePicker/DateTimePickerComponent';
import { DATE_TIME_TYPE } from '../../DateTimePicker/constants';
import { getCurrentDate, handleOptionChange } from '../utils';
import { getTodayDate } from '@cp/src/pages/CaseDetails/constants/communicationHistory.constant';
import { IDateTimePickerFilterProps } from '../types';
import styles from '../index.module.scss';
import { FiltersWithSubfiltersMapping } from '../constants';
const DateTimePickerFilter = (props: IDateTimePickerFilterProps) => {
const { filterKey, filterSchema, selectedOptions, setSelectedOptions } = props;
const defaultDate = getCurrentDate();
let date: string[] = selectedOptions?.[filterKey];
if (!date.length) date = defaultDate;
const handleDateChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
handleOptionChange(
e,
{ label: FiltersWithSubfiltersMapping.PROMISE_TO_PAY, value: e.target.value },
filterKey,
selectedOptions,
filterSchema,
setSelectedOptions
);
},
[filterKey, filterSchema, selectedOptions, setSelectedOptions]
);
return (
<DateTimePickerComponent
type={DATE_TIME_TYPE.DATE}
value={date}
onChange={handleDateChange}
className={styles.datePicker}
/>
);
};
export default DateTimePickerFilter;

View File

@@ -0,0 +1,42 @@
export const DEFAULT_QUERY_PARAM_TABLE_IDENTIFIER = 'filters';
export enum FiltersWithSubfiltersMapping {
PROMISE_TO_PAY = 'PROMISE_TO_PAY',
REQUESTED_CALLBACK = 'REQUESTED_CALLBACK'
}
export enum FilterKeys {
PROMISE_TO_PAY_TELE_CSA = 'promiseToPayDateTeleCsa',
REQUESTED_CALLBACK_TELE_CSA = 'requestedCallbackDateTeleCsa',
PROMISE_TO_PAY_FIELD_AGENT = 'promiseToPayDateFieldAgent',
TELE_INTERACTION_STATUSES = 'teleInteractionStatuses'
}
export enum FilterType {
DATE_TIME_PICKER = 'DateTimePickerFilter'
}
export const FILTERS_WITH_SUBFILTERS = [
FiltersWithSubfiltersMapping.PROMISE_TO_PAY,
FiltersWithSubfiltersMapping.REQUESTED_CALLBACK
];
export const subFilterMapping = new Map([
[FiltersWithSubfiltersMapping.PROMISE_TO_PAY, FilterType.DATE_TIME_PICKER],
[FiltersWithSubfiltersMapping.REQUESTED_CALLBACK, FilterType.DATE_TIME_PICKER]
]);
export const TeleInteractionStatusesFilterKey = new Map([
[FiltersWithSubfiltersMapping.PROMISE_TO_PAY, FilterKeys.PROMISE_TO_PAY_TELE_CSA],
[FiltersWithSubfiltersMapping.REQUESTED_CALLBACK, FilterKeys.REQUESTED_CALLBACK_TELE_CSA]
]);
export const FieldInteractionStatusesFilterKey = new Map([
[FiltersWithSubfiltersMapping.PROMISE_TO_PAY, FilterKeys.PROMISE_TO_PAY_FIELD_AGENT]
]);
export const DATE_QUERY_PARAMS = [
FilterKeys.PROMISE_TO_PAY_TELE_CSA,
FilterKeys.REQUESTED_CALLBACK_TELE_CSA,
FilterKeys.PROMISE_TO_PAY_FIELD_AGENT
];

View File

@@ -131,3 +131,15 @@
} }
} }
} }
.filterSectionLabel {
font-weight: 500;
color: var(--blue-cyan);
background-color: var(--bg-secondary);
padding: 12px 16px;
margin-bottom: 16;
}
.datePicker {
margin-top: 12px;
}

View File

@@ -21,6 +21,8 @@ import LeftPanel from './LeftPanel';
import RightPanel from './RightPanel'; import RightPanel from './RightPanel';
import SelectedFilters from './SelectedFilters'; import SelectedFilters from './SelectedFilters';
import { IFilterDrawer } from './types'; import { IFilterDrawer } from './types';
import { FilterKeys, FiltersWithSubfiltersMapping } from './constants';
import { DEFAULT_QUERY_PARAM_TABLE_IDENTIFIER } from './constants';
const FilterDrawer = (props: IFilterDrawer) => { const FilterDrawer = (props: IFilterDrawer) => {
const { const {
@@ -31,14 +33,20 @@ const FilterDrawer = (props: IFilterDrawer) => {
rightContainerClass, rightContainerClass,
selectedFiltersClass, selectedFiltersClass,
onApply, onApply,
tableName tableName,
queryParamTableIdentifier = DEFAULT_QUERY_PARAM_TABLE_IDENTIFIER,
filterKeys
} = props; } = props;
const mainParams = readQueryParams(); const mainParams = readQueryParams();
const queryParams = getTableQueryParams(filterSchema, 'filters'); const queryParams = getTableQueryParams(filterSchema, queryParamTableIdentifier);
const navigate = useNavigate(); const navigate = useNavigate();
const filterSchemaMap = useMemo(() => Object.keys(filterSchema), [filterSchema]); const filterSchemaMap = useMemo(() => {
const keys = Object.keys(filterSchema);
filterKeys && keys.push(...filterKeys); //Adding keys for subfilters
return keys;
}, [filterSchema, filterKeys]);
const [showFilterDrawer, setShowFilterDrawer] = useState(false); const [showFilterDrawer, setShowFilterDrawer] = useState(false);
const [selectedFilter, setSelectedFilter] = useState(''); const [selectedFilter, setSelectedFilter] = useState('');
@@ -68,10 +76,35 @@ const FilterDrawer = (props: IFilterDrawer) => {
setSelectedFilter(filterKey); setSelectedFilter(filterKey);
}; };
const handleDateChangesInParams = (selectedFilters: Record<string, string[]>) => {
if (
!selectedFilters?.teleInteractionStatuses?.includes(
FiltersWithSubfiltersMapping.PROMISE_TO_PAY
)
) {
selectedFilters.promiseToPayDateTeleCsa = [];
}
if (
!selectedFilters?.teleInteractionStatuses?.includes(
FiltersWithSubfiltersMapping.REQUESTED_CALLBACK
)
) {
selectedFilters.requestedCallbackDateTeleCsa = [];
}
if (
!selectedFilters?.fieldInteractionStatuses?.includes(
FiltersWithSubfiltersMapping.PROMISE_TO_PAY
)
) {
selectedFilters.promiseToPayDateFieldAgent = [];
}
};
const applyFilterChangeHandler = (selectedFilters: Record<string, string[]>) => { const applyFilterChangeHandler = (selectedFilters: Record<string, string[]>) => {
handleDateChangesInParams(selectedFilters);
const url = createQueryParams({ const url = createQueryParams({
...mainParams, ...mainParams,
filters: { [queryParamTableIdentifier]: {
...selectedFilters ...selectedFilters
} }
}); });
@@ -145,6 +178,7 @@ const FilterDrawer = (props: IFilterDrawer) => {
filterSchema={filterSchema} filterSchema={filterSchema}
handleFilterChange={clearFilterWithFilterKey} handleFilterChange={clearFilterWithFilterKey}
clearAllFilters={filterCtaHandler} clearAllFilters={filterCtaHandler}
queryParamTableIdentifier={queryParamTableIdentifier}
/> />
<Button <Button
startAdornment={<FilterIconFilled color="var(--blue-base)" />} startAdornment={<FilterIconFilled color="var(--blue-base)" />}

View File

@@ -1,8 +1,15 @@
import { ChangeEvent } from 'react';
export interface IOption { export interface IOption {
label: string; label: string;
value: string; value: string;
} }
export interface ISectionedOption {
filterSectionLabel: string;
filterSectionValue: IOption[];
}
export type ISelectedOptions = Record<string, string[]>; export type ISelectedOptions = Record<string, string[]>;
export interface IFilterDrawerSchema { export interface IFilterDrawerSchema {
@@ -22,6 +29,8 @@ export interface IFilterDrawer {
selectedFiltersClass?: string; selectedFiltersClass?: string;
onApply: (selectedFilters: ISelectedOptions) => void; onApply: (selectedFilters: ISelectedOptions) => void;
tableName: string; tableName: string;
queryParamTableIdentifier?: string;
filterKeys?: string[];
} }
export interface ILeftPanel { export interface ILeftPanel {
@@ -45,6 +54,7 @@ export interface ISelectedFilters {
filterSchema: Record<string, IFilterDrawerSchema>; filterSchema: Record<string, IFilterDrawerSchema>;
handleFilterChange: (filterKey: string) => void; handleFilterChange: (filterKey: string) => void;
clearAllFilters: (reset: boolean) => void; clearAllFilters: (reset: boolean) => void;
queryParamTableIdentifier?: string;
} }
export interface IResetSelectedOptions { export interface IResetSelectedOptions {
@@ -55,3 +65,20 @@ export interface IResetSelectedOptions {
queryParams: ISelectedOptions; queryParams: ISelectedOptions;
setSelectedOptions: (selectedOptions: ISelectedOptions) => void; setSelectedOptions: (selectedOptions: ISelectedOptions) => void;
} }
export interface IDateTimePickerFilterProps {
filterKey: string;
filterSchema: Record<string, IFilterDrawerSchema>;
selectedOptions: ISelectedOptions;
setSelectedOptions: (selectedOptions: ISelectedOptions) => void;
}
export interface ISectionedOptionValues {
filteredOptionsData: IOption[];
filterSchema: Record<string, IFilterDrawerSchema>;
selectedOptions: ISelectedOptions;
selectedFilter: string;
handleCheckboxChange: (e: ChangeEvent<HTMLInputElement>, option: IOption) => void;
setSelectedOptions: (selectedOptions: ISelectedOptions) => void;
isSearchEnabled: boolean;
}

View File

@@ -1,5 +1,19 @@
import { ChangeEvent } from 'react'; import { ChangeEvent } from 'react';
import { IFilterDrawerSchema, IOption, IResetSelectedOptions, ISelectedOptions } from './types'; import {
IFilterDrawerSchema,
IOption,
IResetSelectedOptions,
ISectionedOption,
ISelectedOptions
} from './types';
import {
DATE_QUERY_PARAMS,
FieldInteractionStatusesFilterKey,
FilterKeys,
FiltersWithSubfiltersMapping,
TeleInteractionStatusesFilterKey
} from './constants';
import { getTodayDate } from '@cp/src/pages/CaseDetails/constants/communicationHistory.constant';
export const removeSpecificOption = ( export const removeSpecificOption = (
filterKeyOptionsMap: ISelectedOptions, filterKeyOptionsMap: ISelectedOptions,
@@ -54,6 +68,13 @@ export const handleOptionChange = (
) => { ) => {
const checked = e?.target?.checked; const checked = e?.target?.checked;
let selectedOptionFilterMap = { ...selectedOptions }; let selectedOptionFilterMap = { ...selectedOptions };
if (DATE_QUERY_PARAMS.includes(filterKey as FilterKeys)) {
selectedOptionFilterMap[filterKey] = [option.value as string];
setSelectedOptions(selectedOptionFilterMap);
return;
}
if (checked) { if (checked) {
if (filterSchema[filterKey].multiSelect) { if (filterSchema[filterKey].multiSelect) {
selectedOptionFilterMap?.[filterKey]?.push(option.value as string); selectedOptionFilterMap?.[filterKey]?.push(option.value as string);
@@ -67,3 +88,28 @@ export const handleOptionChange = (
} }
setSelectedOptions(selectedOptionFilterMap); setSelectedOptions(selectedOptionFilterMap);
}; };
export const getFilterKey = (selectedFilter: string, selectedOption: string) => {
if (selectedFilter === FilterKeys.TELE_INTERACTION_STATUSES) {
return TeleInteractionStatusesFilterKey.get(selectedOption as FiltersWithSubfiltersMapping);
}
return FieldInteractionStatusesFilterKey.get(selectedOption as FiltersWithSubfiltersMapping);
};
export const getOptionsForSectionedFilters = (options: ISectionedOption[], key: string) => {
const filterSectionValues = options.map(option => option.filterSectionValue).flat();
return { options: filterSectionValues, label: key };
};
export const getOptionsAndLabels = (
filterSchema: Record<string, IFilterDrawerSchema>,
filterKey: string
) => {
return filterSchema[filterKey].options?.[0]?.filterSectionLabel
? getOptionsForSectionedFilters(filterSchema[filterKey].options, filterSchema[filterKey].label)
: filterSchema[filterKey] || {};
};
export const getCurrentDate = () => {
return getTodayDate().toISOString().slice(0, 10);
};

View File

@@ -44,7 +44,15 @@ const TableFilters = ({
onSelectedFiltersApplied, onSelectedFiltersApplied,
onClearSelectedFilters onClearSelectedFilters
}: TableFilterProps) => { }: TableFilterProps) => {
const filterSchema = useSelector((state: RootState) => state.allCases.casesTable.filters.schema); const casesFilterSchema = useSelector(
(state: RootState) => state.allCases.casesTable.filters.schema
);
const {
teleInteractionStatuses,
fieldInteractionStatuses,
allocatedFieldAgent,
...filterSchema
} = casesFilterSchema;
const filterSchemaMap = Object.keys(filterSchema); const filterSchemaMap = Object.keys(filterSchema);
const queryParams = getTableQueryParams(filterSchema, CASE_TABLE); const queryParams = getTableQueryParams(filterSchema, CASE_TABLE);

View File

@@ -49,7 +49,8 @@ export const fetchAllCases = (
...searchPageRequest, ...searchPageRequest,
aggregationRequest: defaultAgregationRequest aggregationRequest: defaultAgregationRequest
}; };
if (payload && !payload.paymentStatus) {
if (payload && !payload.paymentStatus?.length) {
payload.paymentStatus = ['ALL']; payload.paymentStatus = ['ALL'];
} }
if (updateParams) { if (updateParams) {

View File

@@ -0,0 +1,109 @@
import { ICellRendererParams } from 'ag-grid-community';
import { DEFAULT_COLOR, FeedbackTypesCodeMap } from '../constants/CasesConstants';
import CasesCustomerName from './CasesCustomerName';
import { formatAmount } from 'src/utils/commonUtils';
import { ColDefsType } from '@navi/web-ui/lib/components/AgTable/types';
import styles from './Cases.module.scss';
import TableCellRenderer from '@cp/src/components/TableCellRenderer';
import CustomTagWithTime from './CustomTagWithTime';
import { DateFormat, dateFormat } from '@cp/src/utils/DateHelper';
const tooltipContentClass = 'z-[var(--z-index-external-sensei-tooltip)]';
export const All_CASES_COLUMN_DEFS_CSA = [
{ field: 'customerName', headerName: 'Name', cellRenderer: CasesCustomerName },
{ field: 'phoneNumber', width: 160, headerName: 'Phone Number' },
{
field: 'accountNumber',
headerName: 'LAN',
minWidth: 145,
maxWidth: 160
},
{
field: 'allocatedFieldAgent',
headerName: 'Allocated FE',
width: 170,
serverSortable: true,
cellRenderer: (params: ICellRendererParams) => {
const { allocatedFieldAgent } = params.data || {};
return (
<TableCellRenderer
value={allocatedFieldAgent}
toolTipContent={allocatedFieldAgent}
showToolTip
tooltipContentClass={tooltipContentClass}
/>
);
}
},
{
field: 'teleFeedbackType',
headerName: 'Tele Feedback',
width: 230,
serverSortable: true,
cellRendererSelector: (params: ICellRendererParams) => {
const feedbackType: string = params?.data?.teleFeedbackType;
const { color = DEFAULT_COLOR, label = '' } = FeedbackTypesCodeMap[feedbackType] || {};
const formattedDate = params?.data?.teleFeedbackTimestamp
? dateFormat(
new Date(params?.data?.teleFeedbackTimestamp),
DateFormat.LONG_DATE_FORMAT_WITH_TIME
)
: null;
return {
component: CustomTagWithTime,
params: {
color,
label,
variant: 'transparent',
labelClass: styles.feedbackTag,
time: formattedDate
}
};
}
},
{
field: 'fieldFeedbackType',
headerName: 'Field Feedback',
width: 230,
serverSortable: true,
cellRendererSelector: (params: ICellRendererParams) => {
const feedbackType: string = params?.data?.fieldFeedbackType;
const { color = DEFAULT_COLOR, label = '' } = FeedbackTypesCodeMap[feedbackType] || {};
const formattedDate = params?.data?.fieldFeedbackTimestamp
? dateFormat(
new Date(params?.data?.fieldFeedbackTimestamp),
DateFormat.LONG_DATE_FORMAT_WITH_TIME
)
: null;
return {
component: CustomTagWithTime,
params: {
color,
label,
variant: 'transparent',
labelClass: styles.feedbackTag,
time: formattedDate
}
};
}
},
{
field: 'outStandingAmount',
headerName: 'Outstanding Amount',
wrapHeaderText: true,
serverSortable: true,
width: 140,
cellRenderer: (params: ICellRendererParams) => formatAmount(params.value, false)
},
{
field: 'dpdBucket',
headerName: 'DPD Bucket',
serverSortable: true,
minWidth: 120,
maxWidth: 130,
wrapHeaderText: true
}
] as ColDefsType[];

View File

@@ -141,6 +141,7 @@
.feedbackTag { .feedbackTag {
width: fit-content; width: fit-content;
line-height: 16px;
} }
.CaseListPage { .CaseListPage {
// position: relative; // position: relative;
@@ -175,3 +176,26 @@
} }
} }
} }
.feedbackContainer {
display: 'flex';
flex-direction: 'column';
justify-content: 'center';
margin-top: 9px;
margin-bottom: 9px;
}
.feedbackTime {
color: var(--grayscale-content-3);
font-weight: 400;
font-size: 10px;
padding-top: 4px;
}
.selectedFilters {
padding: 8px 0px 24px 24px;
display: flex;
justify-content: flex-end;
align-items: flex-start;
flex: 1;
}

View File

@@ -19,7 +19,8 @@ import {
CasesQueryParams, CasesQueryParams,
CASE_TABLE, CASE_TABLE,
defaultAllCasesPageRequest, defaultAllCasesPageRequest,
defaultPageSize defaultPageSize,
CSA_TABLE_ROW_HEIGHT
} from '../constants/CasesConstants'; } from '../constants/CasesConstants';
import Pagination from '@navi/web-ui/lib/components/Pagination/Pagination'; import Pagination from '@navi/web-ui/lib/components/Pagination/Pagination';
import { GridEvent, QueryParams, RowClickEvent } from '../../../components/interfaces'; import { GridEvent, QueryParams, RowClickEvent } from '../../../components/interfaces';
@@ -39,11 +40,15 @@ import { addClickstreamEvent } from '../../../service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '../../../service/clickStream.constant'; import { CLICKSTREAM_EVENT_NAMES } from '../../../service/clickStream.constant';
import AnimateWithLottie from '@cp/src/components/AnimationWithLottie'; import AnimateWithLottie from '@cp/src/components/AnimationWithLottie';
import eid2024Lottie from '@cp/src/assets/json/eid2024Lottie.json'; import eid2024Lottie from '@cp/src/assets/json/eid2024Lottie.json';
import { All_CASES_COLUMN_DEFS_CSA } from './CaseTableColumnCSA';
import FilterDrawer from '@cp/src/components/FilterDrawer';
import { noop } from '@navi/web-ui/lib/utils/common';
import { FilterKeys } from '@cp/src/components/FilterDrawer/constants';
const columnDefs = All_CASES_COLUMN_DEFS;
const Cases = () => { const Cases = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const navigate = useNavigate(); const navigate = useNavigate();
const tableRef = useRef(null);
const searchInputRef = useRef<HTMLInputElement>(null); const searchInputRef = useRef<HTMLInputElement>(null);
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { [CASE_TABLE]: queryParams } = readQueryParams(); const { [CASE_TABLE]: queryParams } = readQueryParams();
@@ -65,6 +70,10 @@ const Cases = () => {
const { PAGE_SIZE, PAGE_NO, QUERY, SORT_BY_FIELD, SORT_ORDER } = CasesQueryParams; const { PAGE_SIZE, PAGE_NO, QUERY, SORT_BY_FIELD, SORT_ORDER } = CasesQueryParams;
const [selectedFiltersApplied, setSelectedFiltersApplied] = React.useState(false); const [selectedFiltersApplied, setSelectedFiltersApplied] = React.useState(false);
const csaUiRevampEnabled = useSelector(
(store: RootState) => store?.common?.featureFlags?.csaUiRevampEnabled
);
const columnDefs = csaUiRevampEnabled ? All_CASES_COLUMN_DEFS_CSA : All_CASES_COLUMN_DEFS;
const setColumnSorting = () => { const setColumnSorting = () => {
columnDefs.forEach((colDef: ColDefsType) => { columnDefs.forEach((colDef: ColDefsType) => {
@@ -94,6 +103,7 @@ const Cases = () => {
if (!loading && queryParams?.lastUsed === QUERY) { if (!loading && queryParams?.lastUsed === QUERY) {
searchInputRef?.current?.focus(); searchInputRef?.current?.focus();
} }
tableRef?.current?.api?.sizeColumnsToFit();
}, [loading]); }, [loading]);
const updateQueryParamsAndNavigate = ( const updateQueryParamsAndNavigate = (
@@ -170,7 +180,8 @@ const Cases = () => {
const handleSortChange = (column: ColDefsType) => { const handleSortChange = (column: ColDefsType) => {
if (column) { if (column) {
const { field, serverSort } = column; const { field, serverSort, serverSortable } = column;
if (!serverSortable) return;
if (field) { if (field) {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_CaseList_SortSuccess, { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_CaseList_SortSuccess, {
sortBy: field, sortBy: field,
@@ -214,6 +225,14 @@ const Cases = () => {
col.sortable = false; col.sortable = false;
}); });
} }
const { interactionStatuses, assignedAtFilters, ...csaFilterSchema } = allCasesFilterSchema;
const csaFilterKeys = [
FilterKeys.PROMISE_TO_PAY_TELE_CSA,
FilterKeys.REQUESTED_CALLBACK_TELE_CSA,
FilterKeys.PROMISE_TO_PAY_FIELD_AGENT
];
return ( return (
<div className={styles.CaseListPage}> <div className={styles.CaseListPage}>
<GridContainer key={location.search} className={cx(styles.cases, 'allCasesTable')}> <GridContainer key={location.search} className={cx(styles.cases, 'allCasesTable')}>
@@ -231,13 +250,24 @@ const Cases = () => {
<AnimateWithLottie animationData={eid2024Lottie} /> <AnimateWithLottie animationData={eid2024Lottie} />
) : null} ) : null}
<div className={styles.cases__filters}> <div className={styles.cases__filters}>
<TableFilters {csaUiRevampEnabled ? (
// filterSchema={allCasesFilterSchema} <FilterDrawer
handlerFilterChange={handlerFilterChange} filterButtonTitle="Filters"
handlerFilterClear={handleClearAllFilters} filterSchema={csaFilterSchema}
onSelectedFiltersApplied={() => setSelectedFiltersApplied(true)} selectedFiltersClass={styles.selectedFilters}
onClearSelectedFilters={() => setSelectedFiltersApplied(false)} onApply={noop}
/> tableName={CASE_TABLE}
queryParamTableIdentifier={CASE_TABLE}
filterKeys={csaFilterKeys}
/>
) : (
<TableFilters
handlerFilterChange={handlerFilterChange}
handlerFilterClear={handleClearAllFilters}
onSelectedFiltersApplied={() => setSelectedFiltersApplied(true)}
onClearSelectedFilters={() => setSelectedFiltersApplied(false)}
/>
)}
<SearchBarInput <SearchBarInput
containerClassName={styles.searchInputContainer} containerClassName={styles.searchInputContainer}
className={styles.searchInput} className={styles.searchInput}
@@ -252,6 +282,7 @@ const Cases = () => {
</div> </div>
<GridColumn md={12} className={styles.cases__table}> <GridColumn md={12} className={styles.cases__table}>
<AgTable <AgTable
ref={tableRef}
className="AllCasesTable" className="AllCasesTable"
defaultColDef={{ defaultColDef={{
suppressMovable: true, suppressMovable: true,
@@ -278,9 +309,9 @@ const Cases = () => {
onGridReady={onCasesGridReady} onGridReady={onCasesGridReady}
onRowClicked={onCasesRowClicked} onRowClicked={onCasesRowClicked}
rowClass={'cursor-pointer'} rowClass={'cursor-pointer'}
rowHeight={csaUiRevampEnabled ? CSA_TABLE_ROW_HEIGHT : undefined}
/> />
</GridColumn> </GridColumn>
{/* {loading && <div className={styles['overlay']} />} */}
<Loader show={loading} className={'overlay'} animate={false} /> <Loader show={loading} className={'overlay'} animate={false} />
</GridContainer> </GridContainer>
</div> </div>

View File

@@ -0,0 +1,21 @@
import React from 'react';
import Tag from '@navi/web-ui/lib/primitives/Tag';
import styles from './Cases.module.scss';
import { Typography } from '@navi/web-ui/lib/primitives';
import { ICustomTagProps } from '../constants/CasesInterfaces';
const CustomTagWithTime: React.FC<ICustomTagProps> = props => {
const { label, color, variant, labelClass, time } = props;
return (
<div className={styles.feedbackContainer}>
<Tag label={label} color={color} variant={variant} labelClass={labelClass} />
{time && (
<Typography variant="h4" className={styles.feedbackTime}>
{time}
</Typography>
)}
</div>
);
};
export default CustomTagWithTime;

View File

@@ -2,6 +2,9 @@ import { TagColors } from '@navi/web-ui/lib/primitives/Tag/types';
import { AlertTypesProps, AllCasesPageRequest } from './CasesInterfaces'; import { AlertTypesProps, AllCasesPageRequest } from './CasesInterfaces';
export const CASE_TABLE = 'casesTable'; export const CASE_TABLE = 'casesTable';
export const CSA_TABLE_ROW_HEIGHT = 80;
export const DEFAULT_DATE = '01 Jan, 1970 | 05:30 am';
export const DEFAULT_COLOR = 'var(--navi-color-green-bg)';
export const FEEDBACK_TYPES_OPTIONS = [ export const FEEDBACK_TYPES_OPTIONS = [
{ value: 'NOT_CONTACTED', label: 'Not Contacted' }, { value: 'NOT_CONTACTED', label: 'Not Contacted' },
@@ -86,7 +89,7 @@ export const defaultAllCasesPageRequest: AllCasesPageRequest = {
interactionStatuses: [], interactionStatuses: [],
dpdBuckets: [], dpdBuckets: [],
posAmountRanges: [], posAmountRanges: [],
paymentStatus: ['ALL'] paymentStatus: []
}; };
export const defaultAllCasesRequestFields = { export const defaultAllCasesRequestFields = {
@@ -177,7 +180,11 @@ export const FeedbackTypesCodeMap = {
PROMISE_TO_PAY_LATER: { color: 'green', label: 'Promise to pay later' }, PROMISE_TO_PAY_LATER: { color: 'green', label: 'Promise to pay later' },
OUT_OF_NETWORK: { color: 'red', label: 'Out of network' }, OUT_OF_NETWORK: { color: 'red', label: 'Out of network' },
NOT_AVAILABLE: { color: 'blue', label: 'Not Available' }, NOT_AVAILABLE: { color: 'blue', label: 'Not Available' },
MISSED_CALL: { color: 'blue', label: 'Missed Call' } MISSED_CALL: { color: 'blue', label: 'Missed Call' },
PAID_ON_CALL: { color: 'yellow', label: 'Paid on call' },
PTP_FROM_FRIENDS_OR_FAMILY: { color: 'green', label: 'PTP from friends and family' },
REQUESTED_VISIT: { color: 'green', label: 'Requested visit' },
FRAUD: { color: 'red', label: 'Fraud' }
} as { [key: string]: CodeMap }; } as { [key: string]: CodeMap };
export const CasesDetailsFeedbackTypesCodeMap = { export const CasesDetailsFeedbackTypesCodeMap = {

View File

@@ -1,5 +1,5 @@
import { IPagination, ITableData, PageRequest } from '../../../components/interfaces'; import { IPagination, ITableData, PageRequest } from '../../../components/interfaces';
import { TagColors } from '@navi/web-ui/lib/primitives/Tag/types'; import { TagColors, TagVariants } from '@navi/web-ui/lib/primitives/Tag/types';
export interface ICasesState extends ITableData { export interface ICasesState extends ITableData {
casesData: AllCasesSummary[] | null; casesData: AllCasesSummary[] | null;
@@ -85,3 +85,11 @@ export interface AllCasesApiResponseData {
data: AllCasesSummary[]; data: AllCasesSummary[];
pages: IPagination; pages: IPagination;
} }
export interface ICustomTagProps {
label: string;
color: TagColors;
variant: TagVariants;
labelClass: string;
time: string;
}

View File

@@ -21,27 +21,49 @@ const initialState = {
paymentStatus: { paymentStatus: {
options: PAYMENT_STATUS_OPTIONS, options: PAYMENT_STATUS_OPTIONS,
multiSelect: true, multiSelect: true,
label: 'Payment Status' label: 'Payment Status',
key: 'paymentStatus'
}, },
interactionStatuses: { interactionStatuses: {
options: FEEDBACK_TYPES_OPTIONS, options: FEEDBACK_TYPES_OPTIONS,
multiSelect: true, multiSelect: true,
label: 'Feedback' label: 'Feedback'
}, },
teleInteractionStatuses: {
options: [],
multiSelect: true,
label: 'Tele Feedback',
key: 'teleInteractionStatuses'
},
fieldInteractionStatuses: {
options: [],
multiSelect: true,
label: 'Field Feedback',
key: 'fieldInteractionStatuses'
},
allocatedFieldAgent: {
options: [],
multiSelect: true,
label: 'Allocated FE',
key: 'allocatedFieldAgent'
},
dpdDuringAllocationFilters: { dpdDuringAllocationFilters: {
options: DPD_BUCKETS_OPTIONS, options: DPD_BUCKETS_OPTIONS,
multiSelect: true, multiSelect: true,
label: 'DPD During Allocation' label: 'DPD During Allocation',
key: 'dpdDuringAllocationFilters'
}, },
assignedAtFilters: { assignedAtFilters: {
options: DATE_OF_ALLOCATION, options: DATE_OF_ALLOCATION,
multiSelect: false, multiSelect: false,
label: 'Allocation Date' label: 'Allocation Date',
key: 'assignedAtFilters'
}, },
states: { states: {
options: [], options: [],
multiSelect: true, multiSelect: true,
label: 'State of Customers' label: 'State of Customers',
key: 'states'
} }
} }
}, },
@@ -93,10 +115,25 @@ const casesSlice = createSlice({
staticFilters.dpdDuringAllocationFilters; staticFilters.dpdDuringAllocationFilters;
} }
if (staticFilters?.teleInteractionStatuses) {
state.casesTable.filters.schema.teleInteractionStatuses.options =
staticFilters?.teleInteractionStatuses;
}
if (staticFilters?.fieldInteractionStatuses) {
state.casesTable.filters.schema.fieldInteractionStatuses.options =
staticFilters?.fieldInteractionStatuses;
}
if (dynamicFilters?.stateFilters) { if (dynamicFilters?.stateFilters) {
state.casesTable.filters.schema.states.options = dynamicFilters.stateFilters; state.casesTable.filters.schema.states.options = dynamicFilters.stateFilters;
} }
if (dynamicFilters?.allocatedFieldAgentFilters) {
state.casesTable.filters.schema.allocatedFieldAgent.options =
dynamicFilters?.allocatedFieldAgentFilters;
}
state.casesTable.casesData = caseSummaryPaginatedResponse?.data; state.casesTable.casesData = caseSummaryPaginatedResponse?.data;
state.casesTable.pagination = { state.casesTable.pagination = {