From 6600981699d8ebb6c391be49c441fbd2d6498e83 Mon Sep 17 00:00:00 2001 From: Bharath Balan <62698609+bhabalan@users.noreply.github.com> Date: Wed, 18 Mar 2026 08:00:22 +0530 Subject: [PATCH 1/2] fix(cc-components): show dialed number instead of entrypoint for outdial calls When making an outdial call via dialpad, the incoming task, task list, and active task were incorrectly showing the entrypoint number (ANI) instead of the actual dialed number (DN). This fix checks for outboundType === 'OUTDIAL' and uses the DN field from callAssociatedDetails for the title display. CAI-7359 --- .../task/CallControlCAD/call-control-cad.tsx | 5 +- .../task/IncomingTask/incoming-task.utils.tsx | 7 +- .../task/TaskList/task-list.utils.ts | 7 +- .../task/IncomingTask/incoming-task.utils.tsx | 70 ++++++++++++++++ .../task/TaskList/task-list.utils.tsx | 81 +++++++++++++++++++ 5 files changed, 167 insertions(+), 3 deletions(-) diff --git a/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx b/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx index b2f31072a..00eda2dd8 100644 --- a/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx +++ b/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx @@ -69,6 +69,9 @@ const CallControlCADComponent: React.FC = (props) => //@ts-expect-error To be fixed in SDK - https://jira-eng-sjc12.cisco.com/jira/browse/CAI-6762 const dn = currentTask?.data?.interaction?.callAssociatedDetails?.dn; + // Check if this is an outdial call - for outdial, show dialed number instead of entrypoint + const isOutdial = currentTask?.data?.interaction?.outboundType === 'OUTDIAL'; + // Create unique IDs for tooltips const customerNameTriggerId = `customer-name-trigger-${currentTask.data.interaction.interactionId}`; const customerNameTooltipId = `customer-name-tooltip-${currentTask.data.interaction.interactionId}`; @@ -76,7 +79,7 @@ const CallControlCADComponent: React.FC = (props) => const phoneNumberTooltipId = `phone-number-tooltip-${currentTask.data.interaction.interactionId}`; const renderCustomerName = () => { - const customerText = isSocial ? customerName || NO_CUSTOMER_NAME : ani || NO_CALLER_ID; + const customerText = isSocial ? customerName || NO_CUSTOMER_NAME : isOutdial ? (dn || ani || NO_CALLER_ID) : (ani || NO_CALLER_ID); const textComponent = ( { }); }); + describe('Outdial tasks', () => { + it('should use dn (dialed number) as title for outdial telephony tasks', () => { + const originalMediaType = mockTask.data.interaction.mediaType; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalOutboundType = mockTask.data.interaction.outboundType; + + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.outboundType = 'OUTDIAL'; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + dn: '+14155559876', + customerName: 'Outdial Customer', + virtualTeamName: 'Outbound Team', + ronaTimeout: '30', + }; + + const result = extractIncomingTaskData(mockTask, true); + + expect(result.title).toBe('+14155559876'); // Should show dn, not ani + expect(result.ani).toBe('+18005551234'); + expect(result.isTelephony).toBe(true); + + // Restore + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.outboundType = originalOutboundType; + }); + + it('should fall back to ani when dn is not available for outdial tasks', () => { + const originalMediaType = mockTask.data.interaction.mediaType; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalOutboundType = mockTask.data.interaction.outboundType; + + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.outboundType = 'OUTDIAL'; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + customerName: 'Outdial Customer', + virtualTeamName: 'Outbound Team', + }; + + const result = extractIncomingTaskData(mockTask, true); + + expect(result.title).toBe('+18005551234'); // Falls back to ani + + // Restore + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.outboundType = originalOutboundType; + }); + + it('should use ani as title for non-outdial telephony tasks', () => { + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + dn: '+14155559876', + customerName: 'Inbound Customer', + virtualTeamName: 'Support Team', + }; + + const result = extractIncomingTaskData(mockTask, true); + + expect(result.title).toBe('+18005551234'); // Should show ani for inbound + + // Restore + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + }); + }); + describe('Edge cases', () => { it('should handle missing call association details', () => { const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; diff --git a/packages/contact-center/cc-components/tests/components/task/TaskList/task-list.utils.tsx b/packages/contact-center/cc-components/tests/components/task/TaskList/task-list.utils.tsx index ac93a7a84..be3708937 100644 --- a/packages/contact-center/cc-components/tests/components/task/TaskList/task-list.utils.tsx +++ b/packages/contact-center/cc-components/tests/components/task/TaskList/task-list.utils.tsx @@ -243,6 +243,87 @@ describe('task-list.utils', () => { mockTask.data.interaction.mediaType = originalMediaType; }); }); + + describe('Outdial tasks', () => { + it('should use dn (dialed number) as title for outdial telephony tasks', () => { + const originalState = mockTask.data.interaction.state; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalMediaType = mockTask.data.interaction.mediaType; + const originalOutboundType = mockTask.data.interaction.outboundType; + + mockTask.data.interaction.state = 'connected'; + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.outboundType = 'OUTDIAL'; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + dn: '+14155559876', + customerName: 'Outdial Customer', + virtualTeamName: 'Outbound Team', + }; + + const result = extractTaskListItemData(mockTask, true, mockTask.data.agentId); + + expect(result.title).toBe('+14155559876'); // Should show dn, not ani + expect(result.ani).toBe('+18005551234'); + expect(result.isTelephony).toBe(true); + + // Restore + mockTask.data.interaction.state = originalState; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.outboundType = originalOutboundType; + }); + + it('should fall back to ani when dn is not available for outdial tasks', () => { + const originalState = mockTask.data.interaction.state; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalMediaType = mockTask.data.interaction.mediaType; + const originalOutboundType = mockTask.data.interaction.outboundType; + + mockTask.data.interaction.state = 'connected'; + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.outboundType = 'OUTDIAL'; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + customerName: 'Outdial Customer', + virtualTeamName: 'Outbound Team', + }; + + const result = extractTaskListItemData(mockTask, true, mockTask.data.agentId); + + expect(result.title).toBe('+18005551234'); // Falls back to ani + + // Restore + mockTask.data.interaction.state = originalState; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.outboundType = originalOutboundType; + }); + + it('should use ani as title for non-outdial telephony tasks even when dn is present', () => { + const originalState = mockTask.data.interaction.state; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalMediaType = mockTask.data.interaction.mediaType; + + mockTask.data.interaction.state = 'connected'; + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + dn: '+14155559876', + customerName: 'Inbound Customer', + virtualTeamName: 'Support Team', + }; + + const result = extractTaskListItemData(mockTask, true, mockTask.data.agentId); + + expect(result.title).toBe('+18005551234'); // Should show ani for inbound + + // Restore + mockTask.data.interaction.state = originalState; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.mediaType = originalMediaType; + }); + }); }); describe('isTaskSelectable', () => { From 336ea0b81a7efe8bb926f40e3c31f7420f808997 Mon Sep 17 00:00:00 2001 From: Bharath Balan <62698609+bhabalan@users.noreply.github.com> Date: Wed, 18 Mar 2026 08:17:37 +0530 Subject: [PATCH 2/2] test(cc-components): add comprehensive outdial test coverage and fix lint errors Add 4 new test cases each for IncomingTask and TaskList outdial scenarios: - Empty dn string fallback to ani - CALLBACK outboundType uses ani (not dn) - Social media outdial uses customerName - Non-browser outdial button states Fix prettier formatting in source files. CAI-7359 --- .../task/CallControlCAD/call-control-cad.tsx | 6 +- .../task/IncomingTask/incoming-task.utils.tsx | 2 +- .../task/TaskList/task-list.utils.ts | 2 +- .../task/IncomingTask/incoming-task.utils.tsx | 103 +++++++++++++++ .../task/TaskList/task-list.utils.tsx | 118 ++++++++++++++++++ 5 files changed, 228 insertions(+), 3 deletions(-) diff --git a/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx b/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx index 00eda2dd8..2a8234f50 100644 --- a/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx +++ b/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx @@ -79,7 +79,11 @@ const CallControlCADComponent: React.FC = (props) => const phoneNumberTooltipId = `phone-number-tooltip-${currentTask.data.interaction.interactionId}`; const renderCustomerName = () => { - const customerText = isSocial ? customerName || NO_CUSTOMER_NAME : isOutdial ? (dn || ani || NO_CALLER_ID) : (ani || NO_CALLER_ID); + const customerText = isSocial + ? customerName || NO_CUSTOMER_NAME + : isOutdial + ? dn || ani || NO_CALLER_ID + : ani || NO_CALLER_ID; const textComponent = ( { // Restore mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; }); + + it('should fall back to ani when dn is empty string for outdial tasks', () => { + const originalMediaType = mockTask.data.interaction.mediaType; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalOutboundType = mockTask.data.interaction.outboundType; + + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.outboundType = 'OUTDIAL'; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + dn: '', + customerName: 'Outdial Customer', + virtualTeamName: 'Outbound Team', + }; + + const result = extractIncomingTaskData(mockTask, true); + + expect(result.title).toBe('+18005551234'); // Empty dn falls back to ani + + // Restore + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.outboundType = originalOutboundType; + }); + + it('should use ani for CALLBACK outboundType (not OUTDIAL)', () => { + const originalMediaType = mockTask.data.interaction.mediaType; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalOutboundType = mockTask.data.interaction.outboundType; + + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.outboundType = 'CALLBACK'; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + dn: '+14155559876', + customerName: 'Callback Customer', + virtualTeamName: 'Callback Team', + }; + + const result = extractIncomingTaskData(mockTask, true); + + expect(result.title).toBe('+18005551234'); // CALLBACK uses ani, not dn + + // Restore + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.outboundType = originalOutboundType; + }); + + it('should still use customerName for social media outdial tasks', () => { + const originalMediaType = mockTask.data.interaction.mediaType; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalOutboundType = mockTask.data.interaction.outboundType; + + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.SOCIAL; + mockTask.data.interaction.outboundType = 'OUTDIAL'; + mockTask.data.interaction.callAssociatedDetails = { + ani: 'social-ani', + dn: 'social-dn', + customerName: 'Social Outdial Customer', + virtualTeamName: 'Social Team', + }; + + const result = extractIncomingTaskData(mockTask, true); + + expect(result.title).toBe('Social Outdial Customer'); // Social always uses customerName + + // Restore + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.outboundType = originalOutboundType; + }); + + it('should extract correct button states for outdial telephony on non-browser', () => { + const originalMediaType = mockTask.data.interaction.mediaType; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalOutboundType = mockTask.data.interaction.outboundType; + const originalWrapUpRequired = mockTask.data.wrapUpRequired; + + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.outboundType = 'OUTDIAL'; + mockTask.data.wrapUpRequired = false; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + dn: '+14155559876', + customerName: 'Outdial Customer', + virtualTeamName: 'Outbound Team', + ronaTimeout: '30', + }; + + const result = extractIncomingTaskData(mockTask, false); + + expect(result.title).toBe('+14155559876'); + expect(result.acceptText).toBe('Ringing...'); + expect(result.declineText).toBeUndefined(); + expect(result.disableAccept).toBe(true); + + // Restore + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.outboundType = originalOutboundType; + mockTask.data.wrapUpRequired = originalWrapUpRequired; + }); }); describe('Edge cases', () => { diff --git a/packages/contact-center/cc-components/tests/components/task/TaskList/task-list.utils.tsx b/packages/contact-center/cc-components/tests/components/task/TaskList/task-list.utils.tsx index be3708937..a9bb2c65d 100644 --- a/packages/contact-center/cc-components/tests/components/task/TaskList/task-list.utils.tsx +++ b/packages/contact-center/cc-components/tests/components/task/TaskList/task-list.utils.tsx @@ -323,6 +323,124 @@ describe('task-list.utils', () => { mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; mockTask.data.interaction.mediaType = originalMediaType; }); + + it('should fall back to ani when dn is empty string for outdial tasks', () => { + const originalState = mockTask.data.interaction.state; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalMediaType = mockTask.data.interaction.mediaType; + const originalOutboundType = mockTask.data.interaction.outboundType; + + mockTask.data.interaction.state = 'connected'; + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.outboundType = 'OUTDIAL'; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + dn: '', + customerName: 'Outdial Customer', + virtualTeamName: 'Outbound Team', + }; + + const result = extractTaskListItemData(mockTask, true, mockTask.data.agentId); + + expect(result.title).toBe('+18005551234'); // Empty dn falls back to ani + + // Restore + mockTask.data.interaction.state = originalState; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.outboundType = originalOutboundType; + }); + + it('should use ani for CALLBACK outboundType (not OUTDIAL)', () => { + const originalState = mockTask.data.interaction.state; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalMediaType = mockTask.data.interaction.mediaType; + const originalOutboundType = mockTask.data.interaction.outboundType; + + mockTask.data.interaction.state = 'connected'; + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.outboundType = 'CALLBACK'; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + dn: '+14155559876', + customerName: 'Callback Customer', + virtualTeamName: 'Callback Team', + }; + + const result = extractTaskListItemData(mockTask, true, mockTask.data.agentId); + + expect(result.title).toBe('+18005551234'); // CALLBACK uses ani, not dn + + // Restore + mockTask.data.interaction.state = originalState; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.outboundType = originalOutboundType; + }); + + it('should still use customerName for social media outdial tasks', () => { + const originalState = mockTask.data.interaction.state; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalMediaType = mockTask.data.interaction.mediaType; + const originalOutboundType = mockTask.data.interaction.outboundType; + + mockTask.data.interaction.state = 'connected'; + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.SOCIAL; + mockTask.data.interaction.outboundType = 'OUTDIAL'; + mockTask.data.interaction.callAssociatedDetails = { + ani: 'social-ani', + dn: 'social-dn', + customerName: 'Social Outdial Customer', + virtualTeamName: 'Social Team', + }; + + const result = extractTaskListItemData(mockTask, true, mockTask.data.agentId); + + expect(result.title).toBe('Social Outdial Customer'); // Social always uses customerName + + // Restore + mockTask.data.interaction.state = originalState; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.outboundType = originalOutboundType; + }); + + it('should extract correct button states for incoming outdial telephony on non-browser', () => { + // Mock isIncomingTask to return true for this test + (isIncomingTask as jest.Mock).mockReturnValueOnce(true); + + const originalState = mockTask.data.interaction.state; + const originalCallAssociatedDetails = mockTask.data.interaction.callAssociatedDetails; + const originalMediaType = mockTask.data.interaction.mediaType; + const originalOutboundType = mockTask.data.interaction.outboundType; + const originalWrapUpRequired = mockTask.data.wrapUpRequired; + + mockTask.data.interaction.state = 'new'; + mockTask.data.interaction.mediaType = MEDIA_CHANNEL.TELEPHONY; + mockTask.data.interaction.outboundType = 'OUTDIAL'; + mockTask.data.wrapUpRequired = false; + mockTask.data.interaction.callAssociatedDetails = { + ani: '+18005551234', + dn: '+14155559876', + customerName: 'Outdial Customer', + virtualTeamName: 'Outbound Team', + ronaTimeout: '30', + }; + + const result = extractTaskListItemData(mockTask, false, mockTask.data.agentId); + + expect(result.title).toBe('+14155559876'); + expect(result.acceptText).toBe('Ringing...'); + expect(result.declineText).toBeUndefined(); + expect(result.disableAccept).toBe(true); + + // Restore + mockTask.data.interaction.state = originalState; + mockTask.data.interaction.callAssociatedDetails = originalCallAssociatedDetails; + mockTask.data.interaction.mediaType = originalMediaType; + mockTask.data.interaction.outboundType = originalOutboundType; + mockTask.data.wrapUpRequired = originalWrapUpRequired; + }); }); });