Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugin-cc): added-call-control #4015

Merged
merged 46 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1122f07
feat(plugin-cc): call accept implemented
Kesari3008 Nov 26, 2024
acba03a
feat(plugin-cc): latest merge
Kesari3008 Nov 26, 2024
39a55a4
feat(plugin-cc): added ut, added sample app changes
Kesari3008 Nov 26, 2024
616d973
feat(plugin-cc): fixes in code
Kesari3008 Nov 26, 2024
555289d
feat(plugin-cc): manual testing for call answer and decline finished
Kesari3008 Nov 27, 2024
3754f66
feat(plugin-cc): small fixes in sample app
Kesari3008 Nov 28, 2024
d3809b2
feat(plugin-cc): fixed ut
Kesari3008 Nov 28, 2024
9b40fde
feat(plugin-cc): fixed ut
Kesari3008 Nov 28, 2024
e756062
feat(plugin-cc): ut fix
Kesari3008 Dec 1, 2024
cfe40f6
feat(plugin-cc): review comments
Kesari3008 Dec 2, 2024
963fa03
feat(plugin-cc): ut fixes
Kesari3008 Dec 2, 2024
37ec111
feat(plugin-cc): ut fixes
Kesari3008 Dec 2, 2024
e8dcefd
feat(plugin-cc): merge conflict
Kesari3008 Dec 2, 2024
c9f2e2c
feat(plugin-cc): review comments thrid round
Kesari3008 Dec 3, 2024
2d3b7ee
feat(plugin-cc): sample app fixes
Kesari3008 Dec 3, 2024
cc99ac8
feat(plugin-cc): test file import fixes
Kesari3008 Dec 3, 2024
4ac05c2
feat(cc-sdk): added-call-control
adhmenon Dec 3, 2024
ac450ed
fix(plugin-cc): rename folders
sreenara Dec 3, 2024
8f6563f
feat(cc-sdk): added-log-changes
adhmenon Dec 4, 2024
064328f
feat(cc-sdk): fixed-sample-app-issue
adhmenon Dec 4, 2024
8917ab4
feat(cc-sdk): added-wrapup-dropdown
adhmenon Dec 4, 2024
5b290c5
Merge branch 'SPARK-562169-Task-API-Implementation' of https://github…
adhmenon Dec 4, 2024
ee6908a
feat(plugin-cc): review comments
Kesari3008 Dec 4, 2024
847f0cc
Merge branch 'SPARK-562169-Task-API-Implementation' of github.com:Kes…
Kesari3008 Dec 4, 2024
338202d
feat(cc-sdk): fixed-renaming-issue
adhmenon Dec 4, 2024
f381fab
fix(plugin-cc): ut-fixes
Kesari3008 Dec 4, 2024
890e2be
Merge branch 'SPARK-562169-Task-API-Implementation' of https://github…
adhmenon Dec 4, 2024
4c6a2bc
fix(plugin-cc): ut added for off listeners
Kesari3008 Dec 4, 2024
fa4c42e
feat(plugin-cc): added types and add ut for cc file
Kesari3008 Dec 4, 2024
d65c4fd
Merge branch 'SPARK-562169-Task-API-Implementation' of https://github…
adhmenon Dec 5, 2024
352315f
feat(cc-sdk): fixed-pipeline-fix
adhmenon Dec 5, 2024
93dc3d0
feat(plugin-cc): updated logs
Kesari3008 Dec 6, 2024
a0d93ca
feat(cc-sdk): added-recording-pause-and-resume
adhmenon Dec 6, 2024
098948e
feat(cc-sdk): added-task-response-unit-tests
adhmenon Dec 6, 2024
2f154e3
feat(cc-sdk): added-end-call-edge-case
adhmenon Dec 6, 2024
1f2cee9
Merge branch 'SPARK-562169-Task-API-Implementation' of https://github…
adhmenon Dec 6, 2024
daeb3e5
feat(cc-sdk): added-cleanup-on-call-wrapup
adhmenon Dec 6, 2024
7988048
feat(plugin-cc): fixed the issue with incoming call being received wi…
Kesari3008 Dec 6, 2024
441c992
Merge branch 'SPARK-562169-Task-API-Implementation' of https://github…
adhmenon Dec 6, 2024
05b9ace
feat(cc-sdk): added-tests-for-edge-cases
adhmenon Dec 6, 2024
1d7a016
fix(plugin-cc): review comments
Kesari3008 Dec 11, 2024
f058339
Merge branch 'SPARK-562169-Task-API-Implementation' of https://github…
adhmenon Dec 11, 2024
57f71e1
fix(plugin-cc): review comments
Kesari3008 Dec 11, 2024
65cd8fe
Merge branch 'SPARK-562169-Task-API-Implementation' of https://github…
adhmenon Dec 11, 2024
83c4345
Merge branch 'wxcc' of https://github.com/webex/webex-js-sdk into dev…
adhmenon Dec 12, 2024
1f56afa
feat(wxcc): fixed-merge-conflicts
adhmenon Dec 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 118 additions & 3 deletions docs/samples/contact-center/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ let deviceId;
let agentStatusId;
let agentStatus;
let agentId;
let taskControl;
let task;
let taskId;
let agentAuxCodeId;

