voxnix 1.0.11 → 1.0.13

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 CHANGED
@@ -1,14 +1,22 @@
1
1
  # Voxnix
2
2
 
3
- Universal Voice Widget SDK. Works in React, Vue, Angular, plain HTML any stack.
3
+ Universal Voice Widget SDK — SIP-based floating phone widget. Works in React, Vue, Angular, plain HTML, or any stack.
4
+
5
+ **Current version: `1.0.12`**
6
+
7
+ ---
4
8
 
5
9
  ## What it does
6
10
 
7
11
  - Floating phone button (fixed position, configurable placement)
8
12
  - Auto-connects to your SIP/PABX server on init
9
13
  - Shows dialer numpad immediately after registration
10
- - Full call UI: incoming call, outgoing dialing, in-call controls (mute, hold, DTMF)
11
- - Fires callbacks for all call events so your app can react
14
+ - Full call UI: incoming call, outgoing dialing, in-call controls (mute, hold, DTMF keypad)
15
+ - **Agent status bar** Ready / Not Ready / AUX with optional custom AUX codes
16
+ - Fires callbacks for all call and agent-status events so your app can react
17
+ - Keyboard shortcuts: digits to dial, `Enter` to call, `Backspace` to delete, `0–9`/`*`/`#` for DTMF during a call
18
+
19
+ ---
12
20
 
13
21
  ## Installation
14
22
 
@@ -18,28 +26,41 @@ npm install voxnix
18
26
  yarn add voxnix
19
27
  ```
20
28
 
29
+ ---
30
+
21
31
  ## Quick start (any framework)
22
32
 
23
33
  ```js
24
34
  import { OmnixWidget } from 'voxnix';
25
35
 
26
36
  const widget = OmnixWidget.init({
27
- platform: 'sipjs',
28
37
  auth: {
29
- username: '1001',
30
- pwd_pbx: 'secret',
38
+ username: '1001',
39
+ pwd_pbx: 'secret',
31
40
  pabx_host: 'sip.example.com',
32
- port: 8089,
41
+ port: 8089, // optional, default 8089
33
42
  },
34
- placement: 'bottom-left', // 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'
35
43
 
44
+ // Required for agent status API (Ready / AUX / Not Ready)
45
+ 'X-CPAAS-Key-Fingerprint': 'your-cpaas-key-fingerprint-here',
46
+
47
+ placement: 'bottom-left', // 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'
48
+
49
+ // Agent status options (optional)
50
+ autoReady: true, // auto-set agent to Ready after SIP register (default: true)
51
+ showAuxButton: false, // show AUX button in status bar (default: false)
52
+ auxOptions: [], // array of { code, label } — custom AUX codes
53
+
54
+ // Callbacks
36
55
  onRegisterStatus: (status) => {
37
- console.log('SIP status:', status); // 'REGISTERED' | 'FAILED'
56
+ // status: 'REGISTERED' | { status: 'FAILED', reason: string }
57
+ console.log('SIP status:', status);
38
58
  },
39
- onIncomingCall: (data) => console.log('Incoming:', data),
40
- onCallAnswered: (data) => console.log('Answered:', data),
41
- onCallEnded: (data) => console.log('Ended:', data),
42
- onCallUnanswered: () => console.log('Missed call'),
59
+ onAgentStatus: (status) => console.log('Agent:', status), // 'READY' | 'NOT_READY' | 'AUX'
60
+ onIncomingCall: (data) => console.log('Incoming:', data),
61
+ onCallAnswered: (data) => console.log('Answered:', data),
62
+ onCallEnded: (data) => console.log('Ended:', data),
63
+ onCallUnanswered: () => console.log('Missed call'),
43
64
  });
44
65
 
45
66
  // Imperative controls (optional — widget has its own UI)
@@ -54,17 +75,155 @@ widget.dial('1002'); // programmatic dial
54
75
  widget.destroy(); // unmount widget
