Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,8 @@
}
}

.global-variables {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
width: 100%;
max-height: 11.25rem;
overflow-y: auto;
margin-top: 0.75rem;

.global-variable-item {
flex-basis: 50%;
min-width: 0;
box-sizing: border-box;
padding: 0.125rem 0;
line-height: 1.75rem;
}
.cad-global-variables {
margin-top: 0.5rem;
}

/* On-hold chip styling */
Expand Down Expand Up @@ -192,6 +178,11 @@
.media-icon.chat {
color: var(--mds-color-theme-indicator-secure);
}

.campaign-call-avatar {
--mdc-avatar-default-background-color: var(--mds-color-theme-avatar-campaign);
--mdc-avatar-default-foreground-color: var(--mds-color-theme-indicator-stable);
}
}
.call-control-task-tooltip::part(popover-content) {
white-space: normal;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import React, {useRef} from 'react';
import CallControlComponent from '../CallControl/call-control';
import {Text, PopoverNext} from '@momentum-ui/react-collaboration';
import {Brandvisual, Icon, Tooltip, Button} from '@momentum-design/components/dist/react';
import {Avatar, Brandvisual, Icon, Tooltip, Button} from '@momentum-design/components/dist/react';
import './call-control-cad.styles.scss';
import TaskTimer from '../TaskTimer/index';
import CallControlConsultComponent from '../CallControl/CallControlCustom/call-control-consult';
Expand All @@ -12,6 +12,7 @@ import {
CallAssociatedDataMap,
} from '../task.types';
import {getAgentViewableGlobalVariables} from '../Task/task.utils';
import GlobalVariablesPanel from '../GlobalVariablesPanel/global-variables-panel';

import {getMediaTypeInfo} from '../../../utils';
import {
Expand All @@ -23,6 +24,7 @@ import {
QUEUE,
PHONE_NUMBER,
CUSTOMER_NAME,
CAMPAIGN_CALL,
} from '../constants';
import {withMetrics} from '@webex/cc-ui-logging';

Expand All @@ -48,6 +50,7 @@ const CallControlCADComponent: React.FC<CallControlComponentProps> = (props) =>
isMuted,
toggleMute,
conferenceParticipants,
isCampaignCall = false,
} = props;

const formatTime = (time: number): string => {
Expand Down Expand Up @@ -77,7 +80,26 @@ const CallControlCADComponent: React.FC<CallControlComponentProps> = (props) =>

//@ts-expect-error To be fixed in SDK - https://jira-eng-sjc12.cisco.com/jira/browse/CAI-6762
const callAssociatedData = currentTask?.data?.interaction?.callAssociatedData as CallAssociatedDataMap | undefined;
const globalVariables = getAgentViewableGlobalVariables(callAssociatedData);
const latestGlobalVariables = getAgentViewableGlobalVariables(callAssociatedData);

// Persist global variables across task updates — some store refreshes
// replace currentTask with a snapshot that omits callAssociatedData,
// which causes getAgentViewableGlobalVariables to return [].
// We intentionally keep the previous values when length === 0 because
// an empty array indicates missing data, not a legitimate clearing of
// variables. Variables are never cleared mid-call by the backend.
// Reset when the interaction changes so stale CAD from a previous task
// is never shown on a new call.
Comment thread
cmullenx marked this conversation as resolved.
const interactionId = currentTask.data.interaction.interactionId;
const globalVariablesRef = useRef(latestGlobalVariables);
const prevInteractionIdRef = useRef(interactionId);
if (prevInteractionIdRef.current !== interactionId) {
prevInteractionIdRef.current = interactionId;
globalVariablesRef.current = latestGlobalVariables;
} else if (latestGlobalVariables.length > 0) {
globalVariablesRef.current = latestGlobalVariables;
}
Comment thread
cmullenx marked this conversation as resolved.
const globalVariables = globalVariablesRef.current;

// Create unique IDs for tooltips
const customerNameTriggerId = `customer-name-trigger-${currentTask.data.interaction.interactionId}`;
Expand Down Expand Up @@ -178,7 +200,9 @@ const CallControlCADComponent: React.FC<CallControlComponentProps> = (props) =>
{/* Caller Information */}
<div className="caller-info">
<div className="call-icon-background">
{currentMediaType.isBrandVisual ? (
{isCampaignCall ? (
<Avatar icon-name="campaign-management-bold" className="campaign-call-avatar" />
) : currentMediaType.isBrandVisual ? (
<Brandvisual name={currentMediaType.iconName} className={`media-icon ${currentMediaType.className}`} />
) : (
<Icon name={currentMediaType.iconName} size={1} className={`media-icon ${currentMediaType.className}`} />
Expand All @@ -190,7 +214,8 @@ const CallControlCADComponent: React.FC<CallControlComponentProps> = (props) =>
<div className="call-details">
<div className="call-details-row">
<Text className="call-timer" type="body-secondary" tagName={'small'} data-testid="cc-cad:call-timer">
{currentMediaType.labelName} - <TaskTimer startTimeStamp={startTimestamp} />
{isCampaignCall ? CAMPAIGN_CALL : currentMediaType.labelName} -{' '}
<TaskTimer startTimeStamp={startTimestamp} />
{stateTimerLabel && stateTimerTimestamp && (
<>
{' '}
Expand Down Expand Up @@ -285,24 +310,7 @@ const CallControlCADComponent: React.FC<CallControlComponentProps> = (props) =>
</Text>
{renderPhoneNumber()}
</div>
{globalVariables.length > 0 && (
<div className="global-variables" data-testid="cc-cad:global-variables">
{globalVariables.map((variable) => (
<div
key={variable.name}
className="global-variable-item"
data-testid={`cc-cad:global-var-${variable.name}`}
>
<Text type="body-secondary" tagName={'small'}>
{variable.displayName || variable.name}
</Text>
<Text type="body-secondary" tagName={'small'}>
{variable.value || ''}
</Text>
</div>
))}
</div>
)}
<GlobalVariablesPanel variables={globalVariables} className="cad-global-variables" />
</div>
{controlVisibility.isConsultInitiatedOrAccepted && !controlVisibility.wrapup.isVisible && (
<div className={`call-control-consult-container ${callControlConsultClassName || ''}`}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type CampaignErrorType = 'ACCEPT_FAILED' | 'SKIP_FAILED' | 'REMOVE_FAILED';
export type CampaignErrorType = 'ACCEPT_FAILED' | 'SKIP_FAILED' | 'REMOVE_FAILED' | 'CANCEL_FAILED';

export interface CampaignErrorDialogProps {
errorType: CampaignErrorType;
Expand All @@ -10,6 +10,7 @@ export const ERROR_TITLES: Record<CampaignErrorType, string> = {
ACCEPT_FAILED: "Can't accept contact",
SKIP_FAILED: "Can't skip contact",
REMOVE_FAILED: "Can't remove contact",
CANCEL_FAILED: "Can't cancel contact",
};

export const ERROR_MESSAGE =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import React from 'react';
import {Avatar, Button, ListItem, Text, Tooltip} from '@momentum-design/components/dist/react';
import CampaignCountdown from '../../CampaignCountdown/campaign-countdown';
import TaskTimer from '../../TaskTimer/index';
import {CampaignTaskListItemProps} from '../../task.types';
import {
CAMPAIGN_ACCEPT,
CAMPAIGN_CONNECTING,
CAMPAIGN_SKIP,
CAMPAIGN_SKIP_TOOLTIP,
CAMPAIGN_SKIP_DISABLED_TOOLTIP,
CAMPAIGN_REMOVE,
CAMPAIGN_REMOVE_TOOLTIP,
CAMPAIGN_REMOVE_DISABLED_TOOLTIP,
CAMPAIGN_ACTIONS_LABEL,
HANDLE_TIME,
} from '../../constants';

/**
* CampaignTaskListItem renders the ListItem row shared between the
* CampaignTask inline card and the CampaignTaskPopover.
*
* Layout: Avatar | Title / Phone / Countdown | Accept + Skip/Remove buttons
*/
const CampaignTaskListItem: React.FC<CampaignTaskListItemProps> = ({
title,
phoneNumber,
customerName,
timeoutTimestamp,
isAcceptClicked,
isAccepted,
isAcceptDisabled,
isSkipDisabled,
isRemoveDisabled,
onAccept,
onSkip,
onRemove,
onTimeout,
handleTimestamp,
logger,
className,
testIdPrefix = 'campaign-task',
}) => {
const skipTooltipText = isSkipDisabled ? CAMPAIGN_SKIP_DISABLED_TOOLTIP : CAMPAIGN_SKIP_TOOLTIP;
const removeTooltipText = isRemoveDisabled ? CAMPAIGN_REMOVE_DISABLED_TOOLTIP : CAMPAIGN_REMOVE_TOOLTIP;
const skipButtonId = `${testIdPrefix}-skip-btn`;
const removeButtonId = `${testIdPrefix}-remove-btn`;

return (
<ListItem className={className} data-testid={`${testIdPrefix}-list-item`}>
<Avatar slot="leading-controls" icon-name="campaign-management-bold" className="campaign-avatar" />

<Text slot="leading-text-primary-label" type="body-large-medium" data-testid={`${testIdPrefix}-title`}>
{title}
</Text>
{customerName && phoneNumber && phoneNumber !== customerName && (
<Text slot="leading-text-secondary-label" type="body-midsize-regular" data-testid={`${testIdPrefix}-phone`}>
{phoneNumber}
</Text>
)}
{!isAccepted && timeoutTimestamp && (
<div slot="leading-text-tertiary-label">
<CampaignCountdown timeoutTimestamp={timeoutTimestamp} onTimeout={onTimeout} logger={logger} />
</div>
)}
{isAccepted && handleTimestamp && (
<Text
slot="leading-text-tertiary-label"
tagname="span"
type="body-midsize-regular"
className="campaign-task-handle-time"
data-testid={`${testIdPrefix}-handle-time`}
>
{HANDLE_TIME} <TaskTimer startTimeStamp={handleTimestamp} />
</Text>
)}

{!isAccepted && (
<div
slot="trailing-controls"
className="campaign-task-actions"
aria-label={CAMPAIGN_ACTIONS_LABEL}
data-testid={`${testIdPrefix}-actions`}
>
{!isAcceptClicked ? (
<Button
variant="primary"
color="positive"
size={28}
onClick={onAccept}
disabled={isAcceptDisabled}
aria-label={CAMPAIGN_ACCEPT}
data-testid={`${testIdPrefix}-accept-button`}
>
{CAMPAIGN_ACCEPT}
</Button>
) : (
<Button
variant="secondary"
size={28}
disabled
aria-label={CAMPAIGN_CONNECTING}
data-testid={`${testIdPrefix}-connecting-button`}
>
{CAMPAIGN_CONNECTING}
</Button>
)}

<div
className="campaign-task-skip-remove"
role="group"
aria-label={`${CAMPAIGN_SKIP} and ${CAMPAIGN_REMOVE}`}
data-testid={`${testIdPrefix}-skip-remove`}
>
<Button
id={skipButtonId}
variant="secondary"
size={28}
prefixIcon="skip-bold"
onClick={onSkip}
disabled={isSkipDisabled}
aria-label={CAMPAIGN_SKIP}
data-testid={`${testIdPrefix}-skip-button`}
/>
<Tooltip triggerID={skipButtonId} placement="bottom" tooltipType="label">
{skipTooltipText}
</Tooltip>

<Button
id={removeButtonId}
variant="secondary"
size={28}
prefixIcon="remove-bold"
onClick={onRemove}
disabled={isRemoveDisabled}
aria-label={CAMPAIGN_REMOVE}
data-testid={`${testIdPrefix}-remove-button`}
/>
<Tooltip triggerID={removeButtonId} placement="bottom" tooltipType="label">
{removeTooltipText}
</Tooltip>
</div>
</div>
)}
</ListItem>
);
};

export default CampaignTaskListItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.campaign-task-popover {
&__content {
display: flex;
flex-direction: column;
padding: 0.5rem;
gap: 0.5rem;
min-height: 0;
overflow: hidden;

// Cap the variables panel height inside the popover so it scrolls
// instead of overflowing past the popover boundary.
// Agent Desktop uses max-height: 120px (~7.5rem) on the details container.
.global-variables-panel--two-column {
max-height: 7.5rem;
overflow-y: auto;
}
}

&__list-item {
--mdc-listitem-padding-left-right: 0.75rem;
--mdc-listitem-padding-top-bottom: 0.5rem;
--mdc-listitem-background-color-hover: transparent;
--mdc-listitem-background-color-active: transparent;
--mdc-listitem-cursor: default;
width: 100%;

.campaign-avatar {
--mdc-avatar-default-background-color: var(--mds-color-theme-avatar-campaign);
--mdc-avatar-default-foreground-color: var(--mds-color-theme-indicator-stable);
}
}

// Action buttons — vertical column on the right (Accept on top, Skip + Remove below)
.campaign-task-actions {
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: flex-end;
gap: 0.25rem;
height: 100%;
}

.campaign-task-skip-remove {
display: flex;
align-items: center;
gap: 0.25rem;
margin-left: auto;
}

// Icon buttons (skip / remove) — match call-control circular icon button style
.campaign-task-icon-button {
width: 1.75rem !important;
height: 1.75rem !important;
}

}
Loading
Loading