From cb7ae54a01fed296ec7895efce3c7d5678dbbbae Mon Sep 17 00:00:00 2001 From: Bharath Balan <62698609+bhabalan@users.noreply.github.com> Date: Mon, 19 Jan 2026 09:20:21 +0530 Subject: [PATCH 1/2] fix(task-util): disable end button during conference with active consult --- .../task/src/Utils/task-util.ts | 7 +- .../task/tests/utils/task-util.ts | 73 ++++++++++++++++++- 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/packages/contact-center/task/src/Utils/task-util.ts b/packages/contact-center/task/src/Utils/task-util.ts index 1fdd01b92..c68934af0 100644 --- a/packages/contact-center/task/src/Utils/task-util.ts +++ b/packages/contact-center/task/src/Utils/task-util.ts @@ -122,12 +122,7 @@ export function getEndButtonVisibility( // Agent-to-agent consult during conference: End button enabled when switched back to main call if (isConsultInitiatedOrAcceptedOrBeingConsulted && isConferenceInProgress) { - return {isVisible, isEnabled: consultCallHeld}; - } - - // Regular consult without conference: End button enabled only when on main call - if (isConsultInitiatedOrAcceptedOrBeingConsulted && !isConferenceInProgress) { - return {isVisible, isEnabled: consultCallHeld}; + return {isVisible, isEnabled: false}; } // Default logic for other states diff --git a/packages/contact-center/task/tests/utils/task-util.ts b/packages/contact-center/task/tests/utils/task-util.ts index 9646f6032..defbac036 100644 --- a/packages/contact-center/task/tests/utils/task-util.ts +++ b/packages/contact-center/task/tests/utils/task-util.ts @@ -423,7 +423,7 @@ describe('getControlsVisibility', () => { }); }); - it('should enable end button when in conference and switched back from consult (consultCallHeld = true)', () => { + it('should disable end button when in conference with active consult (consultCallHeld = true)', () => { const deviceType = 'BROWSER'; const featureFlags = { isEndCallEnabled: true, @@ -485,8 +485,8 @@ describe('getControlsVisibility', () => { const result = getControlsVisibility(deviceType, featureFlags, task, 'agent1', true); - // End button should be enabled when switched back to main call from consult - expect(result.end.isEnabled).toBe(true); + // End button should be disabled during agent-to-agent consult in conference + expect(result.end.isEnabled).toBe(false); expect(result.end.isVisible).toBe(true); }); @@ -612,6 +612,73 @@ describe('getControlsVisibility', () => { expect(result.end.isEnabled).toBe(false); expect(result.end.isVisible).toBe(true); }); + + it('should disable end button during conference when consult is active (not held)', () => { + const deviceType = 'BROWSER'; + const featureFlags = { + isEndCallEnabled: true, + isEndConsultEnabled: true, + webRtcEnabled: true, + }; + + // Mock a task with conference in progress and agent switched to consult + const task = createMockTask({ + isConferenceInProgress: true, + consultMediaResourceId: 'consult', + interaction: createPartialInteraction({ + mediaType: 'telephony', + state: 'conferencing', + media: { + main: { + mediaResourceId: 'main', + mType: 'mainCall', + isHold: true, // Main is held - switched to consult + participants: ['agent1', 'agent2', 'customer1'], + }, + consult: { + mediaResourceId: 'consult', + mType: 'consult', + isHold: false, // Consult is active + participants: ['agent1', 'agent3'], + }, + }, + participants: { + agent1: { + id: 'agent1', + pType: 'Agent', + name: 'Agent One', + consultState: 'Conferencing', + isConsulted: false, + hasLeft: false, + }, + agent2: { + id: 'agent2', + pType: 'Agent', + name: 'Agent Two', + hasLeft: false, + }, + agent3: { + id: 'agent3', + pType: 'Agent', + name: 'Agent Three', + hasLeft: false, + }, + customer1: { + id: 'customer1', + pType: 'Customer', + name: 'Customer', + hasLeft: false, + }, + }, + }), + }); + + const result = getControlsVisibility(deviceType, featureFlags, task, 'agent1', true); + + // End button should be disabled during conference with active consult + expect(result.end.isEnabled).toBe(false); + expect(result.end.isVisible).toBe(true); + }); }); describe('getEndButtonVisibility - EP_DN consult scenarios', () => { From d6aed93dc417074cdc73a18eaff3d215edd17cfd Mon Sep 17 00:00:00 2001 From: Bharath Balan <62698609+bhabalan@users.noreply.github.com> Date: Wed, 21 Jan 2026 10:51:19 +0530 Subject: [PATCH 2/2] fix(task-util): improve EP-DN agent detection in consult media and update end button logic --- .../task/src/Utils/task-util.ts | 53 ++-- .../task/tests/utils/task-util.ts | 261 +++++++++++++++++- 2 files changed, 281 insertions(+), 33 deletions(-) diff --git a/packages/contact-center/task/src/Utils/task-util.ts b/packages/contact-center/task/src/Utils/task-util.ts index c68934af0..cf3b60a0a 100644 --- a/packages/contact-center/task/src/Utils/task-util.ts +++ b/packages/contact-center/task/src/Utils/task-util.ts @@ -43,22 +43,35 @@ function isTelephonySupported(deviceType: string, webRtcEnabled: boolean): boole } /** - * Check if consulting with an EP_DN agent (matches Agent Desktop's isEPorEPDN) + * Check if consulting with an EP_DN agent (Entry Point Dial Number) + * This function looks for EP-DN participants in the consult media */ function isConsultingWithEpDnAgent(task: ITask): boolean { - if (!task?.data?.interaction) { + if (!task?.data?.interaction?.media || !task?.data?.interaction?.participants) { return false; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const destAgentType = (task.data.interaction as any).destAgentType; + // Find the consult media + const consultMedia = Object.values(task.data.interaction.media).find((media) => media.mType === 'consult'); - return ( - destAgentType === DestinationAgentType.EP_DN || - destAgentType === DestinationAgentType.EPDN || - destAgentType === DestinationAgentType.ENTRY_POINT || - destAgentType === DestinationAgentType.EP - ); + if (!consultMedia || !consultMedia.participants) { + return false; + } + + // Check if any participant in the consult media is an EP-DN + const participants = task.data.interaction.participants; + return consultMedia.participants.some((participantId: string) => { + const participant = participants[participantId]; + if (!participant) return false; + + // Check for EP-DN participant types using the type field + return ( + participant.type === DestinationAgentType.EP_DN || + participant.type === DestinationAgentType.EPDN || + participant.type === DestinationAgentType.ENTRY_POINT || + participant.type === DestinationAgentType.EP + ); + }); } export function findHoldTimestamp(interaction: Interaction, mType = 'mainCall'): number | null { @@ -114,17 +127,21 @@ export function getEndButtonVisibility( const isVisible = isBrowser || (isEndCallEnabled && isCall) || !isCall; const isEpDnConsult = task && agentId ? isConsultingWithEpDnAgent(task) : false; - // EP_DN consult: End button enabled unless main call is held - if (isEpDnConsult && isConsultInitiatedOrAcceptedOrBeingConsulted) { - const isEnabled = !isHeld || (isConferenceInProgress && !isConsultCompleted); + if (isConsultInitiatedOrAcceptedOrBeingConsulted) { + let isEnabled = false; + if (isEpDnConsult) { + // EP-DN consult: enabled when on main call OR during conference when main not held + isEnabled = consultCallHeld || (!isHeld && isConferenceInProgress && !isConsultCompleted); + } else if (isConferenceInProgress) { + // Regular consult during conference: disabled to prevent ending conference prematurely + isEnabled = false; + } else { + // Regular consult without conference: enabled when on main call (consultCallHeld = true) + isEnabled = consultCallHeld; + } return {isVisible, isEnabled}; } - // Agent-to-agent consult during conference: End button enabled when switched back to main call - if (isConsultInitiatedOrAcceptedOrBeingConsulted && isConferenceInProgress) { - return {isVisible, isEnabled: false}; - } - // Default logic for other states const isEnabled = (!isHeld || (isConferenceInProgress && !isConsultCompleted)) && diff --git a/packages/contact-center/task/tests/utils/task-util.ts b/packages/contact-center/task/tests/utils/task-util.ts index defbac036..68b8abf48 100644 --- a/packages/contact-center/task/tests/utils/task-util.ts +++ b/packages/contact-center/task/tests/utils/task-util.ts @@ -689,13 +689,12 @@ describe('getEndButtonVisibility - EP_DN consult scenarios', () => { webRtcEnabled: true, }; - it('should enable end button during EP_DN consult when main call is active (not held)', () => { + it('should enable end button during EP_DN consult when switched back to main call (consultCallHeld = true)', () => { // Mock a task with EP_DN consult - switching back to main call (consult on hold) const task = createMockTask({ consultMediaResourceId: 'consult', interaction: createPartialInteraction({ mediaType: 'telephony', - destAgentType: DestinationAgentType.EP_DN, state: 'consulting', media: { main: { @@ -726,13 +725,20 @@ describe('getEndButtonVisibility - EP_DN consult scenarios', () => { name: 'Customer', hasLeft: false, }, + 'epdn-agent': { + id: 'epdn-agent', + pType: 'EP-DN', + type: DestinationAgentType.EP_DN, + name: 'Entry Point Agent', + hasLeft: false, + }, }, }), }); const result = getControlsVisibility(deviceType, featureFlags, task, 'agent1', false); - // EP_DN consult: End button should be enabled when on main call + // EP_DN consult: End button should be enabled when on main call (consultCallHeld = true) expect(result.end.isVisible).toBe(true); expect(result.end.isEnabled).toBe(true); }); @@ -743,7 +749,6 @@ describe('getEndButtonVisibility - EP_DN consult scenarios', () => { consultMediaResourceId: 'consult', interaction: createPartialInteraction({ mediaType: 'telephony', - destAgentType: DestinationAgentType.EPDN, state: 'consulting', media: { main: { @@ -774,6 +779,13 @@ describe('getEndButtonVisibility - EP_DN consult scenarios', () => { name: 'Customer', hasLeft: false, }, + 'epdn-agent': { + id: 'epdn-agent', + pType: 'EP-DN', + type: DestinationAgentType.EPDN, + name: 'Entry Point DN Agent', + hasLeft: false, + }, }, }), }); @@ -785,22 +797,27 @@ describe('getEndButtonVisibility - EP_DN consult scenarios', () => { expect(result.end.isEnabled).toBe(false); }); - it('should enable end button during EP_DN consult conference when main call is held but conference in progress', () => { + it('should enable end button during EP_DN consult conference when main call is not held', () => { // Mock a task with EP_DN consult in conference state const task = createMockTask({ isConferenceInProgress: true, consultMediaResourceId: 'consult', interaction: createPartialInteraction({ mediaType: 'telephony', - destAgentType: DestinationAgentType.ENTRY_POINT, - state: 'conferencing', + state: 'conference', media: { main: { mediaResourceId: 'main', mType: 'mainCall', - isHold: true, // Main call is held during conference + isHold: false, // Main call is not held during conference participants: ['agent1', 'customer1', 'epdn-agent'], }, + consult: { + mediaResourceId: 'consult', + mType: 'consult', + isHold: true, // Consult media on hold (merged into main) + participants: ['agent1', 'epdn-agent'], + }, }, participants: { agent1: { @@ -819,8 +836,9 @@ describe('getEndButtonVisibility - EP_DN consult scenarios', () => { }, 'epdn-agent': { id: 'epdn-agent', - pType: 'Agent', - name: 'EP DN Agent', + pType: 'EP-DN', + type: DestinationAgentType.ENTRY_POINT, + name: 'Entry Point Agent', hasLeft: false, }, }, @@ -829,15 +847,15 @@ describe('getEndButtonVisibility - EP_DN consult scenarios', () => { const result = getControlsVisibility(deviceType, featureFlags, task, 'agent1', true); + // EP_DN conference: End button enabled when main call not held + conference in progress expect(result.end.isVisible).toBe(true); expect(result.end.isEnabled).toBe(true); }); - it('should recognize EP destAgentType variant', () => { + it('should recognize EP participant type variant', () => { const task = createMockTask({ consultMediaResourceId: 'consult', interaction: createPartialInteraction({ - destAgentType: DestinationAgentType.EP, mediaType: 'telephony', state: 'consulting', media: { @@ -869,18 +887,25 @@ describe('getEndButtonVisibility - EP_DN consult scenarios', () => { name: 'Customer', hasLeft: false, }, + 'ep-agent': { + id: 'ep-agent', + pType: 'EP', + type: DestinationAgentType.EP, + name: 'Entry Point', + hasLeft: false, + }, }, }), }); const result = getControlsVisibility(deviceType, featureFlags, task, 'agent1', false); - // EP_DN consult: End button should be enabled when on main call + // EP consult: End button should be enabled when on main call (consultCallHeld = true) expect(result.end.isVisible).toBe(true); expect(result.end.isEnabled).toBe(true); }); - it('should handle missing destAgentType as non-EP_DN consult', () => { + it('should disable end button for regular agent-to-agent consult (non-EP_DN)', () => { const task = createMockTask({ consultMediaResourceId: 'consult', interaction: createPartialInteraction({ @@ -907,13 +932,25 @@ describe('getEndButtonVisibility - EP_DN consult scenarios', () => { consultState: 'Initiated', hasLeft: false, }, + agent2: { + id: 'agent2', + pType: 'Agent', + type: 'Agent', + name: 'Agent Two', + hasLeft: false, + }, + customer1: { + id: 'customer1', + pType: 'Customer', + hasLeft: false, + }, }, }), }); const result = getControlsVisibility(deviceType, featureFlags, task, 'agent1', false); - // Should follow regular consult logic (disabled when on consult call) + // Regular agent-to-agent consult: End button disabled when in consult expect(result.end.isVisible).toBe(true); expect(result.end.isEnabled).toBe(false); }); @@ -942,6 +979,200 @@ describe('getEndButtonVisibility - EP_DN consult scenarios', () => { }); }); +describe('isConsultingWithEpDnAgent', () => { + it('should detect EP-DN participant in consult media', () => { + const task = createMockTask({ + interaction: createPartialInteraction({ + mediaType: 'telephony', + media: { + consult: { + mediaResourceId: 'consult', + mType: 'consult', + participants: ['agent1', 'epdn-agent'], + }, + }, + participants: { + agent1: { + id: 'agent1', + pType: 'Agent', + }, + 'epdn-agent': { + id: 'epdn-agent', + pType: 'EP-DN', + type: DestinationAgentType.EP_DN, + }, + }, + }), + }); + + const result = getControlsVisibility('BROWSER', {isEndCallEnabled: true}, task, 'agent1', false); + // The function should detect EP-DN and apply special logic + expect(result).toBeDefined(); + }); + + it('should detect EPDN variant', () => { + const task = createMockTask({ + interaction: createPartialInteraction({ + mediaType: 'telephony', + media: { + consult: { + mediaResourceId: 'consult', + mType: 'consult', + participants: ['agent1', 'epdn-agent'], + }, + }, + participants: { + agent1: { + id: 'agent1', + pType: 'Agent', + }, + 'epdn-agent': { + id: 'epdn-agent', + type: DestinationAgentType.EPDN, + }, + }, + }), + }); + + const result = getControlsVisibility('BROWSER', {isEndCallEnabled: true}, task, 'agent1', false); + expect(result).toBeDefined(); + }); + + it('should detect ENTRY_POINT variant', () => { + const task = createMockTask({ + interaction: createPartialInteraction({ + mediaType: 'telephony', + media: { + consult: { + mediaResourceId: 'consult', + mType: 'consult', + participants: ['agent1', 'ep-agent'], + }, + }, + participants: { + agent1: { + id: 'agent1', + pType: 'Agent', + }, + 'ep-agent': { + id: 'ep-agent', + type: DestinationAgentType.ENTRY_POINT, + }, + }, + }), + }); + + const result = getControlsVisibility('BROWSER', {isEndCallEnabled: true}, task, 'agent1', false); + expect(result).toBeDefined(); + }); + + it('should detect EP variant', () => { + const task = createMockTask({ + interaction: createPartialInteraction({ + mediaType: 'telephony', + media: { + consult: { + mediaResourceId: 'consult', + mType: 'consult', + participants: ['agent1', 'ep-agent'], + }, + }, + participants: { + agent1: { + id: 'agent1', + pType: 'Agent', + }, + 'ep-agent': { + id: 'ep-agent', + type: DestinationAgentType.EP, + }, + }, + }), + }); + + const result = getControlsVisibility('BROWSER', {isEndCallEnabled: true}, task, 'agent1', false); + expect(result).toBeDefined(); + }); + + it('should return false for regular agent-to-agent consult', () => { + const task = createMockTask({ + interaction: createPartialInteraction({ + mediaType: 'telephony', + media: { + consult: { + mediaResourceId: 'consult', + mType: 'consult', + participants: ['agent1', 'agent2'], + }, + }, + participants: { + agent1: { + id: 'agent1', + pType: 'Agent', + type: 'Agent', + }, + agent2: { + id: 'agent2', + pType: 'Agent', + type: 'Agent', + }, + }, + }), + }); + + const result = getControlsVisibility('BROWSER', {isEndCallEnabled: true}, task, 'agent1', false); + expect(result).toBeDefined(); + }); + + it('should handle missing consult media gracefully', () => { + const task = createMockTask({ + interaction: createPartialInteraction({ + mediaType: 'telephony', + media: { + main: { + mediaResourceId: 'main', + mType: 'mainCall', + participants: ['agent1', 'customer1'], + }, + }, + participants: { + agent1: { + id: 'agent1', + pType: 'Agent', + }, + }, + }), + }); + + const result = getControlsVisibility('BROWSER', {isEndCallEnabled: true}, task, 'agent1', false); + expect(result).toBeDefined(); + }); + + it('should handle missing participants gracefully', () => { + const task = createMockTask({ + interaction: createPartialInteraction({ + mediaType: 'telephony', + media: { + consult: { + mediaResourceId: 'consult', + mType: 'consult', + participants: ['agent1', 'unknown'], + }, + }, + participants: { + agent1: { + id: 'agent1', + pType: 'Agent', + }, + }, + }), + }); + + const result = getControlsVisibility('BROWSER', {isEndCallEnabled: true}, task, 'agent1', false); + expect(result).toBeDefined(); + }); +}); + describe('findHoldTimestamp', () => { it('returns the holdTimestamp for the correct mType', () => { const interaction = {