Skip to content

Commit 99f962d

Browse files
authored
fix(call-timer): resolve-main-consult-conference-hold-timer-discrepancies (#569)
1 parent 89e191c commit 99f962d

20 files changed

Lines changed: 1556 additions & 105 deletions

File tree

packages/contact-center/cc-components/src/components/task/CallControl/CallControlCustom/call-control-consult.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,12 @@ import {ButtonCircle, TooltipNext, Text} from '@momentum-ui/react-collaboration'
33
import {Avatar, Icon} from '@momentum-design/components/dist/react';
44
import TaskTimer from '../../TaskTimer';
55
import {CallControlConsultComponentsProps} from '../../task.types';
6-
import {
7-
createConsultButtons,
8-
getVisibleButtons,
9-
getConsultStatusText,
10-
createTimerKey,
11-
} from './call-control-custom.utils';
6+
import {createConsultButtons, getVisibleButtons, createTimerKey} from './call-control-custom.utils';
127

138
const CallControlConsultComponent: React.FC<CallControlConsultComponentsProps> = ({
149
agentName,
15-
startTimeStamp,
10+
consultTimerLabel,
11+
consultTimerTimestamp,
1612
consultTransfer,
1713
endConsultCall,
1814
consultConference,
@@ -22,7 +18,12 @@ const CallControlConsultComponent: React.FC<CallControlConsultComponentsProps> =
2218
controlVisibility,
2319
toggleConsultMute,
2420
}) => {
25-
const timerKey = createTimerKey(startTimeStamp);
21+
// Use the label and timestamp calculated in helper.ts
22+
// Stable key based on timestamp to prevent timer resets
23+
const timerKey = createTimerKey(consultTimerTimestamp);
24+
25+
// Use consultTimerTimestamp with fallback
26+
const effectiveTimestamp = consultTimerTimestamp || Date.now();
2627

2728
const buttons = createConsultButtons(
2829
isMuted,
@@ -47,8 +48,8 @@ const CallControlConsultComponent: React.FC<CallControlConsultComponentsProps> =
4748
{agentName}
4849
</Text>
4950
<Text tagName="p" type="body-secondary" className="consult-sub-text">
50-
{getConsultStatusText(controlVisibility.isConsultInitiated)}&nbsp;&bull;&nbsp;
51-
<TaskTimer key={timerKey} startTimeStamp={startTimeStamp} />
51+
{consultTimerLabel}&nbsp;&bull;&nbsp;
52+
<TaskTimer key={timerKey} startTimeStamp={effectiveTimestamp} />
5253
</Text>
5354
</div>
5455
</div>

packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,17 @@ const CallControlCADComponent: React.FC<CallControlComponentProps> = (props) =>
2828
isRecording,
2929
holdTime,
3030
consultAgentName,
31-
consultStartTimeStamp,
31+
consultTimerLabel,
32+
consultTimerTimestamp,
3233
endConsultCall,
3334
consultTransfer,
3435
consultConference,
3536
switchToMainCall,
3637
callControlClassName,
3738
callControlConsultClassName,
3839
startTimestamp,
40+
stateTimerLabel,
41+
stateTimerTimestamp,
3942
controlVisibility,
4043
logger,
4144
isMuted,
@@ -166,6 +169,12 @@ const CallControlCADComponent: React.FC<CallControlComponentProps> = (props) =>
166169
<div className="call-details-row">
167170
<Text className="call-timer" type="body-secondary" tagName={'small'} data-testid="cc-cad:call-timer">
168171
{currentMediaType.labelName} - <TaskTimer startTimeStamp={startTimestamp} />
172+
{stateTimerLabel && stateTimerTimestamp && (
173+
<>
174+
{' '}
175+
{stateTimerLabel} - <TaskTimer startTimeStamp={stateTimerTimestamp} />
176+
</>
177+
)}
169178
</Text>
170179
{controlVisibility.isConferenceInProgress && !controlVisibility.wrapup.isVisible && (
171180
<>
@@ -271,7 +280,8 @@ const CallControlCADComponent: React.FC<CallControlComponentProps> = (props) =>
271280
<div className={`call-control-consult-container ${callControlConsultClassName || ''}`}>
272281
<CallControlConsultComponent
273282
agentName={consultAgentName}
274-
startTimeStamp={consultStartTimeStamp}
283+
consultTimerLabel={consultTimerLabel}
284+
consultTimerTimestamp={consultTimerTimestamp}
275285
endConsultCall={endConsultCall}
276286
consultTransfer={consultTransfer}
277287
consultConference={consultConference}

packages/contact-center/cc-components/src/components/task/task.types.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,9 +325,24 @@ export interface ControlProps {
325325
consultTransfer: () => void;
326326

327327
/**
328-
* Timestamp when the consult call started.
328+
* Label for the state timer (e.g., "Wrap Up", "Post Call").
329329
*/
330-
consultStartTimeStamp?: number;
330+
stateTimerLabel?: string | null;
331+
332+
/**
333+
* Timestamp for the state timer.
334+
*/
335+
stateTimerTimestamp?: number;
336+
337+
/**
338+
* Label for the consult timer (e.g., "Consulting", "Consult on Hold").
339+
*/
340+
consultTimerLabel?: string;
341+
342+
/**
343+
* Timestamp for the consult timer.
344+
*/
345+
consultTimerTimestamp?: number;
331346

332347
/**
333348
* Audio stream for the call control.
@@ -462,14 +477,17 @@ export type CallControlComponentProps = Pick<
462477
| 'exitConference'
463478
| 'endConsultCall'
464479
| 'consultTransfer'
465-
| 'consultStartTimeStamp'
466480
| 'callControlAudio'
467481
| 'consultAgentName'
468482
| 'setConsultAgentName'
469483
| 'holdTime'
470484
| 'callControlClassName'
471485
| 'callControlConsultClassName'
472486
| 'startTimestamp'
487+
| 'stateTimerLabel'
488+
| 'stateTimerTimestamp'
489+
| 'consultTimerLabel'
490+
| 'consultTimerTimestamp'
473491
| 'allowConsultToQueue'
474492
| 'lastTargetType'
475493
| 'setLastTargetType'
@@ -600,7 +618,8 @@ export interface ConsultTransferPopoverComponentProps {
600618
*/
601619
export interface CallControlConsultComponentsProps {
602620
agentName: string;
603-
startTimeStamp: number;
621+
consultTimerLabel: string;
622+
consultTimerTimestamp: number;
604623
consultTransfer: () => void;
605624
endConsultCall: () => void;
606625
consultConference: () => void;

packages/contact-center/cc-components/tests/components/task/CallControl/CallControlCustom/call-control-consult.snapshot.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ describe('CallControlConsultComponent Snapshots', () => {
106106

107107
const defaultProps = {
108108
agentName: 'Alice',
109-
startTimeStamp: Date.now(),
109+
consultTimerLabel: 'Consulting',
110+
consultTimerTimestamp: Date.now(),
110111
consultTransfer: mockOnTransfer,
111112
endConsultCall: mockEndConsultCall,
112113
toggleConsultMute: mockOnToggleConsultMute,

packages/contact-center/cc-components/tests/components/task/CallControl/CallControlCustom/call-control-consult.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ describe('CallControlConsultComponent', () => {
9191

9292
const defaultProps = {
9393
agentName: 'Alice',
94-
startTimeStamp: Date.now(),
94+
consultTimerLabel: 'Consulting',
95+
consultTimerTimestamp: Date.now(),
9596
consultTransfer: mockOnTransfer,
9697
endConsultCall: mockEndConsultCall,
9798
toggleConsultMute: mockOnToggleConsultMute,

packages/contact-center/cc-components/tests/components/task/CallControl/call-control.snapshot.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,17 @@ describe('CallControlComponent Snapshots', () => {
8787
consultCall: jest.fn(),
8888
endConsultCall: jest.fn(),
8989
consultTransfer: jest.fn(),
90-
consultStartTimeStamp: undefined,
9190
callControlAudio: null,
9291
consultAgentName: '',
9392
setConsultAgentName: jest.fn(),
9493
holdTime: 0,
9594
callControlClassName: '',
9695
callControlConsultClassName: '',
9796
startTimestamp: Date.now(),
97+
stateTimerLabel: null,
98+
stateTimerTimestamp: 0,
99+
consultTimerLabel: 'Consulting',
100+
consultTimerTimestamp: 0,
98101
allowConsultToQueue: mockProfile.allowConsultToQueue,
99102
lastTargetType: 'agent',
100103
setLastTargetType: jest.fn(),

packages/contact-center/cc-components/tests/components/task/CallControl/call-control.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,17 @@ describe('CallControlComponent', () => {
109109
consultCall: jest.fn(),
110110
endConsultCall: jest.fn(),
111111
consultTransfer: jest.fn(),
112-
consultStartTimeStamp: Date.now(),
113112
callControlAudio: null as unknown as MediaStream,
114113
consultAgentName: '',
115114
setConsultAgentName: jest.fn(),
116115
holdTime: 0,
117116
callControlClassName: '',
118117
callControlConsultClassName: '',
119118
startTimestamp: Date.now(),
119+
stateTimerLabel: null,
120+
stateTimerTimestamp: 0,
121+
consultTimerLabel: 'Consulting',
122+
consultTimerTimestamp: 0,
120123
allowConsultToQueue: true,
121124
lastTargetType: 'agent',
122125
setLastTargetType: jest.fn(),

packages/contact-center/cc-components/tests/components/task/CallControlCAD/call-control-cad.snapshot.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,17 @@ describe('CallControlCADComponent Snapshots', () => {
119119
consultCall: jest.fn(),
120120
endConsultCall: jest.fn(),
121121
consultTransfer: jest.fn(),
122-
consultStartTimeStamp: 1234567890000,
123122
callControlAudio: null,
124123
consultAgentName: '',
125124
setConsultAgentName: jest.fn(),
126125
holdTime: 0,
127126
callControlClassName: '',
128127
callControlConsultClassName: '',
129128
startTimestamp: 1234567890000,
129+
stateTimerLabel: null,
130+
stateTimerTimestamp: 0,
131+
consultTimerLabel: 'Consulting',
132+
consultTimerTimestamp: 0,
130133
allowConsultToQueue: true,
131134
lastTargetType: 'agent',
132135
setLastTargetType: jest.fn(),
@@ -272,7 +275,6 @@ describe('CallControlCADComponent Snapshots', () => {
272275
isConsultInitiatedOrAccepted: true,
273276
},
274277
consultAgentName: 'Consult Agent',
275-
consultStartTimeStamp: 1234567890000,
276278
};
277279
screen = render(<CallControlCADComponent {...consultAcceptedProps} />);
278280
mainContainer = screen.container.querySelector('.call-control-container');

packages/contact-center/cc-components/tests/components/task/CallControlCAD/call-control-cad.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,17 @@ describe('CallControlCADComponent', () => {
129129
consultCall: jest.fn(),
130130
endConsultCall: jest.fn(),
131131
consultTransfer: jest.fn(),
132-
consultStartTimeStamp: Date.now(),
133132
callControlAudio: null as unknown as MediaStream,
134133
consultAgentName: '',
135134
setConsultAgentName: jest.fn(),
136135
holdTime: 0,
137136
callControlClassName: '',
138137
callControlConsultClassName: '',
139138
startTimestamp: Date.now(),
139+
stateTimerLabel: null,
140+
stateTimerTimestamp: 0,
141+
consultTimerLabel: 'Consulting',
142+
consultTimerTimestamp: 0,
140143
allowConsultToQueue: true,
141144
lastTargetType: 'agent',
142145
setLastTargetType: jest.fn(),
@@ -232,7 +235,6 @@ describe('CallControlCADComponent', () => {
232235
const consultProps = {
233236
...defaultProps,
234237
consultAgentName: 'Consult Agent',
235-
consultStartTimeStamp: Date.now(),
236238
controlVisibility: {
237239
...mockControlVisibility,
238240
isConsultInitiatedOrAccepted: true,

packages/contact-center/store/src/task-utils.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,32 @@ export const findHoldStatus = (task: ITask, mType: string, agentId: string): boo
331331
: false
332332
: (interaction.media[mediaId] && interaction.media[mediaId].isHold) || false; // For all the other agent for main whatever is the status of main call hold
333333
};
334+
335+
/**
336+
* Finds the hold timestamp for a specific media type (mainCall, consult, etc.)
337+
* Used for timer alignment in Consult & Conference scenarios to match Agent Desktop behavior.
338+
*
339+
* @param task - The task object containing interaction data
340+
* @param mType - The media type to search for ('mainCall', 'consult', 'conference')
341+
* @returns The hold timestamp in milliseconds or null if not on hold
342+
*/
343+
export const findHoldTimestamp = (task: ITask, mType: string): number | null => {
344+
const interaction = task?.data?.interaction;
345+
346+
if (!interaction || !interaction.media) {
347+
return null;
348+
}
349+
350+
// Adjust mType if agent is secondary EPDN agent
351+
mType = setmTypeForEPDN(task, mType);
352+
353+
// Find media ID for the specified type (mainCall, consult, etc.)
354+
const mediaId = findMediaResourceId(task, mType);
355+
356+
// Return the holdTimestamp if media exists and has a hold timestamp
357+
if (mediaId && interaction.media[mediaId]?.holdTimestamp !== undefined) {
358+
return interaction.media[mediaId].holdTimestamp;
359+
}
360+
361+
return null;
362+
};

0 commit comments

Comments
 (0)