55
76
  ```
56
77
 
57
- ### Custom container or offset
78
+ ---
79
+
80
+ ## Config reference
81
+
82
+ ### Top-level options
83
+
84
+ | Option | Type | Default | Description |
85
+ |-----------------------------|--------------------------------------------------------------------|-----------------|-----------------------------------------------------------------|
86
+ | `auth` | `object` | — | SIP credentials (see [auth object](#auth-object)) |
87
+ | `pabxConfig` | `{ auth: object }` | — | Alternative config shape — use `pabxConfig.auth` instead of `auth` |
88
+ | `X-CPAAS-Key-Fingerprint` | `string` | — | **Required** for agent status API calls. See [section below](#x-cpaas-key-fingerprint) |
89
+ | `fingerprint` | `string` | — | Alias for `X-CPAAS-Key-Fingerprint` |
90
+ | `cpaasKeyFingerprint` | `string` | — | Alias for `X-CPAAS-Key-Fingerprint` |
91
+ | `placement` | `'bottom-left'` \| `'bottom-right'` \| `'top-left'` \| `'top-right'` | `'bottom-left'` | Fixed position of the floating widget |
92
+ | `offset` | `{ bottom?, top?, left?, right? }` | — | CSS pixel coords — overrides `placement` |
93
+ | `container` | `string` \| `HTMLElement` | — | Mount target. Auto-creates `<div id="omnix-widget-root">` if omitted |
94
+ | `autoReady` | `boolean` | `true` | Auto-call the Ready API right after SIP registration |
95
+ | `showAuxButton` | `boolean` | `false` | Show the AUX menu in the agent status bar |
96
+ | `auxOptions` | `Array<{ code: string, label: string }>` | `[]` | Custom AUX reason codes shown in the status dropdown |
97
+ | `onRegisterStatus` | `(status) => void` | — | `'REGISTERED'` or `{ status: 'FAILED', reason: string }` |
98
+ | `onAgentStatus` | `(status: string) => void` | — | `'READY'` \| `'NOT_READY'` \| `'AUX'` |
99
+ | `onIncomingCall` | `(data) => void` | — | Fired on incoming SIP INVITE |
100
+ | `onCallAnswered` | `(data) => void` | — | Fired when call is established |
101
+ | `onCallEnded` | `(data) => void` | — | Fired on BYE / hangup |
102
+ | `onCallUnanswered` | `() => void` | — | Fired when incoming call is not answered (missed) |
103
+
104
+ ### `auth` object
105
+
106
+ | Field | Type | Description |
107
+ |-------------|----------|------------------------------------------|
108
+ | `username` | `string` | SIP username / extension number |
109
+ | `user_pbx` | `string` | Alias for `username` |
110
+ | `pwd_pbx` | `string` | SIP password |
111
+ | `secret` | `string` | Alias for `pwd_pbx` |
112
+ | `pabx_host` | `string` | SIP domain / WSS host (strips `http(s)://`) |
113
+ | `pbxurl` | `string` | Alias for `pabx_host` |
114
+ | `port` | `number` | WSS port (default `8089`) |
115
+
116
+ ---
117
+
118
+ ## `X-CPAAS-Key-Fingerprint`
119
+
120
+ This key is the **API authentication credential** used when the widget calls the CPaaS extension status endpoint:
121
+
122
+ ```
123
+ POST https://api-cpaas.omnix.co.id/voice/extensions/status
124
+ Header: X-CPAAS-Key-Fingerprint: <your-fingerprint>
125
+ ```
126
+
127
+ The endpoint is hit automatically whenever the agent status changes (Ready / AUX / Not Ready). Without this key, status updates will silently fail and — if `autoReady: true` (default) — the widget will report `FAILED` registration even if the SIP connection itself succeeded.
128
+
129
+ ### Accepted config locations
130
+
131
+ The widget looks for the fingerprint in the following order, using the first match:
132
+
133
+ | Priority | Config key | Example |
134
+ |----------|------------|---------|
135
+ | 1 | `'X-CPAAS-Key-Fingerprint'` (top-level) | `{ 'X-CPAAS-Key-Fingerprint': 'abc123', auth: { ... } }` |
136
+ | 2 | `fingerprint` (top-level) | `{ fingerprint: 'abc123', auth: { ... } }` |
137
+ | 3 | `cpaasKeyFingerprint` (top-level) | `{ cpaasKeyFingerprint: 'abc123', auth: { ... } }` |
138
+ | 4 | `env.fingerprint` | `{ env: { fingerprint: 'abc123' }, auth: { ... } }` |
139
+ | 5 | `env.X_CPAAS_Key_Fingerprint` | `{ env: { X_CPAAS_Key_Fingerprint: 'abc123' }, auth: { ... } }` |
140
+ | 6 | `pabxConfig['X-CPAAS-Key-Fingerprint']` | `{ pabxConfig: { 'X-CPAAS-Key-Fingerprint': 'abc123', auth: { ... } } }` |
141
+
142
+ ### Recommended usage
143
+
144
+ ```js
145
+ // Option A — top-level key (simplest)
146
+ OmnixWidget.init({
147
+ 'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
148
+ auth: { username: '1001', pwd_pbx: 'secret', pabx_host: 'sip.example.com' },
149
+ autoReady: true,
150
+ });
151
+
152
+ // Option B — inside pabxConfig (if your backend returns this shape)
153
+ OmnixWidget.init({
154
+ pabxConfig: {
155
+ 'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
156
+ auth: { username: '1001', pwd_pbx: 'secret', pabx_host: 'sip.example.com' },
157
+ },
158
+ });
159
+
160
+ // Option C — via env object
161
+ OmnixWidget.init({
162
+ env: { fingerprint: 'your-fingerprint-here' },
163
+ auth: { username: '1001', pwd_pbx: 'secret', pabx_host: 'sip.example.com' },
164
+ });
165
+ ```
166
+
167
+ > **Important:** If the fingerprint is missing and `autoReady: true` (default), the widget will emit `{ status: 'FAILED', reason: 'Failed to hit extension ready API' }` via `onRegisterStatus` even though the SIP connection itself was successful. Set `autoReady: false` if you do not have a fingerprint yet.
168
+
169
+ ### Custom container / offset
58
170
 