const authTypeElm = document.querySelector('#auth-type');
const credentialsFormElm = document.querySelector('#credentials');
Expand All @@ -27,13 +31,22 @@ const authStatusElm = document.querySelector('#access-token-status');
const registerBtn = document.querySelector('#webexcc-register');
const teamsDropdown = document.querySelector('#teamsDropdown');
const agentLogin = document.querySelector('#AgentLogin');
const agentLoginButton = document.querySelector('#loginAgent');
const loginAgentElm = document.querySelector('#loginAgent');
const dialNumber = document.querySelector('#dialNumber');
const registerStatus = document.querySelector('#ws-connection-status');
const idleCodesDropdown = document.querySelector('#idleCodesDropdown')
const setAgentStatusButton = document.querySelector('#setAgentStatus');
const logoutAgentElm = document.querySelector('#logoutAgent');
const buddyAgentsDropdownElm = document.getElementById('buddyAgentsDropdown');
const incomingCallListener = document.querySelector('#incomingsection');
const incomingDetailsElm = document.querySelector('#incoming-call');
const answerElm = document.querySelector('#answer');
const declineElm = document.querySelector('#decline');
const callControlListener = document.querySelector('#callcontrolsection');
const holdElm = document.querySelector('#hold');
const resumeElm = document.querySelector('#resume');
const endElm = document.querySelector('#end');
const wrapupElm = document.querySelector('#wrapup');

// Store and Grab `access-token` from sessionStorage
if (sessionStorage.getItem('date') > new Date().getTime()) {
Expand Down Expand Up @@ -70,6 +83,19 @@ function toggleDisplay(elementId, status) {
}
}

const taskEvents = new CustomEvent('task:incoming', {
detail: {
task: task,
},
});

// TODO: Activate the call control buttons once the call is accepted and refctor this
function registerListeners(task) {
task.on('task:assigned', (task) => {
console.log('Call has been accepted');
})
}

