diff --git a/README.md b/README.md index f9df3959c91..e5b36f2e71e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![license: Cisco](https://img.shields.io/badge/License-Cisco-blueviolet?style=flat-square)](https://github.com/webex/webex-js-sdk/blob/master/LICENSE) ![state: Stable](https://img.shields.io/badge/State-Stable-blue?style=flat-square) ![scope: Public](https://img.shields.io/badge/Scope-Public-darkgreen?style=flat-square) +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/webex/webex-js-sdk) This project is designed as a mono-repository for all publicly-provided JavaScript packages from Cisco's Webex Developer Experience team. These packages consist of mostly API-related modules that allow for seamless integration with the collection of services that belong to the Webex platform. diff --git a/docs/samples/contact-center/app.js b/docs/samples/contact-center/app.js index 93e626e9363..3f95afedeba 100644 --- a/docs/samples/contact-center/app.js +++ b/docs/samples/contact-center/app.js @@ -71,6 +71,7 @@ const initiateConsultDialog = document.querySelector('#initiate-consult-dialog') const agentMultiLoginAlert = document.querySelector('#agentMultiLoginAlert'); const consultTransferBtn = document.querySelector('#consult-transfer'); const transferElm = document.getElementById('transfer'); +const transferOptionsElm = document.querySelector('#transfer-options'); const conferenceToggleBtn = document.querySelector('#conference-toggle'); const timerElm = document.querySelector('#timerDisplay'); const engageElm = document.querySelector('#engageWidget'); @@ -649,6 +650,16 @@ async function handleQueueConsult(consultPayload) { } +// Function to toggle transfer options visibility +function toggleTransferOptions() { + if (transferOptionsElm.style.display === 'none') { + transferOptionsElm.style.display = 'block'; + onTransferTypeSelectionChanged(); // Refresh the destination options + } else { + transferOptionsElm.style.display = 'none'; + } +} + // Function to initiate transfer async function initiateTransfer() { const destinationType = document.querySelector('#transfer-destination-type').value; @@ -667,6 +678,7 @@ async function initiateTransfer() { try { await currentTask.transfer(transferPayload); console.log('Transfer initiated successfully'); + transferOptionsElm.style.display = 'none'; } catch (error) { console.error('Failed to initiate transfer', error); alert('Failed to initiate transfer'); @@ -692,7 +704,7 @@ async function initiateConsultTransfer() { if (currentTask.data.isConferenceInProgress) { await currentTask.transferConference(); } else { - await currentTask.consultTransfer(consultTransferPayload); + await currentTask.transfer(consultTransferPayload); console.log('Consult transfer initiated successfully'); } } catch (error) { @@ -919,13 +931,16 @@ async function startOutdial() { try { console.log('Making an outdial call'); console.log('Destination:', destination); - console.log('Selected ANI:', selectedAni || 'None selected'); + console.log('Selected ANI:', selectedAni || 'None selected, using default ANI'); // Use selected ANI as the origin parameter if (selectedAni) { await webex.cc.startOutdial(destination, selectedAni); console.log('Outdial call initiated successfully with ANI:', selectedAni); - } + } else { + await webex.cc.startOutdial(destination); + console.log('Outdial call initiated successfully with default ANI'); + } } catch (error) { console.error('Failed to initiate outdial call', error); @@ -1013,6 +1028,12 @@ function registerTaskListeners(task) { showAgentStatePopup(reason); }); + task.on('task:outdialFailed', (reason) => { + updateTaskList(); + console.info('Outdial failed with reason:', reason); + showOutdialFailedPopup(reason); + }); + task.on('task:wrappedup', updateTaskList); // Update the task list UI to have latest tasks // Conference event listeners - Simplified approach @@ -1128,12 +1149,12 @@ function getConsultStatus(task) { const participant = Object.values(participants).find(p => p.pType === 'Agent' && p.id === agentId); if (state === 'consult') { - if (participant && participant.isConsulted) { + if ((participant && participant.isConsulted )|| isSecondaryEpDnAgent(task)) { return 'beingConsulted'; } return 'consultInitiated'; } else if (state === 'consulting') { - if (participant && participant.isConsulted) { + if ((participant && participant.isConsulted) || isSecondaryEpDnAgent(task)) { return 'beingConsultedAccepted'; } return 'consultAccepted'; @@ -1819,6 +1840,31 @@ function showAgentStatePopup(reason) { popup.classList.remove('hidden'); } +function showOutdialFailedPopup(reason) { + const outdialFailedReasonText = document.getElementById('outdialFailedReasonText'); + + // Set the reason text based on the reason + if (reason === 'CUSTOMER_BUSY') { + outdialFailedReasonText.innerText = 'Customer is busy'; + } else if (reason === 'NO_ANSWER') { + outdialFailedReasonText.innerText = 'No answer from customer'; + } else if (reason === 'CALL_FAILED') { + outdialFailedReasonText.innerText = 'Call failed'; + } else if (reason === 'INVALID_NUMBER') { + outdialFailedReasonText.innerText = 'Invalid phone number'; + } else { + outdialFailedReasonText.innerText = `Outdial failed: ${reason}`; + } + + const outdialFailedPopup = document.getElementById('outdialFailedPopup'); + outdialFailedPopup.classList.remove('hidden'); +} + +function closeOutdialFailedPopup() { + const outdialFailedPopup = document.getElementById('outdialFailedPopup'); + outdialFailedPopup.classList.add('hidden'); +} + async function renderBuddyAgents() { buddyAgentsDropdownElm.innerHTML = ''; // Clear previous options const buddyAgentsDropdownNodes = await fetchBuddyAgentsNodeList(); @@ -1955,14 +2001,14 @@ function expandAll() { function holdResumeCall() { if (holdResumeElm.innerText === 'Hold') { holdResumeElm.disabled = true; - currentTask.hold().then(() => { + currentTask.holdResume().then(() => { console.info('Call held successfully'); }).catch((error) => { console.error('Failed to hold the call', error); }); } else { holdResumeElm.disabled = true; - currentTask.resume().then(() => { + currentTask.holdResume().then(() => { console.info('Call resumed successfully'); }).catch((error) => { console.error('Failed to resume the call', error); @@ -2127,6 +2173,7 @@ function renderTaskList(taskList) { const isNew = isIncomingTask(task, agentId); const isTelephony = task.data.interaction.mediaType === 'telephony'; const isBrowserPhone = agentDeviceType === 'BROWSER'; + const isAutoAnswering = task.data.isAutoAnswering || false; // Determine which buttons to show const showAcceptButton = isNew && (isBrowserPhone || !isTelephony); @@ -2136,8 +2183,8 @@ function renderTaskList(taskList) { taskElement.innerHTML = `

${callerDisplay}

- ${showAcceptButton ? `` : ''} - ${showDeclineButton ? `` : ''} + ${showAcceptButton ? `` : ''} + ${showDeclineButton ? `` : ''}

`; @@ -2208,24 +2255,40 @@ function renderTaskList(taskList) { function enableAnswerDeclineButtons(task) { const callerDisplay = task.data.interaction?.callAssociatedDetails?.ani; const isNew = isIncomingTask(task, agentId); - const chatAndSocial = ['chat', 'social']; + const isAutoAnswering = task.data.isAutoAnswering || false; + const chatAndSocial = ['chat', 'social']; + if (task.data.interaction.mediaType === 'telephony') { if (agentDeviceType === 'BROWSER') { - answerElm.disabled = !isNew; - declineElm.disabled = !isNew; + // Disable buttons if auto-answering or not new + answerElm.disabled = !isNew || isAutoAnswering; + declineElm.disabled = !isNew || isAutoAnswering; incomingDetailsElm.innerText = `Call from ${callerDisplay}`; + + // Log auto-answer status for debugging + if (isAutoAnswering) { + console.log('✅ Auto-answer in progress for task:', task.data.interactionId); + } } else { incomingDetailsElm.innerText = `Call from ${callerDisplay}...please answer on the endpoint where the agent's extension is registered`; } } else if (chatAndSocial.includes(task.data.interaction.mediaType)) { - answerElm.disabled = !isNew; + answerElm.disabled = !isNew || isAutoAnswering; declineElm.disabled = true; incomingDetailsElm.innerText = `Chat from ${callerDisplay}`; + + if (isAutoAnswering) { + console.log('✅ Auto-answer in progress for task:', task.data.interactionId); + } } else if (task.data.interaction.mediaType === 'email') { - answerElm.disabled = !isNew; + answerElm.disabled = !isNew || isAutoAnswering; declineElm.disabled = true; incomingDetailsElm.innerText = `Email from ${callerDisplay}`; + + if (isAutoAnswering) { + console.log('✅ Auto-answer in progress for task:', task.data.interactionId); + } } } diff --git a/docs/samples/contact-center/index.html b/docs/samples/contact-center/index.html index e975b8bf0a5..01801b5b2a1 100644 --- a/docs/samples/contact-center/index.html +++ b/docs/samples/contact-center/index.html @@ -311,6 +311,15 @@

Set the state of the agent

+ + +