Skip to content

Commit b9e6c7c

Browse files
fix(incoming-task): add-uts-for-incoming-task (#499)
Co-authored-by: Shreyas Sharma <shreyassharma9912@gmail.com>
1 parent d664717 commit b9e6c7c

24 files changed

Lines changed: 12861 additions & 213 deletions

File tree

packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,38 @@
11
import React from 'react';
22
import {IncomingTaskComponentProps, MEDIA_CHANNEL} from '../task.types';
33
import Task from '../Task';
4+
import {extractIncomingTaskData} from './incoming-task.utils';
45

56
const IncomingTaskComponent: React.FunctionComponent<IncomingTaskComponentProps> = (props) => {
6-
const {incomingTask, isBrowser, accept, reject, logger} = props;
7+
const {incomingTask, isBrowser, accept, reject} = props;
78
if (!incomingTask) {
89
return <></>; // hidden component
910
}
1011

11-
//@ts-expect-error To be fixed in SDK - https://jira-eng-sjc12.cisco.com/jira/browse/CAI-6762
12-
const callAssociationDetails = incomingTask?.data?.interaction?.callAssociatedDetails;
13-
const ani = callAssociationDetails?.ani;
14-
const customerName = callAssociationDetails?.customerName;
15-
const virtualTeamName = callAssociationDetails?.virtualTeamName;
16-
const ronaTimeout = callAssociationDetails?.ronaTimeout ? Number(callAssociationDetails?.ronaTimeout) : null;
17-
const startTimeStamp = incomingTask?.data?.interaction?.createdTimestamp;
18-
const mediaType = incomingTask.data.interaction.mediaType;
19-
const mediaChannel = incomingTask.data.interaction.mediaChannel;
20-
const isTelephony = mediaType === MEDIA_CHANNEL.TELEPHONY;
21-
const isSocial = mediaType === MEDIA_CHANNEL.SOCIAL;
22-
const acceptText = !incomingTask.data.wrapUpRequired
23-
? isTelephony && !isBrowser
24-
? 'Ringing...'
25-
: 'Accept'
26-
: undefined;
27-
const declineText = !incomingTask.data.wrapUpRequired && isTelephony && isBrowser ? 'Decline' : undefined;
12+
// Extract all task data using the utility function
13+
const taskData = extractIncomingTaskData(incomingTask, isBrowser);
2814

2915
return (
3016
<Task
3117
interactionId={incomingTask.data.interactionId}
32-
title={isSocial ? customerName : ani}
18+
title={taskData.title}
3319
state=""
34-
startTimeStamp={startTimeStamp}
20+
startTimeStamp={taskData.startTimeStamp}
3521
isIncomingTask={true}
36-
queue={virtualTeamName}
22+
queue={taskData.virtualTeamName}
3723
acceptTask={() => {
38-
logger.info(
39-
`CC-Widgets: IncomingTask: accept clicked for task with interactionID: ${incomingTask.data.interactionId}`,
40-
{
41-
module: 'incoming-task.tsx',
42-
method: 'acceptTask',
43-
}
44-
);
4524
accept(incomingTask);
4625
}}
4726
declineTask={() => {
48-
logger.info(
49-
`CC-Widgets: IncomingTask: decline clicked for task with interactionID: ${incomingTask.data.interactionId}`,
50-
{
51-
module: 'incoming-task.tsx',
52-
method: 'declineTask',
53-
}
54-
);
5527
reject(incomingTask);
5628
}}
57-
ronaTimeout={ronaTimeout}
58-
acceptText={acceptText}
59-
disableAccept={isTelephony && !isBrowser}
60-
declineText={declineText}
29+
ronaTimeout={taskData.ronaTimeout}
30+
acceptText={taskData.acceptText}
31+
disableAccept={taskData.disableAccept}
32+
declineText={taskData.declineText}
6133
styles="task-list-hover"
62-
mediaType={mediaType}
63-
mediaChannel={mediaChannel}
34+
mediaType={taskData.mediaType as MEDIA_CHANNEL}
35+
mediaChannel={taskData.mediaChannel as MEDIA_CHANNEL}
6436
/>
6537
);
6638
};
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import {MEDIA_CHANNEL} from '../task.types';
2+
import {ITask} from '@webex/cc-store';
3+
4+
export interface IncomingTaskData {
5+
ani: string;
6+
customerName: string;
7+
virtualTeamName: string;
8+
ronaTimeout: number | null;
9+
startTimeStamp: number;
10+
mediaType: string;
11+
mediaChannel: string;
12+
isTelephony: boolean;
13+
isSocial: boolean;
14+
acceptText: string | undefined;
15+
declineText: string | undefined;
16+
title: string;
17+
disableAccept: boolean;
18+
}
19+
20+
/**
21+
* Extracts and processes all data needed for rendering an incoming task
22+
* @param incomingTask - The incoming task object
23+
* @param isBrowser - Whether the device type is browser
24+
* @returns Processed task data with computed values
25+
*/
26+
export const extractIncomingTaskData = (incomingTask: ITask, isBrowser: boolean): IncomingTaskData => {
27+
// Extract basic data from task
28+
//@ts-expect-error To be fixed in SDK - https://jira-eng-sjc12.cisco.com/jira/browse/CAI-6762
29+
const callAssociationDetails = incomingTask?.data?.interaction?.callAssociatedDetails;
30+
const ani = callAssociationDetails?.ani;
31+
const customerName = callAssociationDetails?.customerName;
32+
const virtualTeamName = callAssociationDetails?.virtualTeamName;
33+
const ronaTimeout = callAssociationDetails?.ronaTimeout ? Number(callAssociationDetails?.ronaTimeout) : null;
34+
const startTimeStamp = incomingTask?.data?.interaction?.createdTimestamp;
35+
const mediaType = incomingTask.data.interaction.mediaType;
36+
const mediaChannel = incomingTask.data.interaction.mediaChannel;
37+
38+
// Compute media type flags
39+
const isTelephony = mediaType === MEDIA_CHANNEL.TELEPHONY;
40+
const isSocial = mediaType === MEDIA_CHANNEL.SOCIAL;
41+
42+
// Compute button text based on conditions
43+
const acceptText = !incomingTask.data.wrapUpRequired
44+
? isTelephony && !isBrowser
45+
? 'Ringing...'
46+
: 'Accept'
47+
: undefined;
48+
49+
const declineText = !incomingTask.data.wrapUpRequired && isTelephony && isBrowser ? 'Decline' : undefined;
50+
51+
// Compute title based on media type
52+
const title = isSocial ? customerName : ani;
53+
54+
// Compute disable state for accept button
55+
const disableAccept = isTelephony && !isBrowser;
56+
57+
return {
58+
ani,
59+
customerName,
60+
virtualTeamName,
61+
ronaTimeout,
62+
startTimeStamp,
63+
mediaType,
64+
mediaChannel,
65+
isTelephony,
66+
isSocial,
67+
acceptText,
68+
declineText,
69+
title,
70+
disableAccept,
71+
};
72+
};

packages/contact-center/cc-components/src/components/task/Task/index.tsx

Lines changed: 57 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import {ButtonPill, ListItemBase, ListItemBaseSection, Text} from '@momentum-ui/
33
import {Avatar, Brandvisual, Tooltip} from '@momentum-design/components/dist/react';
44
import {PressEvent} from '@react-types/shared';
55
import TaskTimer from '../TaskTimer';
6-
import {getMediaTypeInfo} from '../../../utils';
76
import type {MEDIA_CHANNEL as MediaChannelType} from '../task.types';
7+
import {extractTaskComponentData, getTaskListItemClasses} from './task.utils';
88
import './styles.scss';
99

10-
interface TaskProps {
10+
export interface TaskProps {
1111
interactionId?: string;
1212
title?: string;
1313
state?: string;
@@ -46,50 +46,44 @@ const Task: React.FC<TaskProps> = ({
4646
mediaType,
4747
mediaChannel,
4848
}) => {
49-
const capitalizeFirstWord = (str: string) => {
50-
return str.replace(/^\s*(\w)/, (match, firstLetter) => firstLetter.toUpperCase());
51-
};
52-
const currentMediaType = getMediaTypeInfo(mediaType, mediaChannel);
53-
const isNonVoiceMedia = currentMediaType.labelName !== 'Call';
54-
// Create unique IDs for tooltip trigger and tooltip
55-
const tooltipTriggerId = `tooltip-trigger-${interactionId}`;
56-
const tooltipId = `tooltip-${interactionId}`;
57-
// Helper function to get the correct CSS class
58-
const getTitleClassName = () => {
59-
if (isNonVoiceMedia && isIncomingTask) {
60-
return 'incoming-digital-task-title';
61-
}
62-
if (isNonVoiceMedia && !isIncomingTask) {
63-
return 'task-digital-title';
64-
}
65-
return 'task-title';
66-
};
49+
// Extract all computed data using the utility function
50+
const taskData = extractTaskComponentData({
51+
mediaType,
52+
mediaChannel,
53+
isIncomingTask,
54+
interactionId,
55+
state,
56+
queue,
57+
ronaTimeout,
58+
startTimeStamp,
59+
});
60+
6761
const renderTitle = () => {
6862
if (!title) return null;
6963

7064
const textComponent = (
7165
<Text
7266
tagName="span"
7367
type={selected ? 'body-large-bold' : 'body-large-medium'}
74-
className={getTitleClassName()}
75-
id={isNonVoiceMedia ? tooltipTriggerId : undefined}
68+
className={taskData.titleClassName}
69+
id={taskData.isNonVoiceMedia ? taskData.tooltipTriggerId : undefined}
7670
>
7771
{title}
7872
</Text>
7973
);
8074

81-
if (isNonVoiceMedia) {
75+
if (taskData.isNonVoiceMedia) {
8276
return (
8377
<>
8478
{textComponent}
8579
<Tooltip
8680
color="contrast"
8781
delay="0,0"
88-
id={tooltipId}
82+
id={taskData.tooltipId}
8983
placement="top-start"
9084
offset={4}
9185
tooltip-type="description"
92-
triggerID={tooltipTriggerId}
86+
triggerID={taskData.tooltipTriggerId}
9387
className="task-tooltip"
9488
>
9589
{title}
@@ -103,48 +97,66 @@ const Task: React.FC<TaskProps> = ({
10397

10498
return (
10599
<ListItemBase
106-
className={`task-list-item ${selected ? 'task-list-item--selected' : ''} ${styles}`}
100+
className={getTaskListItemClasses(selected, styles)}
107101
onPress={onTaskSelect ? onTaskSelect : undefined}
108102
id={interactionId}
109103
>
110104
<ListItemBaseSection position="start">
111-
{currentMediaType.isBrandVisual ? (
105+
{taskData.currentMediaType.isBrandVisual ? (
112106
<div className="brand-visual-background">
113-
<Brandvisual name={currentMediaType.iconName} className={currentMediaType.className} />
107+
<Brandvisual name={taskData.currentMediaType.iconName} className={taskData.currentMediaType.className} />
114108
</div>
115109
) : (
116-
<Avatar icon-name={currentMediaType.iconName} className={currentMediaType.className} />
110+
<Avatar icon-name={taskData.currentMediaType.iconName} className={taskData.currentMediaType.className} />
117111
)}
118112
</ListItemBaseSection>
119113

120114
<ListItemBaseSection position="fill">
121115
<section className="task-details">
122116
{renderTitle()}
123-
{state && !isIncomingTask && (
124-
<Text tagName="span" type="body-midsize-regular" className="task-text">
125-
{capitalizeFirstWord(state)}
117+
{taskData.shouldShowState && (
118+
<Text
119+
tagName="span"
120+
type="body-midsize-regular"
121+
className="task-text"
122+
data-testid={`${interactionId}-state`}
123+
>
124+
{taskData.capitalizedState}
126125
</Text>
127126
)}
128127

129-
{queue && isIncomingTask && (
130-
<Text tagName="span" type="body-midsize-regular" className="task-text">
131-
{capitalizeFirstWord(queue)}
128+
{taskData.shouldShowQueue && (
129+
<Text
130+
tagName="span"
131+
type="body-midsize-regular"
132+
className="task-text"
133+
data-testid={`${interactionId}-queue`}
134+
>
135+
{taskData.capitalizedQueue}
132136
</Text>
133137
)}
134138

135139
{/* Handle Time should render if it's an incoming call without ronaTimeout OR if it's not an incoming call */}
136-
{(isIncomingTask && !ronaTimeout) || !isIncomingTask
137-
? startTimeStamp && (
138-
<Text tagName="span" type="body-midsize-regular" className="task-text">
139-
Handle Time: {' '}
140-
<TaskTimer startTimeStamp={startTimeStamp} />
141-
</Text>
142-
)
143-
: null}
140+
{taskData.shouldShowHandleTime && (
141+
<Text
142+
tagName="span"
143+
type="body-midsize-regular"
144+
className="task-text"
145+
data-testid={`${interactionId}-handle-time`}
146+
>
147+
Handle Time: {' '}
148+
<TaskTimer startTimeStamp={startTimeStamp} />
149+
</Text>
150+
)}
144151

145152
{/* Time Left should render if it's an incoming call with ronaTimeout */}
146-
{isIncomingTask && ronaTimeout && (
147-
<Text tagName="span" type="body-midsize-regular" className="task-text">
153+
{taskData.shouldShowTimeLeft && (
154+
<Text
155+
tagName="span"
156+
type="body-midsize-regular"
157+
className="task-text"
158+
data-testid={`${interactionId}-time-left`}
159+
>
148160
Time Left: {' '}
149161
<TaskTimer countdown={true} ronaTimeout={ronaTimeout} />
150162
</Text>

0 commit comments

Comments
 (0)