function generateWebexConfig({credentials}) {
return {
appName: 'sdk-samples',
Expand Down Expand Up @@ -122,6 +148,7 @@ function register() {
teamsDropdown.innerHTML = ''; // Clear previously selected option on teamsDropdown
const listTeams = agentProfile.teams;
agentId = agentProfile.agentId;
agentAuxCodeId = agentProfile.defaultWrapupCode;
adhmenon marked this conversation as resolved.
Show resolved Hide resolved
listTeams.forEach((team) => {
const option = document.createElement('option');
option.value = team.id;
Expand All @@ -132,7 +159,7 @@ function register() {
agentLogin.innerHTML = '<option value="" selected>Choose Agent Login ...</option>'; // Clear previously selected option on agentLogin.
dialNumber.value = agentProfile.defaultDn ? agentProfile.defaultDn : '';
dialNumber.disabled = agentProfile.defaultDn ? false : true;
if(loginVoiceOptions.length > 0) agentLoginButton.disabled = false;
if (loginVoiceOptions.length > 0) loginAgentElm.disabled = false;
loginVoiceOptions.forEach((voiceOptions)=> {
const option = document.createElement('option');
option.text = voiceOptions;
Expand All @@ -157,10 +184,15 @@ function register() {
idleCodesDropdown.add(option);
}
});

}).catch((error) => {
console.error('Event subscription failed', error);
})

webex.cc.eventEmitter.on('task:incoming', (task) => {
taskEvents.detail.task = task;

incomingCallListener.dispatchEvent(taskEvents);
})
}

async function handleAgentLogin(e) {
Expand All @@ -178,6 +210,7 @@ async function handleAgentLogin(e) {
function doAgentLogin() {
webex.cc.stationLogin({teamId: teamsDropdown.value, loginOption: agentDeviceType, dialNumber: dialNumber.value}).then((response) => {
console.log('Agent Logged in successfully', response);
loginAgentElm.disabled = true;
logoutAgentElm.classList.remove('hidden');
}
).catch((error) => {
Expand Down Expand Up @@ -205,6 +238,7 @@ function setAgentStatus() {
function logoutAgent() {
webex.cc.stationLogout({logoutReason: 'logout'}).then((response) => {
console.log('Agent logged out successfully', response);
loginAgentElm.disabled = false;

setTimeout(() => {
logoutAgentElm.classList.add('hidden');
Expand Down Expand Up @@ -247,6 +281,39 @@ async function fetchBuddyAgents() {
}
}

incomingCallListener.addEventListener('task:incoming', (event) => {
taskId = event.detail.task.data.interactionId;
task = event.detail.task;
const callerDisplay = event.detail.task.data.interaction.callAssociatedDetails.ani;

if (event.detail.task.webCallingService.loginOption === 'BROWSER') {
answerElm.disabled = false;
declineElm.disabled = false;

incomingDetailsElm.innerText = `Call from ${callerDisplay}`;
} else {
incomingDetailsElm.innerText = `Call from ${callerDisplay}...please answer on the endpoint where the agent's extension is registered`;
}
});

function answer() {
answerElm.disabled = true;
declineElm.disabled = true;
holdElm.disabled = false;
resumeElm.disabled = true;
endElm.disabled = false;
wrapupElm.disabled = true;
task.accept(taskId);
incomingDetailsElm.innerText = 'Call Accepted';
}

function decline() {
answerElm.disabled = true;
declineElm.disabled = true;
task.decline(taskId);
incomingDetailsElm.innerText = 'No incoming calls';
}

const allCollapsibleElements = document.querySelectorAll('.collapsible');
allCollapsibleElements.forEach((el) => {
el.addEventListener('click', (event) => {
Expand Down Expand Up @@ -304,3 +371,51 @@ function expandAll() {
});
}

function holdCall() {
holdElm.disabled = true;
webex.cc.taskManager.task.hold(taskId).then(() => {
console.info('Call held successfully');
resumeElm.disabled = false;
}).catch((error) => {
console.error('Failed to hold the call', error);
holdElm.disabled = false;
});
}

function resumeCall() {
resumeElm.disabled = true;
webex.cc.taskManager.task.resume(taskId).then(() => {
console.info('Call resumed successfully');
holdElm.disabled = false;
}).catch((error) => {
console.error('Failed to resume the call', error);
resumeElm.disabled = false;
});
}

function endCall() {
endElm.disabled = true;
webex.cc.taskManager.task.end(taskId).then((agent) => {
console.log('Call ended successfully');
holdElm.disabled = true;
resumeElm.disabled = true;
wrapupElm.disabled = false;
}).catch((error) => {
console.error('Failed to end the call', error);
endElm.disabled = false;
});
}

function wrapupCall() {
adhmenon marked this conversation as resolved.
Show resolved Hide resolved
wrapupElm.disabled = true;
webex.cc.taskManager.task.wrapup(taskId, {wrapUpReason: 'Sale', auxCodeId: agentAuxCodeId}).then((wrapup) => {
console.info('Call wrapped up successfully');
holdElm.disabled = true;
resumeElm.disabled = true;
endElm.disabled = true;
}).catch((error) => {
console.error('Failed to wrap up the call', error);
wrapupElm.disabled = false;
});
}

28 changes: 25 additions & 3 deletions docs/samples/contact-center/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,8 @@ <h2 class="collapsible">
<option value="" selected hidden>Choose Agent Login ...</option>
</select>
<input id="dialNumber" name="dialNumber" placeholder="Dial Number" value="" type="text">
<button id="loginAgent" disabled class="btn btn-primary my-3" onclick="doAgentLogin()">Login With
Selected Team</button>
<button id="logoutAgent" class="btn btn-primary my-3 ml-2 hidden" onclick="logoutAgent()">Logout Agent</button>
<button id="loginAgent" disabled class="btn btn-primary my-3" onclick="doAgentLogin()">Login</button>
<button id="logoutAgent" class="btn btn-primary my-3 ml-2 hidden" onclick="logoutAgent()">Logout</button>
</fieldset>
<fieldset>
<legend>Agent status</legend>
Expand All @@ -131,6 +130,29 @@ <h2 class="collapsible">
</div>
</fieldset>
</div>
<!-- calling / incoming -->
<div id="incomingsection">
<fieldset>
<legend>Incoming Call</legend>
<div class="u-mv">
<pre id="incoming-call"> No Incoming Calls</pre>
<button onclick="answer()" disabled="" id="answer" class="btn--green">Answer</button>
<button onclick="decline()" disabled="" id="decline" class="btn--red">Decline</button>
</div>
</fieldset>
</div>
<!-- Agent Call Controls -->
<div id="callcontrolsection">
<fieldset>
<legend>Call Controls</legend>
<div class="u-mv">
<button onclick="holdCall()" id="hold" class="btn--yellow" disabled>Hold</button>
adhmenon marked this conversation as resolved.
Show resolved Hide resolved
<button onclick="resumeCall()" id="resume" class="btn--blue" disabled>Resume</button>
<button onclick="endCall()" id="end" class="btn--red" disabled>End</button>
<button onclick="wrapupCall()" id="wrapup" class="btn--green" disabled>Wrapup</button>
</div>
</fieldset>
</div>
</section>
</div>
</div>
Expand Down
27 changes: 24 additions & 3 deletions packages/@webex/plugin-cc/src/cc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {WebexPlugin} from '@webex/webex-core';
import EventEmitter from 'events';
import {
SetStateResponse,
CCPluginConfig,
Expand All @@ -14,7 +15,6 @@ import {
SubscribeRequest,
} from './types';
import {READY, CC_FILE, EMPTY_STRING} from './constants';
import WebCallingService from './services/WebCallingService';
import {AGENT, WEB_RTC_PREFIX} from './services/constants';
import Services from './services';
import HttpRequest from './services/core/HttpRequest';
Expand All @@ -24,19 +24,25 @@ import {getErrorDetails} from './services/core/Utils';
import {Profile, WelcomeEvent} from './services/config/types';
import {AGENT_STATE_AVAILABLE} from './services/config/constants';
import {ConnectionLostDetails} from './services/core/WebSocket/types';
import TaskManager from './services/task/TaskManager';
import WebCallingService from './services/WebCallingService';
import {ITask, TASK_EVENTS} from './services/task/types';

export default class ContactCenter extends WebexPlugin implements IContactCenter {
namespace = 'cc';
private $config: CCPluginConfig;
private $webex: WebexSDK;
private eventEmitter: EventEmitter;
private agentConfig: Profile;
private webCallingService: WebCallingService;
private services: Services;
private httpRequest: HttpRequest;
private taskManager: TaskManager;

constructor(...args) {
super(...args);

this.eventEmitter = new EventEmitter();
// @ts-ignore
this.$webex = this.webex;

Expand All @@ -57,11 +63,26 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
});

this.webCallingService = new WebCallingService(this.$webex, this.$config.callingClientConfig);
this.taskManager = TaskManager.getTaskManager(
this.services.contact,
this.webCallingService,
this.services.webSocketManager
);

this.incomingTaskListener();
LoggerProxy.initialize(this.$webex.logger);
});
}

/**
* An Incoming Call listener.
*/
private incomingTaskListener() {
this.taskManager.on(TASK_EVENTS.TASK_INCOMING, (task: ITask) => {
this.eventEmitter.emit(TASK_EVENTS.TASK_INCOMING, task);
});
}

/**
* This is used for making the CC SDK ready by setting up the cc mercury connection.
*/
Expand Down Expand Up @@ -164,7 +185,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
});

if (data.loginOption === LoginOption.BROWSER) {
await this.webCallingService.registerWebCallingLine();
await this.webCallingService.registerWebCallingLine(data.loginOption);
}

await loginResponse;
Expand Down Expand Up @@ -333,7 +354,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
private async handleDeviceType(deviceType: LoginOption, dn: string): Promise<void> {
switch (deviceType) {
case LoginOption.BROWSER:
await this.webCallingService.registerWebCallingLine();
await this.webCallingService.registerWebCallingLine(deviceType);
break;
case LoginOption.AGENT_DN:
case LoginOption.EXTENSION:
Expand Down
Loading
Loading