diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 60488d5ec3..a5c82e627b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,8 +1,8 @@ { "name": "Copilot Debugging Env", "remoteEnv": { - "SITE_USER": "${localEnv:AS_DEV_FLEX_AUTOMATION_USER}", - "SITE_PASS": "${localEnv:AS_DEV_FLEX_AUTOMATION_PASSWORD}", - "SITE_ACCOUNT_SID": "${localEnv:AS_DEV_FLEX_AUTOMATION_SID}" + "SITE_USER": "${localEnv:SITE_USER}", + "SITE_PASS": "${localEnv:SITE_PASS}", + "SITE_ACCOUNT_SID": "${localEnv:SITE_ACCOUNT_SID}" } } \ No newline at end of file diff --git a/.github/instructions/plugin-hrm-form.instructions.md b/.github/instructions/plugin-hrm-form.instructions.md index 3c96db4b43..e95b85f0ba 100644 --- a/.github/instructions/plugin-hrm-form.instructions.md +++ b/.github/instructions/plugin-hrm-form.instructions.md @@ -10,4 +10,4 @@ Always run the following checks and fix any issues they raise prior to requestin - Ensure the code changes are correctly linted by running eslint using the configuration in the plugin-hrm-form directory When debugging the flex website, you can use the version deployed to Aselo Development. To login run the oktaSsoLoginViaApi function under in e2e-tests/okta/ssoLogin.ts -You should pass in the parameters from variables set in the chrome devtools MCP environment, SITE_PASS for password, SITE_USER for username, SITE_ACCOUNT_SID for accountSid and 'https://flex.twilio.com/' as the homeUrl \ No newline at end of file +You should pass in the parameters from variables set in the chrome devtools MCP environment, SITE_PASS for password, SITE_USER for username, SITE_ACCOUNT_SID for accountSid and 'https://flex.twilio.com/wine-lyrebird-1400' as the homeUrl \ No newline at end of file diff --git a/plugin-hrm-form/package-lock.json b/plugin-hrm-form/package-lock.json index 028c35f0cb..ec945ab330 100644 --- a/plugin-hrm-form/package-lock.json +++ b/plugin-hrm-form/package-lock.json @@ -62,7 +62,7 @@ "@testing-library/user-event": "^14.1.1", "@twilio/flex-plugin": "^7.1.0", "@twilio/flex-plugin-scripts": "^7.1.0", - "@twilio/flex-ui": "2.13.3", + "@twilio/flex-ui": "2.16.0", "@twilio/flex-ui-dev-proxy": "^1.1.2", "@types/jest": "^27.5.2", "@types/react": "^17.0.47", @@ -225,9 +225,9 @@ } }, "node_modules/@apollo/client": { - "version": "3.9.11", - "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.9.11.tgz", - "integrity": "sha512-H7e9m7cRcFO93tokwzqrsbnfKorkpV24xU30hFH5u2g6B+c1DMo/ouyF/YrBPdrTzqxQCjTUmds/FLmJ7626GA==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.14.0.tgz", + "integrity": "sha512-0YQKKRIxiMlIou+SekQqdCo0ZTHxOcES+K8vKB53cIDpwABNR0P0yRzPgsbgcj3zRJniD93S/ontsnZsCLZrxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -239,8 +239,7 @@ "hoist-non-react-statics": "^3.3.2", "optimism": "^0.18.0", "prop-types": "^15.7.2", - "rehackt": "0.0.6", - "response-iterator": "^0.2.6", + "rehackt": "^0.1.0", "symbol-observable": "^4.0.0", "ts-invariant": "^0.10.3", "tslib": "^2.3.0", @@ -248,9 +247,9 @@ }, "peerDependencies": { "graphql": "^15.0.0 || ^16.0.0", - "graphql-ws": "^5.5.5", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "graphql-ws": "^5.5.5 || ^6.0.3", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc", "subscriptions-transport-ws": "^0.9.0 || ^0.11.0" }, "peerDependenciesMeta": { @@ -3125,6 +3124,42 @@ "postcss": "^8.4" } }, + "node_modules/@datadog/browser-core": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/@datadog/browser-core/-/browser-core-6.30.1.tgz", + "integrity": "sha512-udXaUdjFld/UkdaH69oVWzDYILRqTdSXobVGnmcsqPvC5VBqdhunl0cR+t1k0oJgJndabqCFY/zluJ+joTOdaA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@datadog/browser-rum": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/@datadog/browser-rum/-/browser-rum-6.30.1.tgz", + "integrity": "sha512-XwY+BQZ1Zh+zFHIX9E14O8w0asuAaI/K15qFvX7psdVfeTf27WCzlJpzhaYJC+qXn3Pys0uR5ioPWgRcliAFjw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@datadog/browser-core": "6.30.1", + "@datadog/browser-rum-core": "6.30.1" + }, + "peerDependencies": { + "@datadog/browser-logs": "6.30.1" + }, + "peerDependenciesMeta": { + "@datadog/browser-logs": { + "optional": true + } + } + }, + "node_modules/@datadog/browser-rum-core": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/@datadog/browser-rum-core/-/browser-rum-core-6.30.1.tgz", + "integrity": "sha512-U7TxRz5MuvLACfuskP4gGZk3ghSniEzkrhDNJeUXdA9+TZZLyrsZYBT9n6NuZTL1qTM5NNeK89e49kLUVI3Tiw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@datadog/browser-core": "6.30.1" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "dev": true, @@ -6474,7 +6509,9 @@ } }, "node_modules/@opentelemetry/api": { - "version": "1.4.1", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -13754,16 +13791,17 @@ "license": "MIT" }, "node_modules/@twilio/flex-sdk": { - "version": "0.99.40-dev", - "resolved": "https://registry.npmjs.org/@twilio/flex-sdk/-/flex-sdk-0.99.40-dev.tgz", - "integrity": "sha512-q9h/RR3dDCaYKB7q4YZRQxGGMBfOrwtudaPg0RlrjQc6HRrAh88nYAi7IPCjEaT4CBlmUQceENYIfFTFN+VxgQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@twilio/flex-sdk/-/flex-sdk-3.0.0.tgz", + "integrity": "sha512-5qfFLIH7m9DwK6c66GQO0L65K9PrLccNM85CYBSwbiZWzfNWpC5qU14VPhXKY0gsv53FoppnAO6RCoDj2V9IjA==", "dev": true, - "license": "Apache-2.0", + "license": "https://www.twilio.com/en-us/legal/tos", "dependencies": { - "@apollo/client": "^3.9.6", + "@apollo/client": "^3.13.8", + "@datadog/browser-rum": "^6.22.0", "@segment/analytics-node": "1.3.0", "@twilio/conversations": "2.6.0", - "@twilio/voice-sdk": "^2.11.0", + "@twilio/voice-sdk": "^2.15.0", "buffer": "^6.0.3", "core-js": "^3.40.0", "crypto-js": "^4.2.0", @@ -13773,10 +13811,7 @@ "lodash": "^4.17.21", "loglevel": "^1.7.0", "query-string": "^6.2.0", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.8.1", - "twilio-taskrouter": "^2.1.0", - "twilsock": "^0.8.3", + "twilio-taskrouter": "^2.1.1", "ws": "^8.18.0" }, "engines": { @@ -13785,8 +13820,8 @@ }, "peerDependencies": { "graphql": "^16.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "graphql": { @@ -13830,50 +13865,28 @@ "node": ">=14" } }, - "node_modules/@twilio/flex-sdk/node_modules/@twilio/conversations/node_modules/twilsock": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/twilsock/-/twilsock-0.12.2.tgz", - "integrity": "sha512-7G59f2TCEnxcY2ZBCzaZOPmMDoxDrK9lMTiA7UvuiKca37Dljbdlu2EHI3+d7gU1JHkH5GNCmyxqJzSbZodwXA==", + "node_modules/@twilio/flex-sdk/node_modules/@twilio/voice-errors": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@twilio/voice-errors/-/voice-errors-1.7.0.tgz", + "integrity": "sha512-9TvniWpzU0iy6SYFAcDP+HG+/mNz2yAHSs7+m0DZk86lE+LoTB6J/ZONTPuxXrXWi4tso/DulSHuA0w7nIQtGg==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.14.5", - "@twilio/declarative-type-validator": "^0.1.11", - "@twilio/operation-retrier": "^4.0.7", - "core-js": "^3.17.3", - "iso8601-duration": "=1.2.0", - "javascript-state-machine": "^3.1.0", - "loglevel": "^1.6.3", - "platform": "^1.3.6", - "uuid": "^3.4.0", - "ws": "^5.2.3" - }, - "engines": { - "node": ">=14" - } + "license": "BSD-3-Clause" }, - "node_modules/@twilio/flex-sdk/node_modules/@twilio/conversations/node_modules/twilsock/node_modules/@twilio/declarative-type-validator": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/@twilio/declarative-type-validator/-/declarative-type-validator-0.1.11.tgz", - "integrity": "sha512-yRAMLPD8j3k67UFvPeZvfTlKYuceiNq+iZ8a/ADzAbZMeaV0FMvsJmG97MH8yN/VdXY9hcscchsnc99bJ1sClw==", + "node_modules/@twilio/flex-sdk/node_modules/@twilio/voice-sdk": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/@twilio/voice-sdk/-/voice-sdk-2.18.0.tgz", + "integrity": "sha512-7SGkTn3cr+V0Cj0cd8kW300154mtrDNvfCXNT3v7Ip+7nv51vt7igTeJX7Sc83tMXfsGEkHTxjQy6lMCbZQ2eQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/runtime": "^7.14.5", - "core-js": "^3.17.3" + "@twilio/voice-errors": "1.7.0", + "@types/events": "^3.0.3", + "events": "3.3.0", + "loglevel": "1.9.2", + "tslib": "2.8.1" }, "engines": { - "node": ">=14" - } - }, - "node_modules/@twilio/flex-sdk/node_modules/@twilio/conversations/node_modules/ws": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.4.tgz", - "integrity": "sha512-fFCejsuC8f9kOSu9FYaOw8CdO68O3h5v0lg4p74o8JqWpwTf9tniOD+nOB78aWoVSS6WptVUmDrp/KPsMVBWFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" + "node": ">= 12" } }, "node_modules/@twilio/flex-sdk/node_modules/loglevel": { @@ -13890,16 +13903,6 @@ "url": "https://tidelift.com/funding/github/npm/loglevel" } }, - "node_modules/@twilio/flex-sdk/node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/@twilio/flex-sdk/node_modules/twilio-sync": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/twilio-sync/-/twilio-sync-3.1.0.tgz", @@ -13935,7 +13938,7 @@ "node": ">=14" } }, - "node_modules/@twilio/flex-sdk/node_modules/twilio-sync/node_modules/twilsock": { + "node_modules/@twilio/flex-sdk/node_modules/twilsock": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/twilsock/-/twilsock-0.12.2.tgz", "integrity": "sha512-7G59f2TCEnxcY2ZBCzaZOPmMDoxDrK9lMTiA7UvuiKca37Dljbdlu2EHI3+d7gU1JHkH5GNCmyxqJzSbZodwXA==", @@ -13957,32 +13960,18 @@ "node": ">=14" } }, - "node_modules/@twilio/flex-sdk/node_modules/twilio-sync/node_modules/ws": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.4.tgz", - "integrity": "sha512-fFCejsuC8f9kOSu9FYaOw8CdO68O3h5v0lg4p74o8JqWpwTf9tniOD+nOB78aWoVSS6WptVUmDrp/KPsMVBWFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/@twilio/flex-sdk/node_modules/twilsock": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/twilsock/-/twilsock-0.8.3.tgz", - "integrity": "sha512-jS7sFoecgofYiRAYvinL3cYlwqOXkgtdb81q24H8mD2DNbnTwNQ6GQNURJ5GxjTYo/iSsgQjYVUs9lJaXU1uaA==", + "node_modules/@twilio/flex-sdk/node_modules/twilsock/node_modules/@twilio/declarative-type-validator": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@twilio/declarative-type-validator/-/declarative-type-validator-0.1.11.tgz", + "integrity": "sha512-yRAMLPD8j3k67UFvPeZvfTlKYuceiNq+iZ8a/ADzAbZMeaV0FMvsJmG97MH8yN/VdXY9hcscchsnc99bJ1sClw==", "dev": true, "license": "MIT", "dependencies": { - "javascript-state-machine": "^3.0.1", - "loglevel": "^1.6.3", - "operation-retrier": "^3.0.0", - "platform": "^1.3.6", - "uuid": "^3.2.1", - "ws": "^5.1.0" + "@babel/runtime": "^7.14.5", + "core-js": "^3.17.3" }, "engines": { - "node": ">=8" + "node": ">=14" } }, "node_modules/@twilio/flex-sdk/node_modules/twilsock/node_modules/ws": { @@ -14007,9 +13996,9 @@ } }, "node_modules/@twilio/flex-sdk/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "dev": true, "license": "MIT", "engines": { @@ -14029,13 +14018,13 @@ } }, "node_modules/@twilio/flex-ui": { - "version": "2.13.3", - "resolved": "https://registry.npmjs.org/@twilio/flex-ui/-/flex-ui-2.13.3.tgz", - "integrity": "sha512-HllSmerwAQzQIQEPHFriqUQjmVpCbfgHSRksxs9HZK5GQPkyFWxE/BxnN7W7kNido+6yxRPA5Dsm2JTkLX/Ymg==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@twilio/flex-ui/-/flex-ui-2.16.0.tgz", + "integrity": "sha512-jkX2BjGcA+qtb3zuYjiaaxpBGAHnxPjB/M3GAEIMU/oZQKls8WJJ8NzzKuqCo+K2G9mZd9SRsgOhNsN/XR801w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@apollo/client": "3.9.11", + "@apollo/client": "^3.13.8", "@citrix/ucsdk": "4.0.2", "@dnd-kit/core": "6.0.8", "@dnd-kit/modifiers": "6.0.1", @@ -14049,7 +14038,7 @@ "@graphql-tools/utils": "^9.2.1", "@material-ui/core": "^4.12.3", "@material-ui/icons": "^4.11.2", - "@opentelemetry/api": "1.4.1", + "@opentelemetry/api": "1.9.0", "@opentelemetry/sdk-trace-base": "1.15.1", "@reduxjs/toolkit": "^1.6.0", "@twilio-paste/core": "^15.3.1", @@ -14058,8 +14047,8 @@ "@twilio-paste/icons": "^9.2.0", "@twilio-paste/lexical-library": "^4.2.0", "@twilio/conversations": "2.5.0", - "@twilio/flex-sdk": "0.99.40-dev", - "@twilio/flex-ui-telemetry": "1.3.2", + "@twilio/flex-sdk": "3.0.0", + "@twilio/flex-ui-telemetry": "1.3.3", "@twilio/player": "^1.1.1", "@twilio/voice-sdk": "2.11.1", "async-sema": "^3.0.0", @@ -14072,7 +14061,7 @@ "eventemitter3": "4.0.0", "fuse.js": "^6.4.6", "google-libphonenumber": "3.2.6", - "graphql": "^16.7.1", + "graphql": "^16.8.2", "graphql-tag": "^2.12.6", "handlebars": "~4.7.7", "highcharts-react-official": "^3.1.0", @@ -14202,18 +14191,22 @@ } }, "node_modules/@twilio/flex-ui-telemetry": { - "version": "1.3.2", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@twilio/flex-ui-telemetry/-/flex-ui-telemetry-1.3.3.tgz", + "integrity": "sha512-JjKpuI6SQ5HzFLialRGdADwDotiidLle8APW21Gs+zGVC+j+bZxMSfoSa0uf7lOU1h2/nErfsimitCOLJSncdw==", "dev": true, "license": "Apache-2.0", "dependencies": { "semver": "7.5.4" }, "engines": { - "node": "^14 || ^16 || ^18 || ^20" + "node": ">=14" } }, "node_modules/@twilio/flex-ui-telemetry/node_modules/lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "license": "ISC", "dependencies": { @@ -14225,6 +14218,8 @@ }, "node_modules/@twilio/flex-ui-telemetry/node_modules/semver": { "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "license": "ISC", "dependencies": { @@ -14239,6 +14234,8 @@ }, "node_modules/@twilio/flex-ui-telemetry/node_modules/yallist": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, "license": "ISC" }, @@ -14539,6 +14536,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/events": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", + "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/express": { "version": "4.17.21", "dev": true, @@ -34592,13 +34596,6 @@ "redux": "^4" } }, - "node_modules/reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "dev": true, @@ -34781,9 +34778,9 @@ } }, "node_modules/rehackt": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.0.6.tgz", - "integrity": "sha512-l3WEzkt4ntlEc/IB3/mF6SRgNHA6zfQR7BlGOgBTOmx7IJJXojDASav+NsgXHFjHn+6RmwqsGPFgZpabWpeOdw==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz", + "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -35118,16 +35115,6 @@ "node": ">=10" } }, - "node_modules/response-iterator": { - "version": "0.2.25", - "resolved": "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.25.tgz", - "integrity": "sha512-15K4tT8X35W0zJ5bv3fAf4eEKqOwS7yzd+Bg6YEE9NLltVbPbuTcYo3J2AP6AMQGMJmJkFCG421+kP2/iCBfDA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/responselike": { "version": "2.0.1", "dev": true, @@ -38084,9 +38071,9 @@ } }, "node_modules/twilio-taskrouter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/twilio-taskrouter/-/twilio-taskrouter-2.1.0.tgz", - "integrity": "sha512-TdO8i1zj97CwW2XZTacOcOojI6ihEKb1DicVGj2dZ5ixGeo+nSKVf5dkXuxTlWDcUppov5waNbzNsX7Kjt3CLQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/twilio-taskrouter/-/twilio-taskrouter-2.1.1.tgz", + "integrity": "sha512-XIl3N2ap4Ff0QYiiBGfS1KsX4U+6+6E5YLKL9LUyQEHpXWRfAB9/NOSshzxMj49wSVrec+68HlHcejG1XV1nOg==", "dev": true, "license": "MIT", "dependencies": { @@ -38099,7 +38086,6 @@ "loglevel": "^1.4.1", "path-browserify": "^1.0.1", "typed-emitter": "^2.1.0", - "util": "^0.12.4", "ws": "^8.17.1" }, "engines": { @@ -38136,9 +38122,9 @@ } }, "node_modules/twilio-taskrouter/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "dev": true, "license": "MIT", "engines": { diff --git a/plugin-hrm-form/package.json b/plugin-hrm-form/package.json index 75938e95a8..0ab1ca3d49 100644 --- a/plugin-hrm-form/package.json +++ b/plugin-hrm-form/package.json @@ -100,7 +100,7 @@ "@testing-library/user-event": "^14.1.1", "@twilio/flex-plugin": "^7.1.0", "@twilio/flex-plugin-scripts": "^7.1.0", - "@twilio/flex-ui": "2.13.3", + "@twilio/flex-ui": "2.16.0", "@twilio/flex-ui-dev-proxy": "^1.1.2", "@types/jest": "^27.5.2", "@types/react": "^17.0.47", diff --git a/plugin-hrm-form/src/___tests__/components/case/caseOverview/EditCaseOverview.test.tsx b/plugin-hrm-form/src/___tests__/components/case/caseOverview/EditCaseOverview.test.tsx index 7571bd7c29..2e10ce1eab 100644 --- a/plugin-hrm-form/src/___tests__/components/case/caseOverview/EditCaseOverview.test.tsx +++ b/plugin-hrm-form/src/___tests__/components/case/caseOverview/EditCaseOverview.test.tsx @@ -85,7 +85,7 @@ const state: RecursivePartial = { status: 'open', info: {}, }, - availableStatusTransitions: [{}], + availableStatusTransitions: [{ value: 'open', label: 'Open', color: 'green', transitions: [] }], }, }, }, diff --git a/plugin-hrm-form/src/___tests__/conference/setUpConferenceActions.test.ts b/plugin-hrm-form/src/___tests__/conference/setUpConferenceActions.test.ts new file mode 100644 index 0000000000..588669fc61 --- /dev/null +++ b/plugin-hrm-form/src/___tests__/conference/setUpConferenceActions.test.ts @@ -0,0 +1,194 @@ +/** + * Copyright (C) 2021-2023 Technology Matters + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +/* eslint-disable camelcase */ + +import * as Flex from '@twilio/flex-ui'; + +import { getAseloFeatureFlags, getHrmConfig } from '../../hrmConfig'; +import { setUpConferenceActions } from '../../conference/setUpConferenceActions'; + +jest.mock('../../transfer/setUpTransferActions', () => ({ + TransfersNotifications: { + CantHangTransferInProgressNotification: 'TransfersNotifications_CantHangTransferInProgressNotification', + }, +})); + +jest.mock('@twilio/flex-ui', () => ({ + Actions: { + addListener: jest.fn(), + }, + Notifications: { + registerNotification: jest.fn(), + showNotificationSingle: jest.fn(), + }, + NotificationType: { error: 'error' }, + Template: jest.fn(), +})); + +jest.mock('../../hrmConfig', () => ({ + getAseloFeatureFlags: jest.fn(), + getHrmConfig: jest.fn(), +})); + +jest.mock('../../transfer/transferTaskState', () => ({ + hasTaskControl: jest.fn(), + isTransferring: jest.fn(), +})); + +const mockGetAseloFeatureFlags = getAseloFeatureFlags as jest.MockedFunction; +const mockGetHrmConfig = getHrmConfig as jest.MockedFunction; +const mockAddListener = Flex.Actions.addListener as jest.MockedFunction; +const mockShowNotificationSingle = Flex.Notifications.showNotificationSingle as jest.MockedFunction< + typeof Flex.Notifications.showNotificationSingle +>; + +const { hasTaskControl, isTransferring } = jest.requireMock('../../transfer/transferTaskState'); + +const listeners: Record any)[]> = {}; + +const captureListeners = () => { + mockAddListener.mockImplementation((eventName: string, cb: (...args: any[]) => any) => { + if (!listeners[eventName]) listeners[eventName] = []; + listeners[eventName].push(cb); + }); +}; + +beforeEach(() => { + jest.clearAllMocks(); + Object.keys(listeners).forEach(k => delete listeners[k]); + mockGetHrmConfig.mockReturnValue({ accountScopedLambdaBaseUrl: 'https://lambda.example.com' } as any); + captureListeners(); +}); + +const triggerListener = (eventName: string, ...args: any[]) => { + const cbs = listeners[eventName] ?? []; + return Promise.all(cbs.map(cb => cb(...args))); +}; + +describe('setUpConferenceActions', () => { + describe('beforeAcceptTask listener', () => { + test('sets conferenceOptions when feature flag is enabled and conferenceOptions is defined', async () => { + mockGetAseloFeatureFlags.mockReturnValue({ enable_conference_status_event_handler: true } as any); + setUpConferenceActions(); + + const conferenceOptions: Record = {}; + await triggerListener('beforeAcceptTask', { conferenceOptions }); + + expect(conferenceOptions.conferenceStatusCallback).toBe( + 'https://lambda.example.com/conference/conferenceStatusCallback', + ); + expect(conferenceOptions.conferenceStatusCallbackMethod).toBe('POST'); + expect(conferenceOptions.conferenceStatusCallbackEvent).toBe('leave'); + }); + + test('does not throw and does nothing when feature flag is enabled but conferenceOptions is undefined (non-call task)', async () => { + mockGetAseloFeatureFlags.mockReturnValue({ enable_conference_status_event_handler: true } as any); + setUpConferenceActions(); + + await expect(triggerListener('beforeAcceptTask', { conferenceOptions: undefined })).resolves.not.toThrow(); + }); + + test('does not set conferenceOptions when feature flag is disabled', async () => { + mockGetAseloFeatureFlags.mockReturnValue({ enable_conference_status_event_handler: false } as any); + setUpConferenceActions(); + + const conferenceOptions: Record = {}; + await triggerListener('beforeAcceptTask', { conferenceOptions }); + + expect(conferenceOptions.conferenceStatusCallback).toBeUndefined(); + expect(conferenceOptions.conferenceStatusCallbackMethod).toBeUndefined(); + expect(conferenceOptions.conferenceStatusCallbackEvent).toBeUndefined(); + }); + + test('does not throw when feature flag is disabled and conferenceOptions is undefined', async () => { + mockGetAseloFeatureFlags.mockReturnValue({ enable_conference_status_event_handler: false } as any); + setUpConferenceActions(); + + await expect(triggerListener('beforeAcceptTask', { conferenceOptions: undefined })).resolves.not.toThrow(); + }); + }); + + describe('beforeHangupCall listener', () => { + const abortFunction = jest.fn(); + + beforeEach(() => { + mockGetAseloFeatureFlags.mockReturnValue({ enable_conference_status_event_handler: true } as any); + isTransferring.mockReturnValue(false); + hasTaskControl.mockReturnValue(false); + }); + + test('does not throw and returns early when conference is undefined', async () => { + setUpConferenceActions(); + const task = { conference: undefined }; + + await expect(triggerListener('beforeHangupCall', { task }, abortFunction)).resolves.not.toThrow(); + expect(abortFunction).not.toHaveBeenCalled(); + }); + + test('calls abortFunction when transfer is in progress', async () => { + setUpConferenceActions(); + isTransferring.mockReturnValue(true); + const task = { + conference: { + participants: [], + }, + }; + + await triggerListener('beforeHangupCall', { task }, abortFunction); + + expect(abortFunction).toHaveBeenCalled(); + expect(mockShowNotificationSingle).toHaveBeenCalledWith( + 'TransfersNotifications_CantHangTransferInProgressNotification', + ); + }); + + test('calls abortFunction when some participant is on hold with more than 2 joined participants and worker has task control', async () => { + setUpConferenceActions(); + hasTaskControl.mockReturnValue(true); + const task = { + conference: { + participants: [ + { status: 'joined', onHold: false }, + { status: 'joined', onHold: false }, + { status: 'joined', onHold: true }, + ], + }, + }; + + await triggerListener('beforeHangupCall', { task }, abortFunction); + + expect(abortFunction).toHaveBeenCalled(); + expect(mockShowNotificationSingle).toHaveBeenCalledWith('ConferenceNotifications_UnholdParticipantsNotification'); + }); + + test('does not call abortFunction when conference is present with no issues', async () => { + setUpConferenceActions(); + const task = { + conference: { + participants: [ + { status: 'joined', onHold: false }, + { status: 'joined', onHold: false }, + ], + }, + }; + + await triggerListener('beforeHangupCall', { task }, abortFunction); + + expect(abortFunction).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/plugin-hrm-form/src/___tests__/translations/index.test.ts b/plugin-hrm-form/src/___tests__/translations/index.test.ts index 9f5552a70f..f9abcf4c38 100644 --- a/plugin-hrm-form/src/___tests__/translations/index.test.ts +++ b/plugin-hrm-form/src/___tests__/translations/index.test.ts @@ -14,28 +14,22 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -import { Manager } from '@twilio/flex-ui'; - import { loadTranslations, initLocalization, lookupTranslation } from '../../translations'; jest.mock('../../translations/en.json', () => ({}), { virtual: true }); jest.mock('../../translations/en-US.json', () => ({}), { virtual: true }); jest.mock('../../translations/en-GB.json', () => ({}), { virtual: true }); -jest.mock('@twilio/flex-ui', () => ({ - Manager: { - getInstance: jest.fn(), - }, -})); - const mockGetAseloFeatureFlags = jest.fn(); const mockGetHrmConfig = jest.fn(); const mockGetDefinitionVersions = jest.fn(); +const mockGetTemplateStrings = jest.fn(); jest.mock('../../hrmConfig', () => ({ getAseloFeatureFlags: () => mockGetAseloFeatureFlags(), getHrmConfig: () => mockGetHrmConfig(), getDefinitionVersions: () => mockGetDefinitionVersions(), + getTemplateStrings: () => mockGetTemplateStrings(), })); describe('Hierarchical Translations', () => { @@ -169,29 +163,27 @@ describe('Hierarchical Translations', () => { }); describe('lookupTranslation', () => { - const mockManagerGetInstance = Manager.getInstance as jest.MockedFunction; - afterEach(() => { jest.clearAllMocks(); }); - test('returns compiled string for a key that exists in Manager strings', () => { - mockManagerGetInstance.mockReturnValue({ strings: { MyKey: 'Hello World' } } as any); + test('returns compiled string for a key that exists in template strings', () => { + mockGetTemplateStrings.mockReturnValue({ MyKey: 'Hello World' } as any); expect(lookupTranslation('MyKey')).toBe('Hello World'); }); - test('falls back to using the code itself as template when key does not exist in Manager strings', () => { - mockManagerGetInstance.mockReturnValue({ strings: {} } as any); + test('falls back to using the code itself as template when key does not exist in template strings', () => { + mockGetTemplateStrings.mockReturnValue({} as any); expect(lookupTranslation('FallbackKey')).toBe('FallbackKey'); }); test('passes parameters to the Handlebars template', () => { - mockManagerGetInstance.mockReturnValue({ strings: { Greeting: 'Hello {{name}}!' } } as any); + mockGetTemplateStrings.mockReturnValue({ Greeting: 'Hello {{name}}!' } as any); expect(lookupTranslation('Greeting', { name: 'World' })).toBe('Hello World!'); }); test('uses empty parameters object by default', () => { - mockManagerGetInstance.mockReturnValue({ strings: { SimpleMsg: 'No params here' } } as any); + mockGetTemplateStrings.mockReturnValue({ SimpleMsg: 'No params here' } as any); expect(lookupTranslation('SimpleMsg')).toBe('No params here'); }); }); diff --git a/plugin-hrm-form/src/components/OfflineContact/AddOfflineContactButton.tsx b/plugin-hrm-form/src/components/OfflineContact/AddOfflineContactButton.tsx index 5b19cd8589..f0c1bb50b4 100644 --- a/plugin-hrm-form/src/components/OfflineContact/AddOfflineContactButton.tsx +++ b/plugin-hrm-form/src/components/OfflineContact/AddOfflineContactButton.tsx @@ -21,12 +21,13 @@ import { RootState } from '../../states'; import { Contact } from '../../types/types'; import AddTaskButton from '../common/AddTaskButton'; import { getOfflineContactTask } from '../../states/contacts/offlineContactTask'; -import { getHrmConfig, getTemplateStrings } from '../../hrmConfig'; +import { getHrmConfig } from '../../hrmConfig'; import { newContact } from '../../states/contacts/contactState'; import asyncDispatch from '../../states/asyncDispatch'; import { createOfflineContactAsyncAction } from '../../states/contacts/saveContact'; import selectCurrentOfflineContact from '../../states/contacts/selectCurrentOfflineContact'; import { selectCurrentDefinitionVersion } from '../../states/configuration/selectDefinitions'; +import { lookupTranslation } from '../../translations'; const AddOfflineContactButton: React.FC = () => { const dispatch = useDispatch(); @@ -71,7 +72,7 @@ const AddOfflineContactButton: React.FC = () => { // Temporary hack: show an error if no offline contact opens after 5 seconds setErrorTimer( setTimeout(() => { - alert(getTemplateStrings()['TaskList-AddOfflineContact-CreateError']); + alert(lookupTranslation('TaskList-AddOfflineContact-CreateError')); setErrorTimer(null); }, 5000), ); diff --git a/plugin-hrm-form/src/components/case/Case.tsx b/plugin-hrm-form/src/components/case/Case.tsx index c49302db5c..1c61592c65 100644 --- a/plugin-hrm-form/src/components/case/Case.tsx +++ b/plugin-hrm-form/src/components/case/Case.tsx @@ -135,7 +135,7 @@ const Case: React.FC = ({ task, handleClose, onNewCaseSaved = () => Promi if (loading || loadingCase) { return ( - + ); } diff --git a/plugin-hrm-form/src/components/caseList/CaseListTable.tsx b/plugin-hrm-form/src/components/caseList/CaseListTable.tsx index 9ae06c9668..fd18708898 100644 --- a/plugin-hrm-form/src/components/caseList/CaseListTable.tsx +++ b/plugin-hrm-form/src/components/caseList/CaseListTable.tsx @@ -33,6 +33,7 @@ import { getInitializedCan } from '../../permissions/rules'; import { namespace } from '../../states/storeNamespaces'; import { RootState } from '../../states'; import { PermissionActions } from '../../permissions/actions'; +import { lookupTranslation } from '../../translations'; const ROW_HEIGHT = 89; @@ -75,7 +76,7 @@ const CaseListTable: React.FC = ({ loading, caseList, caseCount, handl }} > - + diff --git a/plugin-hrm-form/src/components/forms/components/FormSelect/generateSelectOptions.tsx b/plugin-hrm-form/src/components/forms/components/FormSelect/generateSelectOptions.tsx index a6480ea309..0f128c7eca 100644 --- a/plugin-hrm-form/src/components/forms/components/FormSelect/generateSelectOptions.tsx +++ b/plugin-hrm-form/src/components/forms/components/FormSelect/generateSelectOptions.tsx @@ -18,11 +18,11 @@ import { SelectOption } from 'hrm-form-definitions'; import React from 'react'; import { FormOption } from '../../../../styles'; -import { getTemplateStrings } from '../../../../hrmConfig'; +import { lookupTranslation } from '../../../../translations'; const bindCreateSelectOptions = (path: string) => (o: SelectOption, selected: boolean) => ( - {getTemplateStrings()[o.label] ?? o.label} + {lookupTranslation(o.label)} ); diff --git a/plugin-hrm-form/src/components/tabbedForms/hooks/useTabbedForm.ts b/plugin-hrm-form/src/components/tabbedForms/hooks/useTabbedForm.ts index f5b44727d0..dc87044711 100644 --- a/plugin-hrm-form/src/components/tabbedForms/hooks/useTabbedForm.ts +++ b/plugin-hrm-form/src/components/tabbedForms/hooks/useTabbedForm.ts @@ -16,11 +16,11 @@ import { useForm, useFormContext } from 'react-hook-form'; import { recordingErrorHandler } from '../../../fullStory'; -import { getTemplateStrings } from '../../../hrmConfig'; +import { lookupTranslation } from '../../../translations'; const newSubmitHandlerFactory = (methods: ReturnType) => { const onError = recordingErrorHandler('Tabbed HRM Form', () => { - window.alert(getTemplateStrings()['Error-Form']); + window.alert(lookupTranslation('Error-Form')); }); return (successHandler: () => Promise) => { diff --git a/plugin-hrm-form/src/components/teamsView/teamsViewFilters.ts b/plugin-hrm-form/src/components/teamsView/teamsViewFilters.ts index cac10d1fc0..0a665b1cb9 100644 --- a/plugin-hrm-form/src/components/teamsView/teamsViewFilters.ts +++ b/plugin-hrm-form/src/components/teamsView/teamsViewFilters.ts @@ -100,7 +100,8 @@ const generateFilterDefinitionFactoryForInputExpression = ({ fieldName?: string; }): FilterDefinitionFactory => (state, teamsViewProps) => { const values = state.flex.supervisor.appliedFilters.find(af => af.name === id)?.values ?? []; - const selections = Array.isArray(values) ? values : [values]; + const rawSelections: unknown[] = Array.isArray(values) ? values : [values]; + const selections = rawSelections.filter((v): v is string => typeof v === 'string'); if (selections.length) { filterInputExpressionStrings[id] = queryGenerator(selections); } else { diff --git a/plugin-hrm-form/src/conference/setUpConferenceActions.tsx b/plugin-hrm-form/src/conference/setUpConferenceActions.tsx index 6ef1c9cc4a..11b9628e68 100644 --- a/plugin-hrm-form/src/conference/setUpConferenceActions.tsx +++ b/plugin-hrm-form/src/conference/setUpConferenceActions.tsx @@ -53,6 +53,8 @@ export const setUpConferenceActions = () => { Actions.addListener('beforeHangupCall', async (payload: { task: ITask }, abortFunction) => { const { conference } = payload.task; + if (!conference) return; + const someParticipantIsOnHold = conference.participants.filter(p => p.status === 'joined').length > 2 && conference.participants.some(p => p.onHold && p.status === 'joined'); @@ -68,13 +70,16 @@ export const setUpConferenceActions = () => { } }); - Flex.Actions.addListener('beforeAcceptTask', ({ conferenceOptions }) => { + Flex.Actions.addListener('beforeAcceptTask', payload => { if (getAseloFeatureFlags().enable_conference_status_event_handler) { - conferenceOptions.conferenceStatusCallback = `${ - getHrmConfig().accountScopedLambdaBaseUrl - }/conference/conferenceStatusCallback`; - conferenceOptions.conferenceStatusCallbackMethod = 'POST'; - conferenceOptions.conferenceStatusCallbackEvent = 'leave'; + const { conferenceOptions } = payload; + if (conferenceOptions) { + conferenceOptions.conferenceStatusCallback = `${ + getHrmConfig().accountScopedLambdaBaseUrl + }/conference/conferenceStatusCallback`; + conferenceOptions.conferenceStatusCallbackMethod = 'POST'; + conferenceOptions.conferenceStatusCallbackEvent = 'leave'; + } } }); }; diff --git a/plugin-hrm-form/src/notifications/newMessage.ts b/plugin-hrm-form/src/notifications/newMessage.ts index 51e6bd5478..dce5d59348 100644 --- a/plugin-hrm-form/src/notifications/newMessage.ts +++ b/plugin-hrm-form/src/notifications/newMessage.ts @@ -21,6 +21,12 @@ import { playNotification } from './playNotification'; export const subscribeAlertOnConversationJoined = task => { const manager = Manager.getInstance(); + if (!manager.conversationsClient) { + console.warn( + 'conversationsClient not available in subscribeAlertOnConversationJoined, audio alerts will not be set up for this task', + ); + return; + } manager.conversationsClient.once('conversationJoined', () => trySubscribeAudioAlerts(task, 0, 0)); }; diff --git a/plugin-hrm-form/src/services/SyncService.ts b/plugin-hrm-form/src/services/SyncService.ts index 5afcfceba4..6a5550cf47 100644 --- a/plugin-hrm-form/src/services/SyncService.ts +++ b/plugin-hrm-form/src/services/SyncService.ts @@ -18,8 +18,9 @@ import SyncClient from 'twilio-sync'; import { SWITCHBOARD_NOTIFY_DOCUMENT, SWITCHBOARD_STATE_DOCUMENT, SwitchboardSyncState } from 'hrm-types'; import fetchProtectedApi from './fetchProtectedApi'; -import { getAseloFeatureFlags, getTemplateStrings } from '../hrmConfig'; +import { getAseloFeatureFlags } from '../hrmConfig'; import { isErr, newErr, newOk } from '../types/Result'; +import { lookupTranslation } from '../translations'; // eslint-disable-next-line import/no-mutable-exports let sharedSyncClient: SyncClient; @@ -92,7 +93,7 @@ const copyError = error => ({ const validateSyncConnection = (): void => { if (!isSyncClientConnected(sharedSyncClient)) { console.error('Error with Sync Client connection. Sync Client object is: ', sharedSyncClient); - console.error(getTemplateStrings().SharedStateSaveContactError); + console.error(lookupTranslation('SharedStateSaveContactError')); throw new Error('Sync client not connected'); } }; @@ -129,7 +130,7 @@ export const savePendingContactToSharedState = async (task, payload, error) => { export const createCallStatusSyncDocument = async (onUpdateCallback: ({ data }: any) => void) => { if (!isSyncClientConnected(sharedSyncClient)) { console.error('Error with Sync Client connection. Sync Client object is: ', sharedSyncClient); - console.error(getTemplateStrings().SharedStateSaveContactError); + console.error(lookupTranslation('SharedStateSaveContactError')); return { status: 'failure', callStatusSyncDocument: null } as const; } diff --git a/plugin-hrm-form/src/translations/en.json b/plugin-hrm-form/src/translations/en.json index 061e775775..5142357295 100644 --- a/plugin-hrm-form/src/translations/en.json +++ b/plugin-hrm-form/src/translations/en.json @@ -182,6 +182,7 @@ "Forms-FileUpload-InvalidFileTypeError": "Invalid file type. Only PNG, JPG, JPEG, PDF, DOC, and DOCX files are allowed.", "Forms-FileUpload-FileSizeError": "File exceeds max size.", "Case-CaseNumber": "Case #", + "Case-Loading": "Loading case", "Case-Timeline-RecentTitle": "Recent Timeline", "Case-Timeline-Title": "Timeline", "Case-Timeline-PaginationDescription": "Showing {{from}}-{{to}} of {{total}}", @@ -271,6 +272,7 @@ "SideNavCaseList": "Case List", "CaseList-Cases": "Cases", "CaseList-NoCases": "No cases found.", + "CaseList-Loading": "Loading case", "CaseList-Filters-CaseCount-Singular": "{{count}} case", "CaseList-Filters-CaseCount-Plural": "{{count}} cases", "CaseList-THCase": "Case", diff --git a/plugin-hrm-form/src/translations/index.ts b/plugin-hrm-form/src/translations/index.ts index 0423eb2852..b31eba6852 100644 --- a/plugin-hrm-form/src/translations/index.ts +++ b/plugin-hrm-form/src/translations/index.ts @@ -15,9 +15,8 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ import Handlebars from 'handlebars'; -import { Manager } from '@twilio/flex-ui'; -import { getDefinitionVersions } from '../hrmConfig'; +import { getDefinitionVersions, getTemplateStrings } from '../hrmConfig'; // default language to initialize plugin export const defaultLocale = 'en-US'; @@ -122,6 +121,6 @@ export const initLocalization = (localizationConfig: LocalizationConfig, helplin }; export const lookupTranslation = (code: string, parameters: Record = {}): string => { - const { strings } = Manager.getInstance(); + const strings = getTemplateStrings(); return Handlebars.compile(strings[code] ?? code)(parameters); }; diff --git a/plugin-hrm-form/src/utils/setUpActions.ts b/plugin-hrm-form/src/utils/setUpActions.ts index f807dc7299..1e97bd2afe 100644 --- a/plugin-hrm-form/src/utils/setUpActions.ts +++ b/plugin-hrm-form/src/utils/setUpActions.ts @@ -141,6 +141,12 @@ const sendWelcomeMessageOnConversationJoined = ( }, ms); }; // Ignore event payload as we already have everything we want in afterAcceptTask arguments. Start at 0ms as many users are able to send the message right away + if (!manager.conversationsClient) { + console.warn( + 'conversationsClient not available in sendWelcomeMessageOnConversationJoined, welcome message will not be sent for this task', + ); + return; + } manager.conversationsClient.once('conversationJoined', (c: Conversation) => trySendWelcomeMessage(c, 0, 0)); };