Skip to content

Commit 0566ab1

Browse files
cmullenxChristina Mullen
andauthored
feat(callcontrolcad): update to support global variables (#679)
Co-authored-by: Christina Mullen <chrmulle@cisco.com>
1 parent ebb6ca2 commit 0566ab1

7 files changed

Lines changed: 373 additions & 19 deletions

File tree

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

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@
2222
}
2323
}
2424

25+
.global-variables {
26+
display: flex;
27+
flex-flow: row wrap;
28+
justify-content: space-between;
29+
width: 100%;
30+
max-height: 11.25rem;
31+
overflow-y: auto;
32+
margin-top: 0.75rem;
33+
34+
.global-variable-item {
35+
flex-basis: 50%;
36+
min-width: 0;
37+
box-sizing: border-box;
38+
padding: 0.125rem 0;
39+
line-height: 1.75rem;
40+
}
41+
}
42+
2543
/* On-hold chip styling */
2644
.on-hold {
2745
display: flex;
@@ -44,7 +62,6 @@
4462
}
4563
}
4664

47-
4865
.recording-indicator {
4966
position: absolute;
5067
top: 0.5rem;
@@ -143,8 +160,6 @@
143160
transform: rotate(180deg);
144161
}
145162

146-
147-
148163
.call-status {
149164
display: flex;
150165
align-items: center;
@@ -200,11 +215,11 @@
200215
}
201216

202217
.digital-customer-name {
203-
width: auto;
218+
width: auto;
204219
}
205220

206221
.digital-phone-number {
207-
width: auto;
222+
width: auto;
208223
}
209224

210225
@media (min-width: 64.0625rem) and (max-width: 75rem) {
@@ -230,8 +245,8 @@
230245
max-width: 15rem;
231246
}
232247
.call-control-task-tooltip {
233-
width: 50%;
234-
text-align: center;
248+
width: 50%;
249+
text-align: center;
235250
}
236251
}
237252

@@ -246,8 +261,8 @@
246261
max-width: 13rem;
247262
}
248263
.call-control-task-tooltip {
249-
width: 50%;
250-
text-align: center;
264+
width: 50%;
265+
text-align: center;
251266
}
252267
}
253268

@@ -262,27 +277,27 @@
262277
max-width: 12rem;
263278
}
264279
.call-control-task-tooltip {
265-
width: 50%;
266-
text-align: center;
280+
width: 50%;
281+
text-align: center;
267282
}
268283
}
269284

