Skip to content

Commit f2a23d4

Browse files
rsarikaakulakum
andauthored
feat(cc-widgets): multi party conference (#539)
Co-authored-by: “Akula <[email protected]>
1 parent 6a2358c commit f2a23d4

File tree

54 files changed

+11525
-4270
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+11525
-4270
lines changed

packages/contact-center/cc-components/src/components/StationLogin/station-login.utils.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {useRef} from 'react';
22
import {StationLoginLabels} from './constants';
3-
import {LoginOptions, DESKTOP, DIALNUMBER} from '@webex/cc-store';
3+
import {LoginOptions, DESKTOP, DIAL_NUMBER} from '@webex/cc-store';
44

55
const handleModals = (
66
modalRef,
@@ -270,7 +270,7 @@ const handleDNInputChanged = (
270270
// show error for empty string
271271
setDNErrorText(`${LoginOptions[selectedDeviceType]} ${StationLoginLabels.IS_REQUIRED}`);
272272
setShowDNError(true);
273-
} else if (selectedDeviceType === DIALNUMBER) {
273+
} else if (selectedDeviceType === DIAL_NUMBER) {
274274
setShowDNError(validateDialNumber(input, dialNumberRegex, setDNErrorText, logger));
275275
} else {
276276
setShowDNError(false);

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

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,38 @@
1-
import React, {useState} from 'react';
1+
import React from 'react';
22
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';
66
import {
77
createConsultButtons,
88
getVisibleButtons,
9-
handleTransferPress,
10-
handleEndConsultPress,
11-
handleMuteToggle,
129
getConsultStatusText,
1310
createTimerKey,
1411
} from './call-control-custom.utils';
1512

1613
const CallControlConsultComponent: React.FC<CallControlConsultComponentsProps> = ({
1714
agentName,
1815
startTimeStamp,
19-
onTransfer,
16+
consultTransfer,
2017
endConsultCall,
21-
consultCompleted,
22-
isAgentBeingConsulted,
23-
isEndConsultEnabled,
18+
consultConference,
19+
switchToMainCall,
2420
logger,
25-
muteUnmute,
2621
isMuted,
27-
onToggleConsultMute,
22+
controlVisibility,
23+
toggleConsultMute,
2824
}) => {
29-
const [isMuteDisabled, setIsMuteDisabled] = useState(false);
30-
3125
const timerKey = createTimerKey(startTimeStamp);
3226

33-
const handleTransfer = () => {
34-
handleTransferPress(onTransfer, logger);
35-
};
36-
37-
const handleEndConsult = () => {
38-
handleEndConsultPress(endConsultCall, logger);
39-
};
40-
41-
const handleConsultMuteToggle = () => {
42-
handleMuteToggle(onToggleConsultMute, setIsMuteDisabled, logger);
43-
};
44-
4527
const buttons = createConsultButtons(
4628
isMuted,
47-
isMuteDisabled,
48-
consultCompleted,
49-
isAgentBeingConsulted,
50-
isEndConsultEnabled,
51-
muteUnmute,
52-
onTransfer ? handleTransfer : undefined,
53-
handleConsultMuteToggle,
54-
handleEndConsult
29+
controlVisibility,
30+
consultTransfer,
31+
toggleConsultMute,
32+
endConsultCall,
33+
consultConference,
34+
switchToMainCall,
35+
logger
5536
);
5637

5738
// Filter buttons that should be shown, then map them
@@ -66,7 +47,7 @@ const CallControlConsultComponent: React.FC<CallControlConsultComponentsProps> =
6647
{agentName}
6748
</Text>
6849
<Text tagName="p" type="body-secondary" className="consult-sub-text">
69-
{getConsultStatusText(consultCompleted)}&nbsp;&bull;&nbsp;
50+
{getConsultStatusText(controlVisibility.isConsultInitiated)}&nbsp;&bull;&nbsp;
7051
<TaskTimer key={timerKey} startTimeStamp={startTimeStamp} />
7152
</Text>
7253
</div>

packages/contact-center/cc-components/src/components/task/CallControl/CallControlCustom/call-control-custom.utils.ts

Lines changed: 52 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
11
import {BuddyDetails, ContactServiceQueue, ILogger} from '@webex/cc-store';
22
import {MUTE_CALL, UNMUTE_CALL} from '../../constants';
3-
4-
/**
5-
* Interface for button configuration
6-
*/
7-
export interface ButtonConfig {
8-
key: string;
9-
icon: string;
10-
onClick: () => void;
11-
tooltip: string;
12-
className: string;
13-
disabled?: boolean;
14-
shouldShow: boolean;
15-
}
3+
import {ButtonConfig, ControlVisibility} from '../../task.types';
164

175
/**
186
* Interface for list item data
@@ -27,43 +15,59 @@ export interface ListItemData {
2715
*/
2816
export const createConsultButtons = (
2917
isMuted: boolean,
30-
isMuteDisabled: boolean,
31-
consultCompleted: boolean,
32-
isAgentBeingConsulted: boolean,
33-
isEndConsultEnabled: boolean,
34-
muteUnmute: boolean,
35-
onTransfer?: () => void,
36-
handleConsultMuteToggle?: () => void,
37-
handleEndConsult?: () => void,
18+
controlVisibility: ControlVisibility,
19+
consultTransfer: () => void,
20+
toggleConsultMute: () => void,
21+
endConsultCall: () => void,
22+
consultConference: () => void,
23+
switchToMainCall: () => void,
3824
logger?
3925
): ButtonConfig[] => {
4026
try {
4127
return [
4228
{
4329
key: 'mute',
4430
icon: isMuted ? 'microphone-muted-bold' : 'microphone-bold',
45-
onClick: handleConsultMuteToggle || (() => {}),
31+
onClick: toggleConsultMute,
4632
tooltip: isMuted ? UNMUTE_CALL : MUTE_CALL,
4733
className: `${isMuted ? 'call-control-button-muted' : 'call-control-button'}`,
48-
disabled: isMuteDisabled,
49-
shouldShow: muteUnmute,
34+
disabled: !controlVisibility.muteUnmuteConsult.isEnabled,
35+
isVisible: controlVisibility.muteUnmuteConsult.isVisible,
36+
},
37+
{
38+
key: 'switchToMainCall',
39+
icon: 'call-swap-bold',
40+
tooltip: controlVisibility.isConferenceInProgress ? 'Switch to Conference Call' : 'Switch to Call',
41+
onClick: switchToMainCall,
42+
className: 'call-control-button',
43+
disabled: !controlVisibility.switchToMainCall.isEnabled,
44+
isVisible: controlVisibility.switchToMainCall.isVisible,
5045
},
5146
{
5247
key: 'transfer',
5348
icon: 'next-bold',
54-
tooltip: 'Transfer Consult',
55-
onClick: onTransfer || (() => {}),
49+
tooltip: controlVisibility.isConferenceInProgress ? 'Transfer Conference' : 'Transfer',
50+
onClick: consultTransfer,
5651
className: 'call-control-button',
57-
disabled: !consultCompleted,
58-
shouldShow: isAgentBeingConsulted && !!onTransfer,
52+
disabled: !controlVisibility.consultTransferConsult.isEnabled,
53+
isVisible: controlVisibility.consultTransferConsult.isVisible,
54+
},
55+
{
56+
key: 'conference',
57+
icon: 'call-merge-bold',
58+
tooltip: 'Merge',
59+
onClick: consultConference,
60+
className: 'call-control-button',
61+
disabled: !controlVisibility.mergeConferenceConsult.isEnabled,
62+
isVisible: controlVisibility.mergeConferenceConsult.isVisible,
5963
},
6064
{
6165
key: 'cancel',
6266
icon: 'headset-muted-bold',
6367
tooltip: 'End Consult',
64-
onClick: handleEndConsult || (() => {}),
68+
onClick: endConsultCall,
6569
className: 'call-control-consult-button-cancel',
66-
shouldShow: isEndConsultEnabled || isAgentBeingConsulted,
70+
isVisible: controlVisibility.endConsult.isVisible,
6771
},
6872
];
6973
} catch (error) {
@@ -82,7 +86,7 @@ export const createConsultButtons = (
8286
*/
8387
export const getVisibleButtons = (buttons: ButtonConfig[], logger?): ButtonConfig[] => {
8488
try {
85-
return buttons.filter((button) => button.shouldShow);
89+
return buttons.filter((button) => button.isVisible);
8690
} catch (error) {
8791
logger?.error('CC-Widgets: CallControlCustom: Error in getVisibleButtons', {
8892
module: 'cc-components#call-control-custom.utils.ts',
@@ -116,83 +120,12 @@ export const createInitials = (name: string, logger?): string => {
116120
}
117121
};
118122

119-
/**
120-
* Handles transfer button press with logging
121-
*/
122-
export const handleTransferPress = (onTransfer: (() => void) | undefined, logger: ILogger): void => {
123-
logger.info('CC-Widgets: CallControlConsult: transfer button clicked', {
124-
module: 'call-control-consult.tsx',
125-
method: 'handleTransfer',
126-
});
127-
128-
try {
129-
if (onTransfer) {
130-
onTransfer();
131-
logger.log('CC-Widgets: CallControlConsult: transfer completed', {
132-
module: 'call-control-consult.tsx',
133-
method: 'handleTransfer',
134-
});
135-
}
136-
} catch (error) {
137-
throw new Error(`Error transferring call: ${error}`);
138-
}
139-
};
140-
141-
/**
142-
* Handles end consult button press with logging
143-
*/
144-
export const handleEndConsultPress = (endConsultCall: (() => void) | undefined, logger: ILogger): void => {
145-
logger.info('CC-Widgets: CallControlConsult: end consult clicked', {
146-
module: 'call-control-consult.tsx',
147-
method: 'handleEndConsult',
148-
});
149-
150-
try {
151-
if (endConsultCall) {
152-
endConsultCall();
153-
logger.log('CC-Widgets: CallControlConsult: end consult completed', {
154-
module: 'call-control-consult.tsx',
155-
method: 'handleEndConsult',
156-
});
157-
}
158-
} catch (error) {
159-
throw new Error(`Error ending consult call: ${error}`);
160-
}
161-
};
162-
163-
/**
164-
* Handles mute toggle with disabled state management
165-
*/
166-
export const handleMuteToggle = (
167-
onToggleConsultMute: (() => void) | undefined,
168-
setIsMuteDisabled: (disabled: boolean) => void,
169-
logger: ILogger
170-
): void => {
171-
setIsMuteDisabled(true);
172-
173-
try {
174-
if (onToggleConsultMute) {
175-
onToggleConsultMute();
176-
}
177-
} catch (error) {
178-
logger.error(`Mute toggle failed: ${error}`, {
179-
module: 'call-control-consult.tsx',
180-
method: 'handleConsultMuteToggle',
181-
});
182-
} finally {
183-
// Re-enable button after operation
184-
setTimeout(() => {
185-
setIsMuteDisabled(false);
186-
}, 500);
187-
}
188-
};
189-
190123
/**
191124
* Gets the consult status text based on completion state
192125
*/
193-
export const getConsultStatusText = (consultCompleted: boolean, logger?): string => {
126+
export const getConsultStatusText = (consultInitiated: boolean, logger?): string => {
194127
try {
195-
return consultCompleted ? 'Consulting' : 'Consult requested';
128+
return consultInitiated ? 'Consult requested' : 'Consulting';
196129
} catch (error) {
197130
logger?.error('CC-Widgets: CallControlCustom: Error in getConsultStatusText', {
198131
module: 'cc-components#call-control-custom.utils.ts',
@@ -292,7 +225,8 @@ export const handleTabSelection = (key: string, setSelectedTab: (tab: string) =>
292225
export const handleAgentSelection = (
293226
agentId: string,
294227
agentName: string,
295-
onAgentSelect: ((agentId: string, agentName: string) => void) | undefined,
228+
allowParticipantsToInteract: boolean,
229+
onAgentSelect: ((agentId: string, agentName: string, allowParticipantsToInteract: boolean) => void) | undefined,
296230
logger: ILogger
297231
): void => {
298232
try {
@@ -301,7 +235,7 @@ export const handleAgentSelection = (
301235
method: 'onAgentSelect',
302236
});
303237
if (onAgentSelect) {
304-
onAgentSelect(agentId, agentName);
238+
onAgentSelect(agentId, agentName, allowParticipantsToInteract);
305239
}
306240
} catch (error) {
307241
logger.error(`CC-Widgets: CallControlCustom: Error in handleAgentSelection: ${error.message}`, {
@@ -317,7 +251,8 @@ export const handleAgentSelection = (
317251
export const handleQueueSelection = (
318252
queueId: string,
319253
queueName: string,
320-
onQueueSelect: ((queueId: string, queueName: string) => void) | undefined,
254+
allowParticipantsToInteract: boolean,
255+
onQueueSelect: ((queueId: string, queueName: string, allowParticipantsToInteract: boolean) => void) | undefined,
321256
logger: ILogger
322257
): void => {
323258
try {
@@ -326,7 +261,7 @@ export const handleQueueSelection = (
326261
method: 'onQueueSelect',
327262
});
328263
if (onQueueSelect) {
329-
onQueueSelect(queueId, queueName);
264+
onQueueSelect(queueId, queueName, allowParticipantsToInteract);
330265
}
331266
} catch (error) {
332267
logger.error(`CC-Widgets: CallControlCustom: Error in handleQueueSelection: ${error.message}`, {
@@ -684,10 +619,11 @@ export const debounce = <T extends (...args: unknown[]) => unknown>(
684619
export const shouldAddConsultTransferAction = (
685620
selectedCategory: string,
686621
isEntryPointTabVisible: boolean,
622+
allowParticipantsToInteract: boolean,
687623
query: string,
688624
entryPoints: {id: string; name: string}[],
689-
onDialNumberSelect: ((dialNumber: string) => void) | undefined,
690-
onEntryPointSelect: ((entryPointId: string, entryPointName: string) => void) | undefined
625+
onDialNumberSelect: (dialNumber: string, allowParticipantsToInteract: boolean) => void,
626+
onEntryPointSelect: (entryPointId: string, entryPointName: string, allowParticipantsToInteract: boolean) => void
691627
): {visible: boolean; onClick?: () => void; title?: string} => {
692628
const DN_REGEX = new RegExp('^[+1][0-9]{3,18}$|^[*#:][+1][0-9*#:]{3,18}$|^[0-9*#:]{3,18}$');
693629

@@ -697,14 +633,18 @@ export const shouldAddConsultTransferAction = (
697633

698634
if (isDial) {
699635
return valid && onDialNumberSelect
700-
? {visible: true, onClick: () => onDialNumberSelect(query), title: query}
636+
? {visible: true, onClick: () => onDialNumberSelect(query, allowParticipantsToInteract), title: query}
701637
: {visible: false};
702638
}
703639

704640
if (isEntry) {
705641
const match = query ? entryPoints?.find((e) => e.name === query || e.id === query) : null;
706642
return valid && match && onEntryPointSelect
707-
? {visible: true, onClick: () => onEntryPointSelect(match.id, match.name), title: match.name}
643+
? {
644+
visible: true,
645+
onClick: () => onEntryPointSelect(match.id, match.name, allowParticipantsToInteract),
646+
title: match.name,
647+
}
708648
: {visible: false};
709649
}
710650

0 commit comments

Comments
 (0)