59
171
  ```js
60
172
  OmnixWidget.init({
61
173
  container: '#my-widget-slot', // CSS selector or DOM element
62
- // or use offset to position precisely (bypasses placement classes)
63
- offset: { bottom: 80, left: 10 },
174
+ // or
175
+ offset: { bottom: 80, left: 10 }, // exact pixel position (bypasses placement)
64
176
  // ... rest of config
65
177
  });
66
178
  ```
67
179
 
180
+ ---
181
+
182
+ ## Instance API
183
+
184
+ `OmnixWidget.init()` returns an instance with these methods:
185
+
186
+ | Method | Description |
187
+ |----------------|------------------------------------------------------------|
188
+ | `answer()` | Answer the current incoming call |
189
+ | `hangup()` | End the active / outgoing call |
190
+ | `reject()` | Reject the current incoming call |
191
+ | `mute(bool)` | `true` = mute microphone, `false` = unmute |
192
+ | `hold(bool)` | `true` = put call on hold, `false` = resume |
193
+ | `dial(number)` | Programmatically dial a number string |
194
+ | `destroy()` | Unmount the widget and clean up the DOM container |
195
+
196
+ ---
197
+
198
+ ## Agent status bar
199
+
200
+ When `autoReady: false` or `showAuxButton: true` a status bar appears at the bottom of the dialer.
201
+
202
+ > **Note:** The agent status bar calls the CPaaS extension status API. You **must** provide `X-CPAAS-Key-Fingerprint` (or one of its aliases) for these calls to succeed.
203
+
204
+ ```js
205
+ OmnixWidget.init({
206
+ auth: { ... },
207
+ 'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
208
+ autoReady: false, // agent starts as NOT_READY; must go Ready manually
209
+ showAuxButton: true, // show AUX dropdown
210
+ auxOptions: [
211
+ { code: 'BRK', label: 'Break' },
212
+ { code: 'LCH', label: 'Lunch' },
213
+ { code: 'TRN', label: 'Training' },
214
+ ],
215
+ onAgentStatus: (status) => console.log('Agent status changed:', status),
216
+ });
217
+ ```
218
+
219
+ | Status | Badge colour | Meaning |
220
+ |-------------|--------------|--------------------------------------|
221
+ | `READY` | 🟢 Green | Agent is available for calls |
222
+ | `NOT_READY` | ⚪ Gray | Agent is unavailable |
223
+ | `AUX` | 🟡 Amber | Agent is on a break / custom reason |
224
+
225
+ ---
226
+
68
227
  ## React integration
69
228
 
