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:
committed by
GitHub
parent
45ae7d42a9
commit
870e6f4497
@@ -28,6 +28,7 @@
|
||||
"overallPerformanceAgencyManagerPerformanceTable": true,
|
||||
"overallPerformanceAgencyPerformanceTable": true,
|
||||
"fcmDashboard": true,
|
||||
"csaUiRevampEnabled": true
|
||||
"teleGovernancePerformanceDashboard": true,
|
||||
"teleGovernanceAgencyPerformanceTable": true,
|
||||
"teleGovernanceAgentPerformanceTable": true,
|
||||
|
||||
30
src/components/FilterDrawer/FilterRenderingEngine.tsx
Normal file
30
src/components/FilterDrawer/FilterRenderingEngine.tsx
Normal 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;
|
||||
@@ -2,6 +2,7 @@ import styles from './index.module.scss';
|
||||
import cx from 'classnames';
|
||||
import { Typography } from '@navi/web-ui/lib/primitives';
|
||||
import { ILeftPanel } from './types';
|
||||
import { DATE_QUERY_PARAMS, FilterKeys } from './constants';
|
||||
|
||||
const LeftPanel = (props: ILeftPanel) => {
|
||||
const { filterSchemaMap, filterSchema, selectedFilter, selectedOptions, handleFilterChange } =
|
||||
@@ -10,6 +11,7 @@ const LeftPanel = (props: ILeftPanel) => {
|
||||
return (
|
||||
<div>
|
||||
{filterSchemaMap?.map(filterKey => {
|
||||
if (DATE_QUERY_PARAMS.includes(filterKey as FilterKeys)) return null;
|
||||
const { label } = filterSchema?.[filterKey] || {};
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -7,6 +7,7 @@ import styles from './index.module.scss';
|
||||
import cx from 'classnames';
|
||||
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
|
||||
import { IOption, IRightPanel } from './types';
|
||||
import SectionedOptions from './SectionedOptions';
|
||||
|
||||
const RightPanel = (props: IRightPanel) => {
|
||||
const { filterSchema, selectedFilter, selectedOptions, setSelectedOptions, tableName } = props;
|
||||
@@ -75,21 +76,33 @@ const RightPanel = (props: IRightPanel) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={cx(styles.filteredOptions, { [styles.noSearch]: !isSearchEnabled })}>
|
||||
{filteredOptionsData?.map(option => (
|
||||
<div className={styles.option} key={option.label}>
|
||||
<Checkbox
|
||||
onChange={e => handleCheckboxChange(e, option)}
|
||||
title={option.label as string}
|
||||
defaultChecked={isOptionChecked(
|
||||
option?.value as string,
|
||||
selectedOptions,
|
||||
selectedFilter
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{filteredOptionsData?.length && filteredOptionsData[0]?.filterSectionLabel ? (
|
||||
<SectionedOptions
|
||||
filteredOptionsData={filteredOptionsData}
|
||||
filterSchema={filterSchema}
|
||||
selectedOptions={selectedOptions}
|
||||
selectedFilter={selectedFilter}
|
||||
handleCheckboxChange={handleCheckboxChange}
|
||||
setSelectedOptions={setSelectedOptions}
|
||||
isSearchEnabled={isSearchEnabled}
|
||||
/>
|
||||
) : (
|
||||
<div className={cx(styles.filteredOptions, { [styles.noSearch]: !isSearchEnabled })}>
|
||||
{filteredOptionsData?.map(option => (
|
||||
<div className={styles.option} key={option.label}>
|
||||
<Checkbox
|
||||
onChange={e => handleCheckboxChange(e, option)}
|
||||
title={option.label as string}
|
||||
defaultChecked={isOptionChecked(
|
||||
option?.value as string,
|
||||
selectedOptions,
|
||||
selectedFilter
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
61
src/components/FilterDrawer/SectionedOptions.tsx
Normal file
61
src/components/FilterDrawer/SectionedOptions.tsx
Normal 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;
|
||||
@@ -10,11 +10,19 @@ import {
|
||||
import { getTableQueryParams } from '@cp/src/utils/QueryParamsHelper';
|
||||
import { Button, Typography } from '@navi/web-ui/lib/primitives';
|
||||
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 { filterSchemaMap, filterSchema, handleFilterChange, clearAllFilters } = props;
|
||||
const {
|
||||
filterSchemaMap,
|
||||
filterSchema,
|
||||
handleFilterChange,
|
||||
clearAllFilters,
|
||||
queryParamTableIdentifier = DEFAULT_QUERY_PARAM_TABLE_IDENTIFIER
|
||||
} = props;
|
||||
const CLEAR_ALL_FILTERS_COUNT = 1;
|
||||
const queryParams = getTableQueryParams(filterSchema, 'filters');
|
||||
const queryParams = getTableQueryParams(filterSchema, queryParamTableIdentifier);
|
||||
|
||||
const handleCancelIconClick = (filterKey: string) => {
|
||||
handleFilterChange(filterKey);
|
||||
@@ -23,7 +31,9 @@ const SelectedFilters = (props: ISelectedFilters) => {
|
||||
const renderSelectedFilters = useMemo(() => {
|
||||
let activeFiltersCount = 0;
|
||||
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 isActiveFilterArray = Array.isArray(activeFilters);
|
||||
const selectedOptions = isActiveFilterArray
|
||||
|
||||
@@ -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;
|
||||
42
src/components/FilterDrawer/constants.ts
Normal file
42
src/components/FilterDrawer/constants.ts
Normal 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
|
||||
];
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import LeftPanel from './LeftPanel';
|
||||
import RightPanel from './RightPanel';
|
||||
import SelectedFilters from './SelectedFilters';
|
||||
import { IFilterDrawer } from './types';
|
||||
import { FilterKeys, FiltersWithSubfiltersMapping } from './constants';
|
||||
import { DEFAULT_QUERY_PARAM_TABLE_IDENTIFIER } from './constants';
|
||||
|
||||
const FilterDrawer = (props: IFilterDrawer) => {
|
||||
const {
|
||||
@@ -31,14 +33,20 @@ const FilterDrawer = (props: IFilterDrawer) => {
|
||||
rightContainerClass,
|
||||
selectedFiltersClass,
|
||||
onApply,
|
||||
tableName
|
||||
tableName,
|
||||
queryParamTableIdentifier = DEFAULT_QUERY_PARAM_TABLE_IDENTIFIER,
|
||||
filterKeys
|
||||
} = props;
|
||||
|
||||
const mainParams = readQueryParams();
|
||||
const queryParams = getTableQueryParams(filterSchema, 'filters');
|
||||
const queryParams = getTableQueryParams(filterSchema, queryParamTableIdentifier);
|
||||
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 [selectedFilter, setSelectedFilter] = useState('');
|
||||
@@ -68,10 +76,35 @@ const FilterDrawer = (props: IFilterDrawer) => {
|
||||
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[]>) => {
|
||||
handleDateChangesInParams(selectedFilters);
|
||||
const url = createQueryParams({
|
||||
...mainParams,
|
||||
filters: {
|
||||
[queryParamTableIdentifier]: {
|
||||
...selectedFilters
|
||||
}
|
||||
});
|
||||
@@ -145,6 +178,7 @@ const FilterDrawer = (props: IFilterDrawer) => {
|
||||
filterSchema={filterSchema}
|
||||
handleFilterChange={clearFilterWithFilterKey}
|
||||
clearAllFilters={filterCtaHandler}
|
||||
queryParamTableIdentifier={queryParamTableIdentifier}
|
||||
/>
|
||||
<Button
|
||||
startAdornment={<FilterIconFilled color="var(--blue-base)" />}
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
|
||||
export interface IOption {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ISectionedOption {
|
||||
filterSectionLabel: string;
|
||||
filterSectionValue: IOption[];
|
||||
}
|
||||
|
||||
export type ISelectedOptions = Record<string, string[]>;
|
||||
|
||||
export interface IFilterDrawerSchema {
|
||||
@@ -22,6 +29,8 @@ export interface IFilterDrawer {
|
||||
selectedFiltersClass?: string;
|
||||
onApply: (selectedFilters: ISelectedOptions) => void;
|
||||
tableName: string;
|
||||
queryParamTableIdentifier?: string;
|
||||
filterKeys?: string[];
|
||||
}
|
||||
|
||||
export interface ILeftPanel {
|
||||
@@ -45,6 +54,7 @@ export interface ISelectedFilters {
|
||||
filterSchema: Record<string, IFilterDrawerSchema>;
|
||||
handleFilterChange: (filterKey: string) => void;
|
||||
clearAllFilters: (reset: boolean) => void;
|
||||
queryParamTableIdentifier?: string;
|
||||
}
|
||||
|
||||
export interface IResetSelectedOptions {
|
||||
@@ -55,3 +65,20 @@ export interface IResetSelectedOptions {
|
||||
queryParams: ISelectedOptions;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
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 = (
|
||||
filterKeyOptionsMap: ISelectedOptions,
|
||||
@@ -54,6 +68,13 @@ export const handleOptionChange = (
|
||||
) => {
|
||||
const checked = e?.target?.checked;
|
||||
let selectedOptionFilterMap = { ...selectedOptions };
|
||||
|
||||
if (DATE_QUERY_PARAMS.includes(filterKey as FilterKeys)) {
|
||||
selectedOptionFilterMap[filterKey] = [option.value as string];
|
||||
setSelectedOptions(selectedOptionFilterMap);
|
||||
return;
|
||||
}
|
||||
|
||||
if (checked) {
|
||||
if (filterSchema[filterKey].multiSelect) {
|
||||
selectedOptionFilterMap?.[filterKey]?.push(option.value as string);
|
||||
@@ -67,3 +88,28 @@ export const handleOptionChange = (
|
||||
}
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -44,7 +44,15 @@ const TableFilters = ({
|
||||
onSelectedFiltersApplied,
|
||||
onClearSelectedFilters
|
||||
}: 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 queryParams = getTableQueryParams(filterSchema, CASE_TABLE);
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ export const fetchAllCases = (
|
||||
...searchPageRequest,
|
||||
aggregationRequest: defaultAgregationRequest
|
||||
};
|
||||
if (payload && !payload.paymentStatus) {
|
||||
|
||||
if (payload && !payload.paymentStatus?.length) {
|
||||
payload.paymentStatus = ['ALL'];
|
||||
}
|
||||
if (updateParams) {
|
||||
|
||||
109
src/pages/Cases/components/CaseTableColumnCSA.tsx
Normal file
109
src/pages/Cases/components/CaseTableColumnCSA.tsx
Normal 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[];
|
||||
@@ -141,6 +141,7 @@
|
||||
|
||||
.feedbackTag {
|
||||
width: fit-content;
|
||||
line-height: 16px;
|
||||
}
|
||||
.CaseListPage {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ import {
|
||||
CasesQueryParams,
|
||||
CASE_TABLE,
|
||||
defaultAllCasesPageRequest,
|
||||
defaultPageSize
|
||||
defaultPageSize,
|
||||
CSA_TABLE_ROW_HEIGHT
|
||||
} from '../constants/CasesConstants';
|
||||
import Pagination from '@navi/web-ui/lib/components/Pagination/Pagination';
|
||||
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 AnimateWithLottie from '@cp/src/components/AnimationWithLottie';
|
||||
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 dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const tableRef = useRef(null);
|
||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||
const [searchParams] = useSearchParams();
|
||||
const { [CASE_TABLE]: queryParams } = readQueryParams();
|
||||
@@ -65,6 +70,10 @@ const Cases = () => {
|
||||
const { PAGE_SIZE, PAGE_NO, QUERY, SORT_BY_FIELD, SORT_ORDER } = CasesQueryParams;
|
||||
|
||||
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 = () => {
|
||||
columnDefs.forEach((colDef: ColDefsType) => {
|
||||
@@ -94,6 +103,7 @@ const Cases = () => {
|
||||
if (!loading && queryParams?.lastUsed === QUERY) {
|
||||
searchInputRef?.current?.focus();
|
||||
}
|
||||
tableRef?.current?.api?.sizeColumnsToFit();
|
||||
}, [loading]);
|
||||
|
||||
const updateQueryParamsAndNavigate = (
|
||||
@@ -170,7 +180,8 @@ const Cases = () => {
|
||||
|
||||
const handleSortChange = (column: ColDefsType) => {
|
||||
if (column) {
|
||||
const { field, serverSort } = column;
|
||||
const { field, serverSort, serverSortable } = column;
|
||||
if (!serverSortable) return;
|
||||
if (field) {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_CaseList_SortSuccess, {
|
||||
sortBy: field,
|
||||
@@ -214,6 +225,14 @@ const Cases = () => {
|
||||
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 (
|
||||
<div className={styles.CaseListPage}>
|
||||
<GridContainer key={location.search} className={cx(styles.cases, 'allCasesTable')}>
|
||||
@@ -231,13 +250,24 @@ const Cases = () => {
|
||||
<AnimateWithLottie animationData={eid2024Lottie} />
|
||||
) : null}
|
||||
<div className={styles.cases__filters}>
|
||||
<TableFilters
|
||||
// filterSchema={allCasesFilterSchema}
|
||||
handlerFilterChange={handlerFilterChange}
|
||||
handlerFilterClear={handleClearAllFilters}
|
||||
onSelectedFiltersApplied={() => setSelectedFiltersApplied(true)}
|
||||
onClearSelectedFilters={() => setSelectedFiltersApplied(false)}
|
||||
/>
|
||||
{csaUiRevampEnabled ? (
|
||||
<FilterDrawer
|
||||
filterButtonTitle="Filters"
|
||||
filterSchema={csaFilterSchema}
|
||||
selectedFiltersClass={styles.selectedFilters}
|
||||
onApply={noop}
|
||||
tableName={CASE_TABLE}
|
||||
queryParamTableIdentifier={CASE_TABLE}
|
||||
filterKeys={csaFilterKeys}
|
||||
/>
|
||||
) : (
|
||||
<TableFilters
|
||||
handlerFilterChange={handlerFilterChange}
|
||||
handlerFilterClear={handleClearAllFilters}
|
||||
onSelectedFiltersApplied={() => setSelectedFiltersApplied(true)}
|
||||
onClearSelectedFilters={() => setSelectedFiltersApplied(false)}
|
||||
/>
|
||||
)}
|
||||
<SearchBarInput
|
||||
containerClassName={styles.searchInputContainer}
|
||||
className={styles.searchInput}
|
||||
@@ -252,6 +282,7 @@ const Cases = () => {
|
||||
</div>
|
||||
<GridColumn md={12} className={styles.cases__table}>
|
||||
<AgTable
|
||||
ref={tableRef}
|
||||
className="AllCasesTable"
|
||||
defaultColDef={{
|
||||
suppressMovable: true,
|
||||
@@ -278,9 +309,9 @@ const Cases = () => {
|
||||
onGridReady={onCasesGridReady}
|
||||
onRowClicked={onCasesRowClicked}
|
||||
rowClass={'cursor-pointer'}
|
||||
rowHeight={csaUiRevampEnabled ? CSA_TABLE_ROW_HEIGHT : undefined}
|
||||
/>
|
||||
</GridColumn>
|
||||
{/* {loading && <div className={styles['overlay']} />} */}
|
||||
<Loader show={loading} className={'overlay'} animate={false} />
|
||||
</GridContainer>
|
||||
</div>
|
||||
|
||||
21
src/pages/Cases/components/CustomTagWithTime.tsx
Normal file
21
src/pages/Cases/components/CustomTagWithTime.tsx
Normal 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;
|
||||
@@ -2,6 +2,9 @@ import { TagColors } from '@navi/web-ui/lib/primitives/Tag/types';
|
||||
import { AlertTypesProps, AllCasesPageRequest } from './CasesInterfaces';
|
||||
|
||||
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 = [
|
||||
{ value: 'NOT_CONTACTED', label: 'Not Contacted' },
|
||||
@@ -86,7 +89,7 @@ export const defaultAllCasesPageRequest: AllCasesPageRequest = {
|
||||
interactionStatuses: [],
|
||||
dpdBuckets: [],
|
||||
posAmountRanges: [],
|
||||
paymentStatus: ['ALL']
|
||||
paymentStatus: []
|
||||
};
|
||||
|
||||
export const defaultAllCasesRequestFields = {
|
||||
@@ -177,7 +180,11 @@ export const FeedbackTypesCodeMap = {
|
||||
PROMISE_TO_PAY_LATER: { color: 'green', label: 'Promise to pay later' },
|
||||
OUT_OF_NETWORK: { color: 'red', label: 'Out of network' },
|
||||
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 };
|
||||
|
||||
export const CasesDetailsFeedbackTypesCodeMap = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 {
|
||||
casesData: AllCasesSummary[] | null;
|
||||
@@ -85,3 +85,11 @@ export interface AllCasesApiResponseData {
|
||||
data: AllCasesSummary[];
|
||||
pages: IPagination;
|
||||
}
|
||||
|
||||
export interface ICustomTagProps {
|
||||
label: string;
|
||||
color: TagColors;
|
||||
variant: TagVariants;
|
||||
labelClass: string;
|
||||
time: string;
|
||||
}
|
||||
|
||||
@@ -21,27 +21,49 @@ const initialState = {
|
||||
paymentStatus: {
|
||||
options: PAYMENT_STATUS_OPTIONS,
|
||||
multiSelect: true,
|
||||
label: 'Payment Status'
|
||||
label: 'Payment Status',
|
||||
key: 'paymentStatus'
|
||||
},
|
||||
interactionStatuses: {
|
||||
options: FEEDBACK_TYPES_OPTIONS,
|
||||
multiSelect: true,
|
||||
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: {
|
||||
options: DPD_BUCKETS_OPTIONS,
|
||||
multiSelect: true,
|
||||
label: 'DPD During Allocation'
|
||||
label: 'DPD During Allocation',
|
||||
key: 'dpdDuringAllocationFilters'
|
||||
},
|
||||
assignedAtFilters: {
|
||||
options: DATE_OF_ALLOCATION,
|
||||
multiSelect: false,
|
||||
label: 'Allocation Date'
|
||||
label: 'Allocation Date',
|
||||
key: 'assignedAtFilters'
|
||||
},
|
||||
states: {
|
||||
options: [],
|
||||
multiSelect: true,
|
||||
label: 'State of Customers'
|
||||
label: 'State of Customers',
|
||||
key: 'states'
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -93,10 +115,25 @@ const casesSlice = createSlice({
|
||||
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) {
|
||||
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.pagination = {
|
||||
|
||||
Reference in New Issue
Block a user