call-control-sdk 5.2.6 → 5.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1077 -116
- package/dist/index.d.mts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,218 +1,1179 @@
|
|
|
1
|
-
|
|
1
|
+
# Call Control SDK - Comprehensive Documentation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Table of Contents
|
|
4
|
+
1. [Overview](#overview)
|
|
5
|
+
2. [Architecture](#architecture)
|
|
6
|
+
3. [Installation & Setup](#installation--setup)
|
|
7
|
+
4. [Core Components](#core-components)
|
|
8
|
+
5. [Hooks & Utilities](#hooks--utilities)
|
|
9
|
+
6. [Services & APIs](#services--apis)
|
|
10
|
+
7. [TypeScript Types](#typescript-types)
|
|
11
|
+
8. [Usage Examples](#usage-examples)
|
|
12
|
+
9. [Best Practices](#best-practices)
|
|
13
|
+
10. [API Reference](#api-reference)
|
|
14
|
+
11. [Troubleshooting](#troubleshooting)
|
|
15
|
+
12. [Productivity Features](#productivity-features)
|
|
4
16
|
|
|
5
|
-
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
The **Call Control SDK** is a comprehensive TypeScript-based library designed for WebRTC call management in contact center environments. It provides a draggable call control panel with real-time call management capabilities, agent productivity tools, and seamless integration with existing applications.
|
|
6
22
|
|
|
23
|
+
### Key Features
|
|
7
24
|
- 🎯 **Complete Call Control** – Hold, Mute, Status management, and End Call
|
|
8
25
|
- 🖱️ **Draggable Interface** – Movable panel with persisted position
|
|
9
|
-
- 💾 **State Persistence** – Saves control states in
|
|
26
|
+
- 💾 **State Persistence** – Saves control states in localStorage
|
|
10
27
|
- ⏱️ **Live Call Timer** – Real-time call duration tracking
|
|
11
28
|
- 🎨 **Material-UI Design** – Responsive and accessible UI
|
|
12
29
|
- 📱 **Touch Support** – Works seamlessly on desktop and mobile
|
|
13
30
|
- 🔧 **TypeScript Support** – Full type safety and IntelliSense
|
|
14
31
|
- 🎪 **Singleton State** – Consistent shared state across components
|
|
32
|
+
- 📊 **Event Tracking** – Comprehensive analytics and monitoring
|
|
33
|
+
- 🔄 **WebSocket Integration** – Real-time communication
|
|
34
|
+
- 📞 **Conference Calling** – Multi-line conference management
|
|
35
|
+
- 🔀 **Call Transfer** – Transfer to agents, processes, or queues
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Architecture
|
|
40
|
+
|
|
41
|
+
### Core Architecture Components
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
45
|
+
│ Call Control SDK │
|
|
46
|
+
├─────────────────────────────────────────────────────────────┤
|
|
47
|
+
│ Components Layer │
|
|
48
|
+
│ ├── CallControlPanel (Main UI Component) │
|
|
49
|
+
│ ├── CallControls (Control Buttons & Logic) │
|
|
50
|
+
│ ├── Dialog Components (Transfer, Conference, etc.) │
|
|
51
|
+
│ └── SDKProvider (Context Provider) │
|
|
52
|
+
├─────────────────────────────────────────────────────────────┤
|
|
53
|
+
│ Hooks Layer │
|
|
54
|
+
│ ├── useSDKState (State Management) │
|
|
55
|
+
│ ├── useClickToCall (Call Initiation) │
|
|
56
|
+
│ ├── useEndCall (Call Termination) │
|
|
57
|
+
│ ├── useLogout (Agent Logout) │
|
|
58
|
+
│ ├── useDraggable (Drag & Drop) │
|
|
59
|
+
│ └── eventsTracker (Analytics) │
|
|
60
|
+
├─────────────────────────────────────────────────────────────┤
|
|
61
|
+
│ Services Layer │
|
|
62
|
+
│ ├── axios.ts (HTTP Client) │
|
|
63
|
+
│ ├── request.ts (API Hooks) │
|
|
64
|
+
│ ├── endPoint.ts (API Endpoints) │
|
|
65
|
+
│ └── websocketClient.ts (Real-time Communication) │
|
|
66
|
+
├─────────────────────────────────────────────────────────────┤
|
|
67
|
+
│ State Management │
|
|
68
|
+
│ ├── sdk-state.ts (Singleton State Manager) │
|
|
69
|
+
│ └── types.ts (TypeScript Definitions) │
|
|
70
|
+
└─────────────────────────────────────────────────────────────┘
|
|
71
|
+
```
|
|
15
72
|
|
|
16
|
-
|
|
73
|
+
### State Management Flow
|
|
17
74
|
|
|
18
|
-
|
|
75
|
+
```mermaid
|
|
76
|
+
graph TD
|
|
77
|
+
A[SDK Initialization] --> B[State Manager]
|
|
78
|
+
B --> C[localStorage Persistence]
|
|
79
|
+
B --> D[Component Updates]
|
|
80
|
+
D --> E[UI Rendering]
|
|
81
|
+
E --> F[User Interactions]
|
|
82
|
+
F --> G[API Calls]
|
|
83
|
+
G --> H[State Updates]
|
|
84
|
+
H --> B
|
|
85
|
+
|
|
86
|
+
I[WebSocket Events] --> J[Real-time Updates]
|
|
87
|
+
J --> B
|
|
88
|
+
|
|
89
|
+
K[Event Tracking] --> L[Analytics]
|
|
90
|
+
L --> M[Performance Monitoring]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Installation & Setup
|
|
96
|
+
|
|
97
|
+
### Prerequisites
|
|
98
|
+
- Node.js 16+
|
|
99
|
+
- React 16+
|
|
100
|
+
- TypeScript 5+
|
|
101
|
+
|
|
102
|
+
### Installation
|
|
19
103
|
|
|
20
104
|
```bash
|
|
21
105
|
npm install call-control-sdk
|
|
22
106
|
```
|
|
23
107
|
|
|
24
|
-
|
|
108
|
+
### Peer Dependencies
|
|
25
109
|
|
|
26
110
|
```bash
|
|
27
111
|
npm install react react-dom axios @mui/material @mui/icons-material @emotion/react @emotion/styled
|
|
28
112
|
```
|
|
29
113
|
|
|
30
|
-
|
|
114
|
+
### Basic Setup
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import React, { useEffect } from 'react';
|
|
118
|
+
import { initSDK, CallControlPanel } from 'call-control-sdk';
|
|
119
|
+
|
|
120
|
+
function App() {
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
// Initialize the SDK
|
|
123
|
+
initSDK({
|
|
124
|
+
apiKey: "your-api-key",
|
|
125
|
+
tenantId: "your-tenant-id",
|
|
126
|
+
agentId: "your-agent-id"
|
|
127
|
+
});
|
|
128
|
+
}, []);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<div className="app">
|
|
132
|
+
<h1>Agent Dashboard</h1>
|
|
133
|
+
<CallControlPanel
|
|
134
|
+
onDataChange={(data) => {
|
|
135
|
+
console.log('Call data updated:', data);
|
|
136
|
+
}}
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export default App;
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Core Components
|
|
148
|
+
|
|
149
|
+
### 1. CallControlPanel
|
|
150
|
+
|
|
151
|
+
The main UI component that renders the draggable call control interface.
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
interface CallControlPanelProps {
|
|
155
|
+
onDataChange?: (data: CallData) => void;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Usage
|
|
159
|
+
<CallControlPanel
|
|
160
|
+
onDataChange={(callData) => {
|
|
161
|
+
// Handle call data changes
|
|
162
|
+
updateDashboard(callData);
|
|
163
|
+
}}
|
|
164
|
+
/>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Features:**
|
|
168
|
+
- Draggable positioning with persistence
|
|
169
|
+
- Real-time call status display
|
|
170
|
+
- Agent status management
|
|
171
|
+
- Call duration timer
|
|
172
|
+
- Control buttons (Hold, Mute, Transfer, Conference, End Call)
|
|
173
|
+
|
|
174
|
+
### 2. CallControls
|
|
175
|
+
|
|
176
|
+
The core control component containing all call management buttons and logic.
|
|
177
|
+
|
|
178
|
+
**Key Controls:**
|
|
179
|
+
- **Agent Ready**: Set agent status to ready
|
|
180
|
+
- **Hold/Resume**: Toggle call hold state
|
|
181
|
+
- **Mute/Unmute**: Toggle microphone state
|
|
182
|
+
- **Transfer**: Transfer call to another agent/queue/process
|
|
183
|
+
- **Conference**: Add participants to conference call
|
|
184
|
+
- **End Call**: Terminate call with disposition
|
|
185
|
+
|
|
186
|
+
### 3. Dialog Components
|
|
187
|
+
|
|
188
|
+
#### ConferenceDialog
|
|
189
|
+
Manages multi-line conference calls with up to 5 lines.
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
// Conference line management
|
|
193
|
+
interface ConferenceLineTypes {
|
|
194
|
+
line: number;
|
|
195
|
+
status: string;
|
|
196
|
+
type: "external" | "internal" | "";
|
|
197
|
+
phone: string;
|
|
198
|
+
isMute: boolean;
|
|
199
|
+
isHold: boolean;
|
|
200
|
+
isCallStart: boolean;
|
|
201
|
+
isMergeCall: boolean;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### CallTransferDialog
|
|
206
|
+
Handles call transfers to:
|
|
207
|
+
- **Agents**: Transfer to specific agents
|
|
208
|
+
- **Processes**: Transfer to business processes
|
|
209
|
+
- **Queues**: Transfer to call queues
|
|
210
|
+
|
|
211
|
+
#### EndCallDispositionDialog
|
|
212
|
+
Captures call disposition and follow-up information:
|
|
213
|
+
- Disposition codes (Resolved, Not Interested)
|
|
214
|
+
- Follow-up requirements
|
|
215
|
+
- Callback scheduling
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Hooks & Utilities
|
|
220
|
+
|
|
221
|
+
### 1. useSDKState
|
|
222
|
+
|
|
223
|
+
Provides reactive access to the SDK's global state.
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
import { useSDKState } from 'call-control-sdk';
|
|
227
|
+
|
|
228
|
+
function MyComponent() {
|
|
229
|
+
const state = useSDKState();
|
|
230
|
+
|
|
231
|
+
return (
|
|
232
|
+
<div>
|
|
233
|
+
<p>Agent Status: {state.status}</p>
|
|
234
|
+
<p>Call Duration: {state.callStartTime}</p>
|
|
235
|
+
<p>Is Muted: {state.isMuted ? 'Yes' : 'No'}</p>
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 2. useClickToCall
|
|
242
|
+
|
|
243
|
+
Initiates outbound calls to phone numbers.
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
import { useClickToCall } from 'call-control-sdk';
|
|
247
|
+
|
|
248
|
+
function DialerComponent() {
|
|
249
|
+
const { handleStartCall, isLoading, isSuccess, error } = useClickToCall();
|
|
250
|
+
|
|
251
|
+
const makeCall = () => {
|
|
252
|
+
handleStartCall({ mobileNumber: "1234567890" });
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
return (
|
|
256
|
+
<button onClick={makeCall} disabled={isLoading}>
|
|
257
|
+
{isLoading ? 'Calling...' : 'Call'}
|
|
258
|
+
</button>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### 3. useEndCall
|
|
264
|
+
|
|
265
|
+
Terminates active calls with disposition tracking.
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
import { useEndCall } from 'call-control-sdk';
|
|
269
|
+
|
|
270
|
+
function EndCallComponent() {
|
|
271
|
+
const { handleEndCall, isLoading } = useEndCall();
|
|
272
|
+
|
|
273
|
+
const endCall = () => {
|
|
274
|
+
handleEndCall({
|
|
275
|
+
disposition: "RES",
|
|
276
|
+
followUp: "N",
|
|
277
|
+
callbackDate: "",
|
|
278
|
+
callbackHrs: "",
|
|
279
|
+
callbackMins: ""
|
|
280
|
+
});
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
return (
|
|
284
|
+
<button onClick={endCall} disabled={isLoading}>
|
|
285
|
+
End Call
|
|
286
|
+
</button>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### 4. useLogout
|
|
292
|
+
|
|
293
|
+
Handles agent logout functionality.
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
import { useLogout } from 'call-control-sdk';
|
|
297
|
+
|
|
298
|
+
function LogoutComponent() {
|
|
299
|
+
const { logout, isLoading } = useLogout();
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<button onClick={logout} disabled={isLoading}>
|
|
303
|
+
{isLoading ? 'Logging out...' : 'Logout'}
|
|
304
|
+
</button>
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 5. useDraggable
|
|
310
|
+
|
|
311
|
+
Provides drag-and-drop functionality for UI elements.
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
import { useDraggable } from 'call-control-sdk';
|
|
315
|
+
|
|
316
|
+
function DraggablePanel() {
|
|
317
|
+
const { position, isDragging, dragRef, handleMouseDown, handleTouchStart } =
|
|
318
|
+
useDraggable({ x: 100, y: 100 }, (newPosition) => {
|
|
319
|
+
console.log('Moved to:', newPosition);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
return (
|
|
323
|
+
<div
|
|
324
|
+
ref={dragRef}
|
|
325
|
+
onMouseDown={handleMouseDown}
|
|
326
|
+
onTouchStart={handleTouchStart}
|
|
327
|
+
style={{
|
|
328
|
+
position: 'fixed',
|
|
329
|
+
left: position.x,
|
|
330
|
+
top: position.y,
|
|
331
|
+
cursor: isDragging ? 'grabbing' : 'grab'
|
|
332
|
+
}}
|
|
333
|
+
>
|
|
334
|
+
Drag me
|
|
335
|
+
</div>
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Services & APIs
|
|
343
|
+
|
|
344
|
+
### 1. HTTP Client (axios.ts)
|
|
345
|
+
|
|
346
|
+
Configured Axios instance with:
|
|
347
|
+
- Base URL configuration
|
|
348
|
+
- Authorization headers
|
|
349
|
+
- Request/response interceptors
|
|
350
|
+
- Error handling
|
|
351
|
+
- Retry logic for 401 errors
|
|
352
|
+
|
|
353
|
+
```tsx
|
|
354
|
+
// Automatic token injection
|
|
355
|
+
axiosInstance.interceptors.request.use((config) => {
|
|
356
|
+
const token = getAuthToken();
|
|
357
|
+
if (token && config.headers) {
|
|
358
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
359
|
+
}
|
|
360
|
+
return config;
|
|
361
|
+
});
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 2. API Request Hooks (request.ts)
|
|
365
|
+
|
|
366
|
+
Custom hooks for different HTTP methods:
|
|
367
|
+
|
|
368
|
+
#### useGetRequest
|
|
369
|
+
```tsx
|
|
370
|
+
const [getData, { isLoading, isSuccess, error, data }] = useGetRequest<User>({
|
|
371
|
+
onSuccess: (response) => console.log('Success:', response),
|
|
372
|
+
onError: (error) => console.error('Error:', error)
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Usage
|
|
376
|
+
getData('/api/users');
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
#### usePostRequest
|
|
380
|
+
```tsx
|
|
381
|
+
const [createUser, { isLoading, isSuccess, error }] = usePostRequest<User>({
|
|
382
|
+
onSuccess: (response) => console.log('User created:', response)
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Usage
|
|
386
|
+
createUser('/api/users', userData);
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### 3. API Endpoints (endPoint.ts)
|
|
390
|
+
|
|
391
|
+
Centralized endpoint configuration:
|
|
392
|
+
|
|
393
|
+
```tsx
|
|
394
|
+
export const END_POINT = {
|
|
395
|
+
LOGIN: `${BASE_URL}/api/v1/cti/login?provider=convox`,
|
|
396
|
+
READY_AGENT: `${BASE_URL}/api/v1/cti/ready-agent?provider=convox`,
|
|
397
|
+
CLICK_TO_CALL: `${BASE_URL}/api/v1/cti/calls?provider=convox`,
|
|
398
|
+
HOLD_CALL: `${BASE_URL}/api/v1/cti/calls/hold?provider=convox`,
|
|
399
|
+
MUTE_CALL: `${BASE_URL}/api/v1/cti/calls/mute?provider=convox`,
|
|
400
|
+
END_CALL: `${BASE_URL}/api/v1/cti/calls/end?provider=convox`,
|
|
401
|
+
CONFERENCE_CALL: `${BASE_URL}/api/v1/cti/calls/conference?provider=convox`,
|
|
402
|
+
TRANSFER_CALL: `${BASE_URL}/api/v1/cti/calls/transfer?provider=convox`,
|
|
403
|
+
// ... more endpoints
|
|
404
|
+
};
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### 4. WebSocket Integration
|
|
408
|
+
|
|
409
|
+
Real-time communication for call events:
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
// WebSocket connection setup
|
|
413
|
+
useEffect(() => {
|
|
414
|
+
if (state.agentId) {
|
|
415
|
+
const ws = new WebSocket(`${WS_END_POINT.WS}?agent_id=${state.agentId}`);
|
|
416
|
+
|
|
417
|
+
ws.onmessage = (event) => {
|
|
418
|
+
const data = JSON.parse(event.data);
|
|
419
|
+
sdkStateManager.updateCallData(data);
|
|
420
|
+
|
|
421
|
+
if (data.status === "ONCALL") {
|
|
422
|
+
sdkStateManager.startCall();
|
|
423
|
+
}
|
|
424
|
+
if (data.status === "WRAPUP") {
|
|
425
|
+
sdkStateManager.endCall();
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
return () => ws.close();
|
|
430
|
+
}
|
|
431
|
+
}, [state.agentId]);
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## TypeScript Types
|
|
437
|
+
|
|
438
|
+
### Core Types
|
|
439
|
+
|
|
440
|
+
```tsx
|
|
441
|
+
// Call data structure
|
|
442
|
+
interface CallData {
|
|
443
|
+
mobileNumber?: string;
|
|
444
|
+
callReferenceId?: string;
|
|
445
|
+
convoxId?: string;
|
|
446
|
+
type?: string;
|
|
447
|
+
status?: string;
|
|
448
|
+
agent_id?: string;
|
|
449
|
+
phone_number?: string;
|
|
450
|
+
event_time?: string;
|
|
451
|
+
convox_id?: string;
|
|
452
|
+
process_id?: string;
|
|
453
|
+
process_name?: string;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Agent status types
|
|
457
|
+
type CallStatus =
|
|
458
|
+
| "idle"
|
|
459
|
+
| "ready"
|
|
460
|
+
| "break"
|
|
461
|
+
| "on call"
|
|
462
|
+
| "wrap up"
|
|
463
|
+
| "dial"
|
|
464
|
+
| "hold"
|
|
465
|
+
| "mute";
|
|
466
|
+
|
|
467
|
+
// Conference line configuration
|
|
468
|
+
type ConferenceLineTypes = {
|
|
469
|
+
line: number;
|
|
470
|
+
status: string;
|
|
471
|
+
type: "external" | "internal" | "";
|
|
472
|
+
phone: string;
|
|
473
|
+
isMute: boolean;
|
|
474
|
+
isHold: boolean;
|
|
475
|
+
isCallStart: boolean;
|
|
476
|
+
isMergeCall: boolean;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// SDK state interface
|
|
480
|
+
interface SDKState {
|
|
481
|
+
apiKey: string | null;
|
|
482
|
+
process?: { process_id: number; process_name: string } | null;
|
|
483
|
+
agentId: string;
|
|
484
|
+
isInitialized: boolean;
|
|
485
|
+
isHolding: boolean;
|
|
486
|
+
isMuted: boolean;
|
|
487
|
+
status: CallStatus;
|
|
488
|
+
convox_id?: string;
|
|
489
|
+
process_id?: number;
|
|
490
|
+
callStartTime: number | null;
|
|
491
|
+
position?: { x: number; y: number };
|
|
492
|
+
controlPanelPosition: { x: number; y: number };
|
|
493
|
+
iframePosition: { x: number; y: number };
|
|
494
|
+
callData: CallData;
|
|
495
|
+
conferenceLine: ConferenceLineTypes[];
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### API Request Types
|
|
500
|
+
|
|
501
|
+
```tsx
|
|
502
|
+
// Request result interface
|
|
503
|
+
interface RequestResult<T> {
|
|
504
|
+
isLoading: boolean;
|
|
505
|
+
isSuccess: boolean;
|
|
506
|
+
isError: boolean;
|
|
507
|
+
error: AxiosError | null;
|
|
508
|
+
data: AxiosResponse<T> | null;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Request options
|
|
512
|
+
interface RequestOptions {
|
|
513
|
+
onSuccess?: (response: AxiosResponse, request: any) => void;
|
|
514
|
+
onError?: (error: any, request: any) => void;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Hook signatures
|
|
518
|
+
type UseGetRequest<T = UseApiRequest> = [
|
|
519
|
+
(url: string, config?: AxiosRequestConfig) => void,
|
|
520
|
+
RequestResult<T>
|
|
521
|
+
];
|
|
522
|
+
|
|
523
|
+
type UsePostRequest<T = UseApiRequest> = [
|
|
524
|
+
(url: string, payload: T, config?: AxiosRequestConfig) => void,
|
|
525
|
+
RequestResult<T>
|
|
526
|
+
];
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## Usage Examples
|
|
532
|
+
|
|
533
|
+
### 1. Complete Agent Dashboard
|
|
534
|
+
|
|
535
|
+
```tsx
|
|
536
|
+
import React, { useEffect, useState } from 'react';
|
|
537
|
+
import {
|
|
538
|
+
initSDK,
|
|
539
|
+
CallControlPanel,
|
|
540
|
+
useClickToCall,
|
|
541
|
+
useEndCall,
|
|
542
|
+
useLogout
|
|
543
|
+
} from 'call-control-sdk';
|
|
544
|
+
|
|
545
|
+
function AgentDashboard() {
|
|
546
|
+
const [callData, setCallData] = useState(null);
|
|
547
|
+
|
|
548
|
+
// Initialize SDK
|
|
549
|
+
useEffect(() => {
|
|
550
|
+
initSDK({
|
|
551
|
+
apiKey: process.env.REACT_APP_API_KEY,
|
|
552
|
+
tenantId: process.env.REACT_APP_TENANT_ID,
|
|
553
|
+
agentId: process.env.REACT_APP_AGENT_ID
|
|
554
|
+
});
|
|
555
|
+
}, []);
|
|
31
556
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
6. [Browser Support](#-browser-support)
|
|
43
|
-
7. [License](#-license)
|
|
557
|
+
// Call management hooks
|
|
558
|
+
const { handleStartCall, isLoading: calling } = useClickToCall();
|
|
559
|
+
const { handleEndCall, isLoading: ending } = useEndCall();
|
|
560
|
+
const { logout, isLoading: loggingOut } = useLogout();
|
|
561
|
+
|
|
562
|
+
const handleCallDataChange = (data) => {
|
|
563
|
+
setCallData(data);
|
|
564
|
+
// Update dashboard metrics
|
|
565
|
+
updateDashboardMetrics(data);
|
|
566
|
+
};
|
|
44
567
|
|
|
45
|
-
|
|
568
|
+
return (
|
|
569
|
+
<div className="agent-dashboard">
|
|
570
|
+
<header className="dashboard-header">
|
|
571
|
+
<h1>Agent Dashboard</h1>
|
|
572
|
+
<button onClick={logout} disabled={loggingOut}>
|
|
573
|
+
{loggingOut ? 'Logging out...' : 'Logout'}
|
|
574
|
+
</button>
|
|
575
|
+
</header>
|
|
576
|
+
|
|
577
|
+
<main className="dashboard-content">
|
|
578
|
+
<div className="call-controls">
|
|
579
|
+
<CallControlPanel onDataChange={handleCallDataChange} />
|
|
580
|
+
</div>
|
|
581
|
+
|
|
582
|
+
<div className="dashboard-widgets">
|
|
583
|
+
<CallMetricsWidget data={callData} />
|
|
584
|
+
<CustomerInfoWidget data={callData} />
|
|
585
|
+
<CallHistoryWidget />
|
|
586
|
+
</div>
|
|
587
|
+
</main>
|
|
588
|
+
</div>
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
```
|
|
46
592
|
|
|
47
|
-
###
|
|
593
|
+
### 2. Custom Call Controls
|
|
48
594
|
|
|
49
595
|
```tsx
|
|
50
|
-
import
|
|
51
|
-
import {
|
|
596
|
+
import React from 'react';
|
|
597
|
+
import { useSDKState, useClickToCall, useEndCall } from 'call-control-sdk';
|
|
598
|
+
|
|
599
|
+
function CustomCallControls() {
|
|
600
|
+
const state = useSDKState();
|
|
601
|
+
const { handleStartCall } = useClickToCall();
|
|
602
|
+
const { handleEndCall } = useEndCall();
|
|
603
|
+
|
|
604
|
+
const isOnCall = state.callData?.status === "ONCALL";
|
|
605
|
+
const callDuration = state.callStartTime
|
|
606
|
+
? Math.floor((Date.now() - state.callStartTime) / 1000)
|
|
607
|
+
: 0;
|
|
608
|
+
|
|
609
|
+
const formatDuration = (seconds) => {
|
|
610
|
+
const mins = Math.floor(seconds / 60);
|
|
611
|
+
const secs = seconds % 60;
|
|
612
|
+
return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
return (
|
|
616
|
+
<div className="custom-controls">
|
|
617
|
+
<div className="call-status">
|
|
618
|
+
<span className="status">{state.status}</span>
|
|
619
|
+
{isOnCall && (
|
|
620
|
+
<span className="duration">{formatDuration(callDuration)}</span>
|
|
621
|
+
)}
|
|
622
|
+
</div>
|
|
623
|
+
|
|
624
|
+
<div className="control-buttons">
|
|
625
|
+
{!isOnCall && (
|
|
626
|
+
<button onClick={() => handleStartCall({ mobileNumber: "1234567890" })}>
|
|
627
|
+
Start Call
|
|
628
|
+
</button>
|
|
629
|
+
)}
|
|
630
|
+
|
|
631
|
+
{isOnCall && (
|
|
632
|
+
<>
|
|
633
|
+
<button
|
|
634
|
+
onClick={() => handleEndCall({ disposition: "RES" })}
|
|
635
|
+
className="end-call"
|
|
636
|
+
>
|
|
637
|
+
End Call
|
|
638
|
+
</button>
|
|
639
|
+
</>
|
|
640
|
+
)}
|
|
641
|
+
</div>
|
|
642
|
+
</div>
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### 3. Conference Call Management
|
|
648
|
+
|
|
649
|
+
```tsx
|
|
650
|
+
import React, { useState } from 'react';
|
|
651
|
+
import { useSDKState } from 'call-control-sdk';
|
|
652
|
+
|
|
653
|
+
function ConferenceManager() {
|
|
654
|
+
const state = useSDKState();
|
|
655
|
+
const [showConference, setShowConference] = useState(false);
|
|
656
|
+
|
|
657
|
+
const addParticipant = (phoneNumber) => {
|
|
658
|
+
// Find available conference line
|
|
659
|
+
const availableLine = state.conferenceLine.find(
|
|
660
|
+
line => line.line !== 1 && line.status === "IDLE"
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
if (availableLine) {
|
|
664
|
+
// Initiate conference call
|
|
665
|
+
handleConferenceCall(availableLine.line, phoneNumber);
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
return (
|
|
670
|
+
<div className="conference-manager">
|
|
671
|
+
<button onClick={() => setShowConference(true)}>
|
|
672
|
+
Manage Conference
|
|
673
|
+
</button>
|
|
674
|
+
|
|
675
|
+
{showConference && (
|
|
676
|
+
<div className="conference-dialog">
|
|
677
|
+
<h3>Conference Lines</h3>
|
|
678
|
+
{state.conferenceLine.map(line => (
|
|
679
|
+
<div key={line.line} className="conference-line">
|
|
680
|
+
<span>Line {line.line}: {line.status}</span>
|
|
681
|
+
{line.line !== 1 && (
|
|
682
|
+
<input
|
|
683
|
+
placeholder="Phone number"
|
|
684
|
+
onBlur={(e) => addParticipant(e.target.value)}
|
|
685
|
+
/>
|
|
686
|
+
)}
|
|
687
|
+
</div>
|
|
688
|
+
))}
|
|
689
|
+
</div>
|
|
690
|
+
)}
|
|
691
|
+
</div>
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
```
|
|
52
695
|
|
|
53
|
-
|
|
696
|
+
---
|
|
697
|
+
|
|
698
|
+
## Best Practices
|
|
699
|
+
|
|
700
|
+
### 1. Initialization
|
|
701
|
+
|
|
702
|
+
```tsx
|
|
703
|
+
// ✅ Good: Initialize SDK early in app lifecycle
|
|
54
704
|
function App() {
|
|
55
705
|
useEffect(() => {
|
|
56
|
-
// Initialize the SDK.
|
|
57
706
|
try {
|
|
58
707
|
initSDK({
|
|
59
|
-
apiKey:
|
|
60
|
-
tenantId:
|
|
61
|
-
agentId:
|
|
708
|
+
apiKey: process.env.REACT_APP_API_KEY,
|
|
709
|
+
tenantId: process.env.REACT_APP_TENANT_ID,
|
|
710
|
+
agentId: process.env.REACT_APP_AGENT_ID
|
|
62
711
|
});
|
|
63
|
-
console.log("SDK initialized successfully");
|
|
64
712
|
} catch (error) {
|
|
65
|
-
console.error(
|
|
713
|
+
console.error('SDK initialization failed:', error);
|
|
714
|
+
// Handle initialization error
|
|
66
715
|
}
|
|
67
716
|
}, []);
|
|
68
717
|
|
|
69
|
-
return <
|
|
718
|
+
return <YourApp />;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// ❌ Bad: Initializing in component that might unmount
|
|
722
|
+
function SomeComponent() {
|
|
723
|
+
useEffect(() => {
|
|
724
|
+
initSDK(config); // This might be called multiple times
|
|
725
|
+
}, []);
|
|
70
726
|
}
|
|
71
727
|
```
|
|
72
728
|
|
|
73
|
-
### 2.
|
|
729
|
+
### 2. State Management
|
|
74
730
|
|
|
75
731
|
```tsx
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
console.log("Call data updated:", data);
|
|
81
|
-
};
|
|
82
|
-
|
|
732
|
+
// ✅ Good: Use the provided state hook
|
|
733
|
+
function MyComponent() {
|
|
734
|
+
const state = useSDKState();
|
|
735
|
+
|
|
83
736
|
return (
|
|
84
737
|
<div>
|
|
85
|
-
<
|
|
86
|
-
<
|
|
738
|
+
<p>Status: {state.status}</p>
|
|
739
|
+
<p>Call Duration: {state.callStartTime}</p>
|
|
87
740
|
</div>
|
|
88
741
|
);
|
|
89
742
|
}
|
|
743
|
+
|
|
744
|
+
// ❌ Bad: Direct state access
|
|
745
|
+
function MyComponent() {
|
|
746
|
+
const state = sdkStateManager.getState(); // Won't trigger re-renders
|
|
747
|
+
return <div>{state.status}</div>;
|
|
748
|
+
}
|
|
90
749
|
```
|
|
91
750
|
|
|
92
|
-
### 3.
|
|
751
|
+
### 3. Error Handling
|
|
93
752
|
|
|
94
753
|
```tsx
|
|
95
|
-
|
|
754
|
+
// ✅ Good: Comprehensive error handling
|
|
755
|
+
function CallComponent() {
|
|
756
|
+
const { handleStartCall, isLoading, isError, error } = useClickToCall();
|
|
757
|
+
|
|
758
|
+
const makeCall = async () => {
|
|
759
|
+
try {
|
|
760
|
+
await handleStartCall({ mobileNumber: "1234567890" });
|
|
761
|
+
} catch (error) {
|
|
762
|
+
console.error('Call failed:', error);
|
|
763
|
+
// Show user-friendly error message
|
|
764
|
+
showNotification('Call failed. Please try again.');
|
|
765
|
+
}
|
|
766
|
+
};
|
|
96
767
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
768
|
+
return (
|
|
769
|
+
<div>
|
|
770
|
+
<button onClick={makeCall} disabled={isLoading}>
|
|
771
|
+
{isLoading ? 'Calling...' : 'Call'}
|
|
772
|
+
</button>
|
|
773
|
+
{isError && <p className="error">Error: {error?.message}</p>}
|
|
774
|
+
</div>
|
|
775
|
+
);
|
|
100
776
|
}
|
|
101
777
|
```
|
|
102
778
|
|
|
103
|
-
### 4.
|
|
779
|
+
### 4. Performance Optimization
|
|
104
780
|
|
|
105
781
|
```tsx
|
|
106
|
-
|
|
782
|
+
// ✅ Good: Memoize expensive operations
|
|
783
|
+
function CallMetrics({ callData }) {
|
|
784
|
+
const formattedDuration = useMemo(() => {
|
|
785
|
+
if (!callData?.startTime) return '00:00';
|
|
786
|
+
const duration = Date.now() - callData.startTime;
|
|
787
|
+
return formatDuration(duration);
|
|
788
|
+
}, [callData?.startTime]);
|
|
107
789
|
|
|
108
|
-
|
|
109
|
-
|
|
790
|
+
return <div>Duration: {formattedDuration}</div>;
|
|
791
|
+
}
|
|
110
792
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
793
|
+
// ✅ Good: Debounce user input
|
|
794
|
+
function PhoneInput() {
|
|
795
|
+
const [phoneNumber, setPhoneNumber] = useState('');
|
|
796
|
+
const debouncedPhone = useDebounce(phoneNumber, 500);
|
|
797
|
+
|
|
798
|
+
useEffect(() => {
|
|
799
|
+
if (debouncedPhone.length === 10) {
|
|
800
|
+
validatePhoneNumber(debouncedPhone);
|
|
801
|
+
}
|
|
802
|
+
}, [debouncedPhone]);
|
|
803
|
+
}
|
|
804
|
+
```
|
|
116
805
|
|
|
806
|
+
### 5. Accessibility
|
|
807
|
+
|
|
808
|
+
```tsx
|
|
809
|
+
// ✅ Good: Accessible controls
|
|
810
|
+
function AccessibleCallControls() {
|
|
811
|
+
const state = useSDKState();
|
|
812
|
+
|
|
117
813
|
return (
|
|
118
|
-
<div>
|
|
119
|
-
<button
|
|
120
|
-
{
|
|
814
|
+
<div role="toolbar" aria-label="Call controls">
|
|
815
|
+
<button
|
|
816
|
+
onClick={handleMute}
|
|
817
|
+
aria-pressed={state.isMuted}
|
|
818
|
+
aria-label={state.isMuted ? 'Unmute microphone' : 'Mute microphone'}
|
|
819
|
+
>
|
|
820
|
+
{state.isMuted ? <MicOffIcon /> : <MicIcon />}
|
|
821
|
+
</button>
|
|
822
|
+
|
|
823
|
+
<button
|
|
824
|
+
onClick={handleHold}
|
|
825
|
+
aria-pressed={state.isHolding}
|
|
826
|
+
aria-label={state.isHolding ? 'Resume call' : 'Hold call'}
|
|
827
|
+
>
|
|
828
|
+
{state.isHolding ? <PlayIcon /> : <PauseIcon />}
|
|
121
829
|
</button>
|
|
122
|
-
{isSuccess && <p>✅ Call ended successfully</p>}
|
|
123
|
-
{isError && <p>❌ Error: {error?.message}</p>}
|
|
124
830
|
</div>
|
|
125
831
|
);
|
|
126
832
|
}
|
|
127
833
|
```
|
|
128
834
|
|
|
129
|
-
|
|
835
|
+
---
|
|
836
|
+
|
|
837
|
+
## API Reference
|
|
838
|
+
|
|
839
|
+
### Core Functions
|
|
840
|
+
|
|
841
|
+
#### `initSDK(config: InitSDKParams): void`
|
|
842
|
+
|
|
843
|
+
Initializes the Call Control SDK.
|
|
844
|
+
|
|
845
|
+
**Parameters:**
|
|
846
|
+
- `config.apiKey` (string, required): API key for authentication
|
|
847
|
+
- `config.tenantId` (string, required): Tenant ID for events/authentication
|
|
848
|
+
- `config.agentId` (string, required): Agent ID for call controls
|
|
849
|
+
|
|
850
|
+
**Example:**
|
|
851
|
+
```tsx
|
|
852
|
+
initSDK({
|
|
853
|
+
apiKey: "your-api-key",
|
|
854
|
+
tenantId: "tenant-123",
|
|
855
|
+
agentId: "agent-456"
|
|
856
|
+
});
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
#### `CallControlPanel`
|
|
860
|
+
|
|
861
|
+
React component that renders the draggable call control interface.
|
|
862
|
+
|
|
863
|
+
**Props:**
|
|
864
|
+
- `onDataChange?: (data: CallData) => void`: Callback fired when call data changes
|
|
865
|
+
|
|
866
|
+
**Example:**
|
|
867
|
+
```tsx
|
|
868
|
+
<CallControlPanel
|
|
869
|
+
onDataChange={(data) => {
|
|
870
|
+
console.log('Call updated:', data);
|
|
871
|
+
}}
|
|
872
|
+
/>
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
### Hooks
|
|
130
876
|
|
|
131
|
-
|
|
877
|
+
#### `useSDKState(): SDKState`
|
|
132
878
|
|
|
133
|
-
|
|
879
|
+
Returns the current SDK state with reactive updates.
|
|
134
880
|
|
|
135
|
-
**
|
|
881
|
+
**Returns:** `SDKState` object containing:
|
|
882
|
+
- `isInitialized`: boolean
|
|
883
|
+
- `status`: CallStatus
|
|
884
|
+
- `isHolding`: boolean
|
|
885
|
+
- `isMuted`: boolean
|
|
886
|
+
- `callData`: CallData
|
|
887
|
+
- `conferenceLine`: ConferenceLineTypes[]
|
|
888
|
+
- And more...
|
|
136
889
|
|
|
137
|
-
|
|
138
|
-
| ---------- | ------ | -------- | ----------------------------------- |
|
|
139
|
-
| `apiKey` | string | ✅ | API key for authentication |
|
|
140
|
-
| `tenantId` | string | ✅ | Tenant ID for events/authentication |
|
|
141
|
-
| `agentId` | string | ✅ | Agent ID for call controls |
|
|
890
|
+
#### `useClickToCall()`
|
|
142
891
|
|
|
143
|
-
|
|
892
|
+
Hook for initiating outbound calls.
|
|
144
893
|
|
|
145
|
-
|
|
894
|
+
**Returns:**
|
|
895
|
+
- `handleStartCall`: (payload: StartCallPayload) => Promise<void>
|
|
896
|
+
- `isLoading`: boolean
|
|
897
|
+
- `isSuccess`: boolean
|
|
898
|
+
- `isError`: boolean
|
|
899
|
+
- `error`: any
|
|
900
|
+
- `data`: any
|
|
146
901
|
|
|
147
|
-
|
|
902
|
+
#### `useEndCall()`
|
|
148
903
|
|
|
149
|
-
|
|
150
|
-
| --------------- | -------- | -------- | -------------------------------------------------------------------------------------------------- |
|
|
151
|
-
| `onDataChange?` | function | ❌ | Callback fired when call data changes. Receives `{ mobileNumber, callReferenceId, agentLoginId }`. |
|
|
904
|
+
Hook for ending active calls.
|
|
152
905
|
|
|
153
|
-
|
|
906
|
+
**Returns:**
|
|
907
|
+
- `handleEndCall`: (data: EndCallData) => Promise<void>
|
|
908
|
+
- `isLoading`: boolean
|
|
909
|
+
- `isSuccess`: boolean
|
|
910
|
+
- `isError`: boolean
|
|
911
|
+
- `error`: any
|
|
912
|
+
- `data`: any
|
|
154
913
|
|
|
155
|
-
|
|
914
|
+
#### `useLogout()`
|
|
156
915
|
|
|
157
|
-
|
|
916
|
+
Hook for agent logout functionality.
|
|
158
917
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
918
|
+
**Returns:**
|
|
919
|
+
- `logout`: () => Promise<void>
|
|
920
|
+
- `isLoading`: boolean
|
|
921
|
+
- `isSuccess`: boolean
|
|
922
|
+
- `isError`: boolean
|
|
923
|
+
- `error`: any
|
|
924
|
+
- `data`: any
|
|
925
|
+
|
|
926
|
+
---
|
|
164
927
|
|
|
165
|
-
|
|
928
|
+
## Troubleshooting
|
|
166
929
|
|
|
167
|
-
###
|
|
930
|
+
### Common Issues
|
|
168
931
|
|
|
169
|
-
|
|
932
|
+
#### 1. SDK Not Initializing
|
|
170
933
|
|
|
934
|
+
**Problem:** SDK fails to initialize
|
|
935
|
+
**Solution:**
|
|
171
936
|
```tsx
|
|
172
|
-
|
|
173
|
-
|
|
937
|
+
// Check if all required parameters are provided
|
|
938
|
+
try {
|
|
939
|
+
initSDK({
|
|
940
|
+
apiKey: "valid-api-key", // Ensure this is not empty
|
|
941
|
+
tenantId: "valid-tenant-id", // Ensure this is not empty
|
|
942
|
+
agentId: "valid-agent-id" // Ensure this is not empty
|
|
943
|
+
});
|
|
944
|
+
} catch (error) {
|
|
945
|
+
console.error('SDK initialization failed:', error);
|
|
946
|
+
}
|
|
174
947
|
```
|
|
175
948
|
|
|
176
|
-
|
|
949
|
+
#### 2. State Not Updating
|
|
177
950
|
|
|
178
|
-
|
|
951
|
+
**Problem:** Components not re-rendering when state changes
|
|
952
|
+
**Solution:**
|
|
953
|
+
```tsx
|
|
954
|
+
// ✅ Use the hook, not direct state access
|
|
955
|
+
function MyComponent() {
|
|
956
|
+
const state = useSDKState(); // This will trigger re-renders
|
|
957
|
+
|
|
958
|
+
// ❌ Don't do this
|
|
959
|
+
// const state = sdkStateManager.getState();
|
|
960
|
+
}
|
|
961
|
+
```
|
|
179
962
|
|
|
180
|
-
|
|
963
|
+
#### 3. WebSocket Connection Issues
|
|
181
964
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
965
|
+
**Problem:** Real-time updates not working
|
|
966
|
+
**Solution:**
|
|
967
|
+
```tsx
|
|
968
|
+
// Check WebSocket connection
|
|
969
|
+
useEffect(() => {
|
|
970
|
+
if (state.agentId) {
|
|
971
|
+
const ws = new WebSocket(`${WS_END_POINT.WS}?agent_id=${state.agentId}`);
|
|
972
|
+
|
|
973
|
+
ws.onopen = () => console.log('WebSocket connected');
|
|
974
|
+
ws.onerror = (error) => console.error('WebSocket error:', error);
|
|
975
|
+
ws.onclose = () => console.log('WebSocket disconnected');
|
|
976
|
+
|
|
977
|
+
return () => ws.close();
|
|
978
|
+
}
|
|
979
|
+
}, [state.agentId]);
|
|
980
|
+
```
|
|
190
981
|
|
|
191
|
-
|
|
982
|
+
#### 4. Call Controls Not Working
|
|
192
983
|
|
|
193
|
-
|
|
984
|
+
**Problem:** Buttons not responding or API calls failing
|
|
985
|
+
**Solution:**
|
|
986
|
+
```tsx
|
|
987
|
+
// Check agent status and call state
|
|
988
|
+
function CallControls() {
|
|
989
|
+
const state = useSDKState();
|
|
990
|
+
|
|
991
|
+
// Ensure agent is ready before allowing calls
|
|
992
|
+
const canMakeCall = state.status === "idle" || state.status === "ready";
|
|
993
|
+
|
|
994
|
+
return (
|
|
995
|
+
<button
|
|
996
|
+
onClick={handleCall}
|
|
997
|
+
disabled={!canMakeCall || isLoading}
|
|
998
|
+
>
|
|
999
|
+
Call
|
|
1000
|
+
</button>
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
### Debug Mode
|
|
1006
|
+
|
|
1007
|
+
Enable debug logging:
|
|
194
1008
|
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
|
|
1009
|
+
```tsx
|
|
1010
|
+
// Add to your app initialization
|
|
1011
|
+
if (process.env.NODE_ENV === 'development') {
|
|
1012
|
+
// Enable SDK debug mode
|
|
1013
|
+
sdkStateManager.debugStorage();
|
|
1014
|
+
|
|
1015
|
+
// Log all state changes
|
|
1016
|
+
sdkStateManager.subscribe(() => {
|
|
1017
|
+
console.log('SDK State changed:', sdkStateManager.getState());
|
|
1018
|
+
});
|
|
198
1019
|
}
|
|
199
1020
|
```
|
|
200
1021
|
|
|
201
|
-
|
|
1022
|
+
---
|
|
202
1023
|
|
|
203
|
-
|
|
204
|
-
- **Call Controls**: Hold / Resume, Mute / Unmute, End Call, Agent Status
|
|
205
|
-
- **Persistent State**: Hold, mute, agent status, panel position, call timer
|
|
1024
|
+
## Productivity Features
|
|
206
1025
|
|
|
207
|
-
|
|
1026
|
+
### 1. Agent Productivity Metrics
|
|
208
1027
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
1028
|
+
The SDK automatically tracks various productivity metrics:
|
|
1029
|
+
|
|
1030
|
+
```tsx
|
|
1031
|
+
// Event tracking for productivity analysis
|
|
1032
|
+
eventTracker.logEvent('callStarted', {
|
|
1033
|
+
agentId: state.agentId,
|
|
1034
|
+
phoneNumber: state.callData.phone_number,
|
|
1035
|
+
timestamp: new Date().toISOString(),
|
|
1036
|
+
processId: state.process?.process_id
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
eventTracker.logEvent('callEnded', {
|
|
1040
|
+
agentId: state.agentId,
|
|
1041
|
+
duration: callDuration,
|
|
1042
|
+
disposition: disposition,
|
|
1043
|
+
timestamp: new Date().toISOString()
|
|
1044
|
+
});
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
### 2. Real-time Status Updates
|
|
1048
|
+
|
|
1049
|
+
```tsx
|
|
1050
|
+
// Automatic status synchronization
|
|
1051
|
+
useEffect(() => {
|
|
1052
|
+
const ws = new WebSocket(`${WS_END_POINT.WS}?agent_id=${state.agentId}`);
|
|
1053
|
+
|
|
1054
|
+
ws.onmessage = (event) => {
|
|
1055
|
+
const data = JSON.parse(event.data);
|
|
1056
|
+
|
|
1057
|
+
// Update call data
|
|
1058
|
+
sdkStateManager.updateCallData(data);
|
|
1059
|
+
|
|
1060
|
+
// Track status changes
|
|
1061
|
+
eventTracker.logEvent('statusChanged', {
|
|
1062
|
+
from: state.status,
|
|
1063
|
+
to: data.status,
|
|
1064
|
+
timestamp: new Date().toISOString()
|
|
1065
|
+
});
|
|
1066
|
+
};
|
|
1067
|
+
}, [state.agentId]);
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
### 3. Call Quality Monitoring
|
|
1071
|
+
|
|
1072
|
+
```tsx
|
|
1073
|
+
// Monitor call quality metrics
|
|
1074
|
+
function CallQualityMonitor() {
|
|
1075
|
+
const state = useSDKState();
|
|
1076
|
+
|
|
1077
|
+
useEffect(() => {
|
|
1078
|
+
if (state.callData?.status === "ONCALL") {
|
|
1079
|
+
const startTime = Date.now();
|
|
1080
|
+
|
|
1081
|
+
const interval = setInterval(() => {
|
|
1082
|
+
const duration = Date.now() - startTime;
|
|
1083
|
+
|
|
1084
|
+
// Log call quality metrics every 30 seconds
|
|
1085
|
+
if (duration % 30000 === 0) {
|
|
1086
|
+
eventTracker.logEvent('callQualityMetrics', {
|
|
1087
|
+
duration: duration,
|
|
1088
|
+
isHolding: state.isHolding,
|
|
1089
|
+
isMuted: state.isMuted,
|
|
1090
|
+
timestamp: new Date().toISOString()
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
}, 1000);
|
|
1094
|
+
|
|
1095
|
+
return () => clearInterval(interval);
|
|
1096
|
+
}
|
|
1097
|
+
}, [state.callData?.status, state.isHolding, state.isMuted]);
|
|
1098
|
+
}
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
### 4. Performance Analytics
|
|
1102
|
+
|
|
1103
|
+
```tsx
|
|
1104
|
+
// Track UI performance
|
|
1105
|
+
function PerformanceTracker() {
|
|
1106
|
+
useEffect(() => {
|
|
1107
|
+
// Track component render times
|
|
1108
|
+
const startTime = performance.now();
|
|
1109
|
+
|
|
1110
|
+
return () => {
|
|
1111
|
+
const renderTime = performance.now() - startTime;
|
|
1112
|
+
eventTracker.logEvent('componentRender', {
|
|
1113
|
+
component: 'CallControlPanel',
|
|
1114
|
+
renderTime: renderTime,
|
|
1115
|
+
timestamp: new Date().toISOString()
|
|
1116
|
+
});
|
|
1117
|
+
};
|
|
1118
|
+
}, []);
|
|
1119
|
+
}
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
### 5. User Experience Optimization
|
|
1123
|
+
|
|
1124
|
+
```tsx
|
|
1125
|
+
// Optimize user interactions
|
|
1126
|
+
function OptimizedCallControls() {
|
|
1127
|
+
const state = useSDKState();
|
|
1128
|
+
|
|
1129
|
+
// Debounce rapid button clicks
|
|
1130
|
+
const debouncedHold = useDebounce(() => {
|
|
1131
|
+
handleHoldToggle();
|
|
1132
|
+
}, 300);
|
|
1133
|
+
|
|
1134
|
+
// Track user interaction patterns
|
|
1135
|
+
const trackInteraction = (action) => {
|
|
1136
|
+
eventTracker.logEvent('userInteraction', {
|
|
1137
|
+
action: action,
|
|
1138
|
+
agentId: state.agentId,
|
|
1139
|
+
timestamp: new Date().toISOString()
|
|
1140
|
+
});
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
return (
|
|
1144
|
+
<div>
|
|
1145
|
+
<button
|
|
1146
|
+
onClick={() => {
|
|
1147
|
+
trackInteraction('holdToggle');
|
|
1148
|
+
debouncedHold();
|
|
1149
|
+
}}
|
|
1150
|
+
>
|
|
1151
|
+
Hold
|
|
1152
|
+
</button>
|
|
1153
|
+
</div>
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
```
|
|
213
1157
|
|
|
214
1158
|
---
|
|
215
1159
|
|
|
216
|
-
|
|
1160
|
+
## Conclusion
|
|
1161
|
+
|
|
1162
|
+
The Call Control SDK provides a comprehensive solution for WebRTC call management in contact center environments. With its draggable interface, real-time state management, extensive TypeScript support, and productivity features, it enables developers to build robust call center applications quickly and efficiently.
|
|
1163
|
+
|
|
1164
|
+
Key benefits:
|
|
1165
|
+
- **Rapid Development**: Pre-built components and hooks accelerate development
|
|
1166
|
+
- **Type Safety**: Full TypeScript support prevents runtime errors
|
|
1167
|
+
- **Real-time Updates**: WebSocket integration ensures live data synchronization
|
|
1168
|
+
- **Productivity Focus**: Built-in analytics and monitoring capabilities
|
|
1169
|
+
- **Accessibility**: Material-UI components with proper ARIA support
|
|
1170
|
+
- **Mobile Ready**: Touch support and responsive design
|
|
1171
|
+
- **Extensible**: Modular architecture allows for easy customization
|
|
1172
|
+
|
|
1173
|
+
For additional support or feature requests, please refer to the official documentation or contact the development team.
|
|
1174
|
+
|
|
1175
|
+
---
|
|
217
1176
|
|
|
218
|
-
|
|
1177
|
+
*Documentation Version: 1.0*
|
|
1178
|
+
*Last Updated: 2025*
|
|
1179
|
+
*SDK Version: 5.3.0*
|