70
229
  ```jsx
@@ -76,13 +235,22 @@ function App() {
76
235
 
77
236
  useEffect(() => {
78
237
  widgetRef.current = OmnixWidget.init({
79
- platform: 'sipjs',
80
- auth: { username: '1001', pwd_pbx: 'secret', pabx_host: 'sip.example.com' },
81
- placement: 'bottom-right',
82
- onIncomingCall: (data) => { /* navigate, show notification, etc. */ },
83
- onCallAnswered: (data) => { /* routing logic */ },
84
- onCallEnded: () => { /* cleanup */ },
85
- onRegisterStatus: (s) => { /* update UI */ },
238
+ auth: {
239
+ username: '1001',
240
+ pwd_pbx: 'secret',
241
+ pabx_host: 'sip.example.com',
242
+ },
243
+ 'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
244
+ placement: 'bottom-right',
245
+ autoReady: true,
246
+ showAuxButton: true,
247
+ auxOptions: [{ code: 'BRK', label: 'Break' }],
248
+
249
+ onIncomingCall: (data) => { /* navigate, show notification, etc. */ },
250
+ onCallAnswered: (data) => { /* routing logic */ },
251
+ onCallEnded: () => { /* cleanup */ },
252
+ onRegisterStatus: (s) => { /* update UI */ },
253
+ onAgentStatus: (s) => { /* s: 'READY' | 'NOT_READY' | 'AUX' */ },
86
254
  });
87
255
 
88
256
  return () => widgetRef.current?.destroy();
@@ -92,96 +260,114 @@ function App() {
92
260
  }
93
261
  ```
94
262
 
263
+ ---
264
+
95
265
  ## Vue integration
96
266
 
97
267
  ```js
98
- // composable or mounted()
99
268
  import { OmnixWidget } from 'voxnix';
100
269
 
270
+ // inside setup() or options API
101
271
  onMounted(() => {
102
- const widget = OmnixWidget.init({ ... });
272
+ const widget = OmnixWidget.init({
273
+ auth: { username: '1001', pwd_pbx: 'secret', pabx_host: 'sip.example.com' },
274
+ 'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
275
+ autoReady: false,
276
+ showAuxButton: true,
277
+ auxOptions: [{ code: 'BRK', label: 'Break' }],
278
+ });
103
279
  onUnmounted(() => widget.destroy());
104
280
  });
105
281
  ```
106
282
 
283
+ ---
284
+
107
285
  ## Via CDN (plain HTML, no bundler)
108
286
 
109
287
  ```html
110
- <!-- Include React CDN first -->
111
- <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
112
- <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
113
-
114
- <!-- Or use the self-contained IIFE build (includes React) -->
115
- <script src="./dist/voxnix.iife.js"></script>
288
+ <!-- Self-contained IIFE build (bundles React) -->
289
+ <script src="https://unpkg.com/voxnix/dist/voxnix.iife.js"></script>
116
290
  <script>
117
291
  OmnixWidget.init({
118
- platform: 'sipjs',
119
292
  auth: {
120
- username: '1001',
121
- pwd_pbx: 'secret',
293
+ username: '1001',
294
+ pwd_pbx: 'secret',
122
295
  pabx_host: 'sip.example.com',
123
296
  },
124
- placement: 'bottom-right',
297
+ 'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
298
+ placement: 'bottom-right',
299
+ autoReady: true,
300
+ showAuxButton: true,
301
+ auxOptions: [
302
+ { code: 'BRK', label: 'Break' },
303
+ { code: 'LCH', label: 'Lunch' },
304
+ ],
125
305
  onRegisterStatus: (s) => console.log('SIP:', s),
126
306
  onIncomingCall: (d) => console.log('Incoming:', d),
307
+ onAgentStatus: (s) => console.log('Agent:', s),
127
308
  });
128
309
  </script>
129
310
  ```
130
311
 
131
- ## Config reference
312
+ > If you need to use your own React instance, include `react@18` and `react-dom@18` UMD builds **before** the script tag and use `dist/voxnix.js` (ESM) or `dist/voxnix.umd.cjs` (CJS) instead.
132
313
 