270-
@media (min-width: 15.625rem) and (max-width: 20rem){
285+
@media (min-width: 15.625rem) and (max-width: 20rem) {
271286
.call-control-task-tooltip {
272287
width: 50%;
273288
text-align: center;
274289
}
275290
}
276291

277292
.participants-popover {
278-
background: var(--mds-color-theme-background-solid-primary-normal, #FFFFFF) !important;
293+
background: var(--mds-color-theme-background-solid-primary-normal, #ffffff) !important;
279294
align-items: normal;
280295
width: 100%;
281296
padding: 0 !important;
282297
border: var(--mds-color-theme-outline-secondary-normal);
283298

284299
&::part(popover-content) {
285-
background: var(--mds-color-theme-background-solid-primary-normal, #FFFFFF);
300+
background: var(--mds-color-theme-background-solid-primary-normal, #ffffff);
286301
border-radius: 0.75rem;
287302
border: 1px solid var(--mds-color-theme-outline-secondary-normal);
288303
box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.08);
@@ -307,7 +322,7 @@
307322
padding: 0.4rem 0.6rem;
308323
font-size: 0.875rem;
309324
cursor: pointer;
310-
color: var(--mds-color-theme-text-primary-normal, #000000F2);
325+
color: var(--mds-color-theme-text-primary-normal, #000000f2);
311326
border-radius: 0.5rem;
312327
min-width: 0;
313328
max-width: 100%;

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import {Brandvisual, Icon, Tooltip, Button} from '@momentum-design/components/di
55
import './call-control-cad.styles.scss';
66
import TaskTimer from '../TaskTimer/index';
77
import CallControlConsultComponent from '../CallControl/CallControlCustom/call-control-consult';
8-
import {MEDIA_CHANNEL as MediaChannelType, CallControlComponentProps, getCallerIdentifier} from '../task.types';
8+
import {
9+
MEDIA_CHANNEL as MediaChannelType,
10+
CallControlComponentProps,
11+
getCallerIdentifier,
12+
CallAssociatedDataMap,
13+
} from '../task.types';
14+
import {getAgentViewableGlobalVariables} from '../Task/task.utils';
915

1016
import {getMediaTypeInfo} from '../../../utils';
1117
import {
@@ -69,6 +75,10 @@ const CallControlCADComponent: React.FC<CallControlComponentProps> = (props) =>
6975
//@ts-expect-error To be fixed in SDK - https://jira-eng-sjc12.cisco.com/jira/browse/CAI-6762
7076
const dn = currentTask?.data?.interaction?.callAssociatedDetails?.dn;
7177

78+
//@ts-expect-error To be fixed in SDK - https://jira-eng-sjc12.cisco.com/jira/browse/CAI-6762
79+
const callAssociatedData = currentTask?.data?.interaction?.callAssociatedData as CallAssociatedDataMap | undefined;
80+
const globalVariables = getAgentViewableGlobalVariables(callAssociatedData);
81+
7282
// Create unique IDs for tooltips
7383
const customerNameTriggerId = `customer-name-trigger-${currentTask.data.interaction.interactionId}`;
7484
const customerNameTooltipId = `customer-name-tooltip-${currentTask.data.interaction.interactionId}`;
@@ -275,6 +285,24 @@ const CallControlCADComponent: React.FC<CallControlComponentProps> = (props) =>
275285
</Text>
276286
{renderPhoneNumber()}
277287
</div>
288+
{globalVariables.length > 0 && (
289+
<div className="global-variables" data-testid="cc-cad:global-variables">
290+
{globalVariables.map((variable) => (
291+
<div
292+
key={variable.name}
293+
className="global-variable-item"
294+
data-testid={`cc-cad:global-var-${variable.name}`}
295+
>
296+
<Text type="body-secondary" tagName={'small'}>
297+
{variable.displayName || variable.name}
298+
</Text>
299+
<Text type="body-secondary" tagName={'small'}>
300+
{variable.value || ''}
301+
</Text>
302+
</div>
303+
))}
304+
</div>
305+
)}
278306
</div>
279307
{controlVisibility.isConsultInitiatedOrAccepted && !controlVisibility.wrapup.isVisible && (
280308
<div className={`call-control-consult-container ${callControlConsultClassName || ''}`}>

packages/contact-center/cc-components/src/components/task/Task/task.utils.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,43 @@
1-
import type {MEDIA_CHANNEL as MediaChannelType, TaskComponentData} from '../task.types';
1+
import type {
2+
MEDIA_CHANNEL as MediaChannelType,
3+
TaskComponentData,
4+
CADVariable,
5+
CallAssociatedDataMap,
6+
} from '../task.types';
27
import {getMediaTypeInfo} from '../../../utils';
38

9+
/** System CAD variable keys that are already displayed elsewhere in the UI. */
10+
export const SYSTEM_CAD_KEYS = new Set([
11+
'ani',
12+
'dn',
13+
'customerName',
14+
'virtualTeamName',
15+
'ronaTimeout',
16+
'FC-DESKTOP-VIEW',
17+
]);
18+
19+
/**
20+
* Returns agent-viewable global variables from a callAssociatedData map,
21+
* excluding system variables that are already rendered elsewhere.
22+
*/
23+
export const getAgentViewableGlobalVariables = (
24+
callAssociatedData: CallAssociatedDataMap | undefined
25+
): CADVariable[] => {
26+
if (!callAssociatedData || typeof callAssociatedData !== 'object') {
27+
return [];
28+
}
29+
30+
return Object.entries(callAssociatedData)
31+
.filter(([key, cadVar]) => {
32+
if (!cadVar || !cadVar.name) return false;
33+
if (cadVar.agentViewable === false) return false;
34+
if (!cadVar.global) return false;
35+
if (SYSTEM_CAD_KEYS.has(key)) return false;
36+
return true;
37+
})
38+
.map(([, cadVar]) => cadVar);
39+
};
40+
441
/**
542
* Capitalizes the first word of a string
643
* @param str - The string to capitalize

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,29 @@ import {
1616

1717
type Enum<T extends Record<string, unknown>> = T[keyof T];
1818

19+
/**
20+
* Represents a single Call Associated Data (CAD) variable on an interaction.
21+
* Global variables have `global: true` and are set by flow control.
22+
*/
23+
export interface CADVariable {
24+
name: string;
25+
displayName: string;
26+
value: string;
27+
type: string;
28+
agentEditable: boolean;
29+
agentViewable: boolean;
30+
global: boolean;
31+
isSecure: boolean;
32+
secureKeyId: string;
33+
secureKeyVersion: number;
34+
}
35+
36+
/**
37+
* Record of CAD variables keyed by variable name.
38+
* This is the shape of `callAssociatedData` on the interaction at runtime.
39+
*/
40+
export type CallAssociatedDataMap = Record<string, CADVariable>;
41+
1942
/**
2043
* Target types for consult/transfer operations
2144
*/

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

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import React from 'react';
22
import {render} from '@testing-library/react';
33
import CallControlCADComponent from '../../../../src/components/task/CallControlCAD/call-control-cad';
4-
import {CallControlComponentProps, TARGET_TYPE, OUTBOUND_TYPE} from '../../../../src/components/task/task.types';
5-
import {mockTask} from '@webex/test-fixtures';
4+
import {
5+
CallControlComponentProps,
6+
TARGET_TYPE,
7+
OUTBOUND_TYPE,
8+
CallAssociatedDataMap,
9+
} from '../../../../src/components/task/task.types';
10+
import {mockTask, mockCallAssociatedData} from '@webex/test-fixtures';
611
import {BuddyDetails} from '@webex/cc-store';
712
import '@testing-library/jest-dom';
813

@@ -370,4 +375,92 @@ describe('CallControlCADComponent', () => {
370375
const customConsultContainer = customScreen.container.querySelector('.call-control-consult-container');
371376
expect(customConsultContainer).toHaveClass('custom-consult-control');
372377
});
378+
379+
describe('Global Variables', () => {
380+
const makePropsWithCallAssociatedData = (callAssociatedData: CallAssociatedDataMap) => ({
381+
...defaultProps,
382+
currentTask: {
383+
...defaultProps.currentTask,
384+
data: {
385+
...defaultProps.currentTask.data,
386+
interaction: {
387+
...defaultProps.currentTask.data.interaction,
388+
callAssociatedData,
389+
},
390+
},
391+
},
392+
});
393+
394+
it('should render agent-viewable global variables', () => {
395+
const screen = render(<CallControlCADComponent {...makePropsWithCallAssociatedData(mockCallAssociatedData)} />);
396+
397+
const globalVarsContainer = screen.getByTestId('cc-cad:global-variables');
398+
expect(globalVarsContainer).toBeInTheDocument();
399+
400+
expect(screen.getByTestId('cc-cad:global-var-Global_Language')).toBeInTheDocument();
401+
expect(screen.getByText('Customer Language')).toBeInTheDocument();
402+
expect(screen.getByText('English')).toBeInTheDocument();
403+
404+
expect(screen.getByTestId('cc-cad:global-var-Global_FeedbackSurveyOptIn')).toBeInTheDocument();
405+
expect(screen.getByText('Post Call Survey Opt-in')).toBeInTheDocument();
406+
expect(screen.getByText('true')).toBeInTheDocument();
407+
});
408+
409+
it('should not render non-global variables (e.g. system CAD like ani)', () => {
410+
const screen = render(<CallControlCADComponent {...makePropsWithCallAssociatedData(mockCallAssociatedData)} />);
411+
412+
expect(screen.queryByTestId('cc-cad:global-var-ani')).not.toBeInTheDocument();
413+
});
414+
415+
it('should not render global variables where agentViewable is false', () => {
416+
const screen = render(<CallControlCADComponent {...makePropsWithCallAssociatedData(mockCallAssociatedData)} />);
417+
418+
expect(screen.queryByTestId('cc-cad:global-var-Global_Hidden')).not.toBeInTheDocument();
419+
});
420+
421+
it('should not render global variables section when no global variables exist', () => {
422+
const screen = render(<CallControlCADComponent {...defaultProps} />);
423+
424+
expect(screen.queryByTestId('cc-cad:global-variables')).not.toBeInTheDocument();
425+
});
426+
427+
it('should not render global variables section when callAssociatedData is undefined', () => {
428+
const propsWithNoData = {
429+
...defaultProps,
430+
currentTask: {
431+
...defaultProps.currentTask,
432+
data: {
433+
...defaultProps.currentTask.data,
434+
interaction: {
435+
...defaultProps.currentTask.data.interaction,
436+
},
437+
},
438+
},
439+
};
440+
const screen = render(<CallControlCADComponent {...propsWithNoData} />);
441+
442+
expect(screen.queryByTestId('cc-cad:global-variables')).not.toBeInTheDocument();
443+
});
444+
445+
it('should use variable name as label when displayName is empty', () => {
446+
const dataWithEmptyDisplayName: CallAssociatedDataMap = {
447+
Global_NoDisplay: {
448+
name: 'Global_NoDisplay',
449+
displayName: '',
450+
value: 'some value',
451+
type: 'STRING',
452+
agentEditable: false,
453+
agentViewable: true,
454+
global: true,
455+
isSecure: false,
456+
secureKeyId: '',
457+
secureKeyVersion: 0,
458+
},
459+
};
460+
const screen = render(<CallControlCADComponent {...makePropsWithCallAssociatedData(dataWithEmptyDisplayName)} />);
461+
462+
expect(screen.getByText('Global_NoDisplay')).toBeInTheDocument();
463+
expect(screen.getByText('some value')).toBeInTheDocument();
464+
});
465+
});
373466
});

0 commit comments

Comments
 (0)