UNPKG

@kmhgmbh/dialer-vue-components

Version:

VueJS components for telephony and dialer functions 2

327 lines (242 loc) 13.6 kB
# Dialer Vue Components `@kmhgmbh/dialer-vue-components` — a Vue 3 component library for telephony and dialer functions against the Vocalcom Hermes call-center system. Ships as an npm package (ES/CJS/UMD builds, `vue` externalized), UI built with Vuetify. ## Documentation Additional documentation can be found at [Confluence](https://kmhgmbh.atlassian.net/l/cp/V0fWa8B1). ## Requirements - Node.js 18 (see `.nvmrc`) - Vue 3 (peer dependency) ## Installation ```bash npm install @kmhgmbh/dialer-vue-components ``` Register the plugin with the adapter type (currently only `hermes`) and import the styles: ```ts import { createApp } from 'vue'; import plugin from '@kmhgmbh/dialer-vue-components'; import '@kmhgmbh/dialer-vue-components/style.css'; const app = createApp(App); app.use(plugin, { type: 'hermes' }); ``` Installing the plugin does three things: 1. Globally registers the public components `TopBar`, `CallController` and `CampaignsController`. 2. Creates the telephony singleton and exposes it as `globalProperties.$telephonyAdapter` and via `provide('telephonyAdapter')`. 3. Injects the Hermes vendor scripts as `<script>` tags (in order): `/hermes/agentlink_enums.js`, `/hermes/AgentLink.js`, `/hermes/swfobject.js`, `/hermes/wsjavascript.js`. > **Important:** The consuming app must serve the files from `public/hermes/` under the `/hermes/` path, otherwise the vendor scripts cannot be loaded and the adapter will not work. ## Usage There are two ways to use the library: via the ready-made components (recommended) or directly via the telephony adapter. ### Components #### `<call-controller>` The main UI with all telephony functions (connect, call, hangup, hold, transfer, pause, recording). ```vue <call-controller type="hermes" :config="config" :credentials="{ username: 'agentId', password: 'secret', extension: '11010' }" :campaign="{ id: 'campaignId', description: 'campaignDescription' }" :webrtc="{ wsUrl: 'wss://…/ws', uri: 'sip:11010@…', extension: '11010' }" :is-webrtc="true" :is-snackbar-active="true" :is-logging-enabled="true" @status="onStatus" @error="onError" /> ``` Props: | Prop | Type | Default | Description | | --- | --- | --- | --- | | `type` | `String` | — (required) | Adapter type, currently `'hermes'` | | `config` | `Object` | — (required) | Hermes connection config (see [Configuration](#configuration)) | | `credentials` | `{ username, password, extension }` | — (required) | Login credentials | | `campaign` | `{ id, description }` | — (required) | Manual campaign set on login | | `isSnackbarActive` | `Boolean` | `true` | Toast notifications for errors on/off | | `isLoggingEnabled` | `Boolean` | `true` | Adapter logging on/off | | `isWebrtc` | `Boolean` | `false` | Audio via WebRTC (JsSIP) instead of a physical phone | | `isOutboundUser` | `Boolean` | `false` | Outbound mode (campaign dialing, preview calls) | | `webrtc` | `WebRtcConfig` | `undefined` | WebRTC settings: `{ wsUrl, uri, extension, logging? }` | | `phonenumber` | `String` | `''` | Pre-filled phone number | | `displayPhoneNumber` | `String` | `''` | Number presented to the callee (CLI) | | `showAudioWaveform` | `Boolean` | `true` | Audio waveform visualization during the call | Events: | Event | Payload | Description | | --- | --- | --- | | `status` | `string` | Status changes (`CONNECTED`, `LOGGED_IN`, `SESSION_START`, …) | | `error` | `string` | Error messages (also shown as snackbar if enabled) | | `openCustomer` | `SessionInfo` | On session start in outbound mode — contains session ID, contact number, campaign | | `loadAgentsAndServices` | — | Request from the transfer dialog to load agents/service numbers | #### `<top-bar>` A wrapper around `CallController` for embedding in an app header. Accepts the same props (`type`, `config`, `credentials`, `campaign`, `webrtc`, `isSnackbarActive`, `isWebrtc`, `isOutboundUser`, `isLoggingEnabled`) and forwards the `status` and `error` events. #### `<campaigns-controller>` UI for inbound/outbound campaigns and queues (start/stop, agent counts per queue, pause state). | Prop | Type | Default | Description | | --- | --- | --- | --- | | `showCallback` | `Boolean` | `false` | Show callback campaigns | | `inboundAvailableAgentList` | `Array` | `[]` | Available agents for inbound queue auto-start | | `updatedCampaignNames` | `Array` | `[]` | Overrides for campaign display names | | `autoStartOutboundCampaign` | `String` | `''` | ID of an outbound campaign to start automatically | ### Direct use via the telephony adapter The adapter is a singleton and can be accessed in three ways: ```ts // 1. Composable (recommended) import { useTelephony } from '@kmhgmbh/dialer-vue-components'; const telephonyAdapter = useTelephony('hermes'); // 2. provide/inject const telephonyAdapter = inject('telephonyAdapter'); // 3. globalProperties const telephonyAdapter = getCurrentInstance()?.appContext.config.globalProperties.$telephonyAdapter; ``` Typical flow: ```ts telephonyAdapter.setConfig(config); telephonyAdapter.init(true, false); // (isLoggingEnabled, isOutboundUser) await telephonyAdapter.login('username', 'password', 'extension'); telephonyAdapter.call('+4912345678', '+4998765432'); telephonyAdapter.hangup(); telephonyAdapter.logout(); ``` #### API overview **Setup & login** | Function | Description | | --- | --- | | `setConfig(config)` | Sets the connection configuration and creates the adapter (only once) | | `init(isLoggingEnabled, isOutboundUser)` | Initializes the connection to the phone system | | `login(username, password, extension)` | Logs the agent in (async; all parameters required) | | `logout()` | Logs the agent out and unregisters WebRTC if needed | **Calls** | Function | Description | | --- | --- | | `call(phonenumber, displayPhonenumber)` | Starts a call; `displayPhonenumber` is the presented number | | `hangup()` | Ends the call | | `hold()` / `retrieve()` | Puts the call on hold / retrieves it | | `redial(phonenumber)` | Redial | | `previewCancel()` | Cancels a preview call (outbound) | | `getSessionId()` / `getSession()` | Session ID / session info of the current call | **Transfer** | Function | Description | | --- | --- | | `doInternalBlindTransfer(agentId)` | Blind transfer to another agent | | `doBlindTransfer(number, isExternal?)` | Blind transfer to a number | | `doWarmHandover(number, isExternal?)` | Warm handover (announced transfer) | | `handoverCall()` | Completes the handover | **Campaigns & queues** | Function | Description | | --- | --- | | `getManualCampaigns()` / `setManualCamapaign(campaign)` | Get / set manual campaigns (login required) | | `getCampaigns()` / `getQueues()` | Available campaigns / queues | | `startQueue(context, queue, campaignId)` / `stopQueue(…)` | Start / stop queues | **Pause & agent state** | Function | Description | | --- | --- | | `getPauseOptions()` | Available pause reasons | | `requestPause(pauseCode)` / `stopPause()` | Request / end pause | | `getState()` / `getAgentState()` / `getTelephonyState()` | Current adapter / agent / telephony state | **Recording & call status** | Function | Description | | --- | --- | | `startRecording(fileName)` / `stopRecording()` | Start / stop call recording (login required) | | `canSetCallStatus()` / `setCallStatus(callStatus)` | Set call qualification (status, follow-up, comment) | **WebRTC & audio** | Function | Description | | --- | --- | | `registerWebRTC(config)` / `unregisterWebRTC()` | SIP registration via JsSIP (`{ wsUrl, uri, extension, logging? }`) | | `toogleWebRTC(status)` | Enables/disables WebRTC for outgoing calls | | `getAvailableMediaDevices()` | Available microphones/speakers | | `setSelectedMicrophoneId(id)` / `setSelectedSpeakerId(id)` | Select audio devices | | `getSelectedMicrophoneId()` / `getSelectedSpeakerId()` | Currently selected devices | | `negotiateNewMediaStream()` | Renegotiates the media stream after a device change | | `isMicropheAllowed()` | Checks the microphone permission | **Reactive state** (Vue refs on the adapter): `state`, `isPause`, `pauseName`, `pauseTime`, `agentId`, `agentExtension`, `remoteAudioStream`, `localAudioStream`, `lastContactAgents`, `serviceNumbers`, `availableAgents`. ## Configuration The `config` object for `setConfig()` / the `config` prop: ```ts { AgentProxy: 'hermes-proxy.example.com', Port: 9992, CustomerId: 1, Locale: 'en-US', LocalWebServiceProxy: 'WSProxy.ashx', AdminUrl: 'http://…/hermes_net_v5/admin/', AdminServerUrl: 'http://…/hermes_net_v5/admin/', OnMediaWebService: 'http://…/MailService.asmx?OMS=…', CrmUrl: 'http://…/hermes_net_v5/CRM/', CrmServerUrl: 'http://…/hermes_net_v5/CRM/', apiUrl: 'http://…', } ``` ### Hermes 6.3.x authentication As of Hermes 6.3.x, a separate authentication flow is required before the AgentLink login (verification token → RSA public key → hybrid-encrypted credentials → SignIn cookies → private token). The library handles this automatically via the internal `HermesAuthService` when the config additionally contains one of the following fields: ```ts { // …config as above… hermesBaseUrl: 'https://hermes.example.com', // '' = relative paths (e.g. through a dev proxy) useHermesAuth: true, // alternatively: enable explicitly } ``` Without these fields the adapter falls back to the legacy direct login. The actual `AgentLink.Login` is deferred via polling until the AgentLink connection is ready. ## Events The adapter emits events you can subscribe to with `on()` (and unsubscribe with `off()`): ```ts import { TelephonyEvents } from '@kmhgmbh/dialer-vue-components'; telephonyAdapter.on(TelephonyEvents.ERROR, (message: string) => { setNotification(message); }); ``` | Event | Description | | --- | --- | | `CONNECTED` | Plugin is connected to the phone system | | `DISCONNECTED` | Connection to the phone system lost | | `LOGGED_IN` | Agent logged in | | `LOGGED_OUT` | Agent logged out | | `SESSION_START` | Call established, session created (message: `SessionInfo`) | | `SESSION_END` | Call ended | | `SESSION_STATE` | Session state changed (message: `{ contextType, sessionId, label }`, third argument: `SessionTelephonyStates` code) | | `AGENT_STATE` | Global agent state changed (message: localized state label, third argument: `AgentGlobalStates` code) | | `TELEPHONY_STATE` | Telephony context state changed (message: localized state label) | | `RECORD_START` / `RECORD_STOP` | Call recording started / stopped | | `CALL_TRANSFERRED` | Call was transferred (blind transfer or handover) | | `ERROR` | Error detected | | `CONNECTION_ERROR` | Connection or login problem detected | | `DEBUG` | Diagnostic information (e.g. WebRTC/ICE connection issues) | All event names are exported as the `TelephonyEvents` constant; the numeric agent state codes as `AgentGlobalStates` (`Off`, `Waiting`, `Working`, `Pause`). ## Notifications By default the `CallController` component shows a toast notification for errors. This can be toggled via the `isSnackbarActive` prop. ## Development ### Setup ```bash npm install cp example.env .env # then fill in credentials/endpoints (VITE_* variables) ``` ### Dev harness `npm run dev` starts a local test app (`src/App.vue` + `TestBar.vue`) on port 8081. It is not part of the published library (the lib entry is `src/index.ts`) and reads credentials and Hermes/WebRTC endpoints from `.env`. The Vite dev server proxies `/hermes360` to `http://localhost:8082` and rewrites the Set-Cookie headers (removes Domain/Secure, sets SameSite) so the Hermes auth cookie flow works on localhost — see [vite.config.ts](vite.config.ts). ### Commands ```bash npm run dev # dev harness on port 8081 npm run build # vue-tsc type check + vite lib build npm run lint # eslint npm run lint:fix # eslint with auto-fix npm run type-check # vue-tsc for node/vitest/app tsconfigs npm test # vitest with coverage, single run npm run test:unit # vitest in watch mode npx vitest run tests/Vue.spec.ts # single test file ``` ### Tests Tests live in `tests/`, run on `happy-dom` and use the mount helpers from `tests/test-suite.ts` (`findByTestAttr()` / `findAllByTestAttr()` select elements by `data-test` attribute — use these attributes in components for testability). ### Note on the vendor scripts The files in `public/hermes/` (`AgentLink.js`, `agentlink_enums.js`, …) are untyped vendor globals from Vocalcom. They are excluded from ESLint and test coverage and should not be refactored. ## Project structure - `./docs` — documentation in markdown format (mkdocs) - `./public` — public assets, including the Hermes vendor scripts (`public/hermes/`) - `./src` — source code - `src/index.ts` — library entry (Vue plugin) - `src/components/` — Vue components (button components in `buttons/`) - `src/composables/``telephony` (facade/singleton), `webrtc` (JsSIP), `eventBus`, `timer`, `audioWaveform` - `src/adapters/``AgentLink` adapter around the Hermes vendor API - `src/services/``HermesAuthService` (Hermes 6.3.x auth flow) - `src/constants/` — event and state constants - `./tests` — unit tests (Vitest) ## CI & publishing Bitbucket Pipelines: - **Pull requests:** sandworm audit, `npm run lint`, `npm test` - **Tag `v*`:** build and automatic publish to npmjs.com - **`main` branch:** publish step available as a manual trigger Before releasing, bump the `version` in [package.json](package.json).