133
- | Option | Type | Default | Description |
134
- |---------------------|------------------------------------------------|-----------------|--------------------------------------------------|
135
- | `platform` | `string` | `'sipjs'` | Voice platform. Currently: `'sipjs'` |
136
- | `auth` | `object` | — | SIP credentials (see below) |
137
- | `placement` | `'bottom-left'│'bottom-right'│'top-left'│'top-right'` | `'bottom-left'` | Fixed position of the floating widget |
138
- | `offset` | `{ bottom?, top?, left?, right? }` | `undefined` | CSS pixel coords — overrides `placement` |
139
- | `container` | `string │ HTMLElement` | `undefined` | Mount target. Auto-creates `div` if omitted |
140
- | `onIncomingCall` | `(data) => void` | — | Fired on incoming SIP INVITE |
141
- | `onCallAnswered` | `(data) => void` | — | Fired when call is established |
142
- | `onCallEnded` | `(data) => void` | — | Fired on BYE / hangup |
143
- | `onCallUnanswered` | `() => void` | — | Fired when incoming call is not answered |
144
- | `onRegisterStatus` | `(status: string) => void` | — | `'REGISTERED'` or `'FAILED'` |
314
+ ---
145
315
 
146
- ### `auth` object
316
+ ## Keyboard shortcuts
147
317
 
148
- | Field | Description |
149
- |---------------|---------------------------------|
150
- | `username` | SIP username / extension |
151
- | `pwd_pbx` | SIP password |
152
- | `pabx_host` | SIP domain / WSS host |
153
- | `port` | WSS port (default `8089`) |
318
+ These shortcuts are active whenever the dialer widget is open:
154
319
 
155
- ## Instance API
320
+ | Key | State | Action |
321
+ |------------------|---------|-------------------------------|
322
+ | `0–9`, `*`, `#` | Idle | Append digit to dial input |
323
+ | `Backspace` | Idle | Delete last digit |
324
+ | `Enter` | Idle | Dial the current number |
325
+ | `0–9`, `*`, `#` | In-call | Send DTMF tone |
156
326
 
157
- `OmnixWidget.init()` returns an instance with these methods:
158
-
159
- | Method | Description |
160
- |-----------------|------------------------------------|
161
- | `answer()` | Answer incoming call |
162
- | `hangup()` | End active call |
163
- | `reject()` | Reject incoming call |
164
- | `mute(bool)` | Mute / unmute microphone |
165
- | `hold(bool)` | Hold / resume call |
166
- | `dial(number)` | Programmatically dial a number |
167
- | `destroy()` | Unmount widget and clean up |
327
+ ---
168
328
 
169
329
  ## Build
170
330
 
171
331
  ```bash
172
- # ESM + UMD (peer React, for npm consumers)
332
+ # ESM + UMD (peer React for npm consumers)
173
333
  yarn build:lib
174
334
 
175
- # Self-contained IIFE (bundles React, for CDN/script tag)
335
+ # Self-contained IIFE (bundles React for CDN / script tag)
176
336
  yarn build:iife
177
337
 
178
- # Both
338
+ # Both outputs
179
339
  yarn build
180
340
  ```
181
341
 
342
+ Output files in `dist/`:
343
+
344
+ | File | Format | React | Use case |
345
+ |--------------------|--------|----------|-----------------------|
346
+ | `voxnix.js` | ESM | peer dep | `import` / bundlers |
347
+ | `voxnix.umd.cjs` | UMD | peer dep | CommonJS / `require` |
348
+ | `voxnix.iife.js` | IIFE | bundled | `<script>` / CDN |
349
+
350
+ ---
351
+
182
352
  ## Development
183
353
 
184
354
  ```bash
185
355
  yarn install
186
- yarn dev
356
+ yarn dev # Vite dev server with hot reload
357
+ yarn watch # Vite library watch mode
187
358
  ```
359
+
360
+ ---
361
+
362
+ ## Changelog
363
+
364
+ ### v1.0.12
365
+ - Rollback DevToast / console interceptor feature
366
+
367
+ ### v1.0.11 and earlier
368
+ - Agent status bar (Ready / Not Ready / AUX) with configurable `auxOptions`
369
+ - `onAgentStatus` callback
370
+ - `autoReady` and `showAuxButton` config options
371
+ - Keyboard shortcut support for dialer and DTMF
372
+ - React 17 / 18 compatibility layer
373
+ - Dual-SIP auth shape support (`auth` and `pabxConfig.auth`)