@defra/flood-webchat 0.0.1-beta.9 → 1.0.1

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.
Files changed (48) hide show
  1. package/README.md +6 -0
  2. package/dist/client.js +1 -329
  3. package/dist/server.js +1 -286
  4. package/main.scss +13 -8
  5. package/package.json +28 -21
  6. package/src/client/components/availability/availability.jsx +104 -65
  7. package/src/client/components/availability/availability.scss +20 -32
  8. package/src/client/components/chat/chat.jsx +239 -0
  9. package/src/client/components/chat/chat.scss +258 -0
  10. package/src/client/components/errorSummary/error-summary.jsx +34 -0
  11. package/src/client/components/live-region.jsx +55 -0
  12. package/src/client/components/message/message.jsx +21 -0
  13. package/src/client/components/panel/panel-footer.jsx +9 -0
  14. package/src/client/components/panel/panel-header.jsx +75 -0
  15. package/src/client/components/panel/panel.jsx +163 -0
  16. package/src/client/components/panel/panel.scss +112 -0
  17. package/src/client/components/screens/end-chat.jsx +77 -0
  18. package/src/client/components/screens/feedback.jsx +65 -0
  19. package/src/client/components/screens/pre-chat.jsx +62 -0
  20. package/src/client/components/screens/request-chat.jsx +183 -0
  21. package/src/client/components/screens/settings.jsx +97 -0
  22. package/src/client/components/screens/unavailable.jsx +23 -0
  23. package/src/client/components/skip-link.jsx +42 -0
  24. package/src/client/hooks/useFocusedElements.js +80 -0
  25. package/src/client/hooks/useTextareaAutosize.js +13 -0
  26. package/src/client/index.jsx +18 -1
  27. package/src/client/lib/agent-status-headline.js +17 -0
  28. package/src/client/lib/check-availability.js +1 -0
  29. package/src/client/lib/history.js +16 -0
  30. package/src/client/lib/message-notification.js +30 -0
  31. package/src/client/lib/transform-messages.js +47 -0
  32. package/src/client/scss/_objects.scss +33 -0
  33. package/src/client/scss/_settings.scss +101 -0
  34. package/src/client/scss/_tools.scss +33 -0
  35. package/src/client/scss/_utilities.scss +25 -0
  36. package/src/client/store/AppProvider.jsx +184 -0
  37. package/src/client/store/actions-map.js +153 -0
  38. package/src/client/store/constants.js +5 -0
  39. package/src/client/store/reducer.js +31 -0
  40. package/src/client/store/useApp.js +5 -0
  41. package/src/client/store/useChatSdk.js +37 -0
  42. package/src/server/lib/utils.js +25 -9
  43. package/webpack.config.mjs +0 -2
  44. package/webpack.prod.mjs +7 -0
  45. package/dist/client.js.map +0 -1
  46. package/dist/server.js.map +0 -1
  47. package/src/client/lib/external-stores.js +0 -4
  48. package/src/client/lib/external-sync-store.js +0 -42
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.js","mappings":"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","sources":["webpack://@defra/flood-webchat/./src/server/index.js","webpack://@defra/flood-webchat/./src/server/lib/client.js","webpack://@defra/flood-webchat/./src/server/lib/utils.js","webpack://@defra/flood-webchat/external commonjs \"axios\"","webpack://@defra/flood-webchat/external commonjs \"jwt-decode\"","webpack://@defra/flood-webchat/external commonjs \"querystring\"","webpack://@defra/flood-webchat/webpack/bootstrap","webpack://@defra/flood-webchat/webpack/before-startup","webpack://@defra/flood-webchat/webpack/startup","webpack://@defra/flood-webchat/webpack/after-startup"],"sourcesContent":["const { authenticate, getApiBaseUrl, getIsOpen, getActivity } = require('./lib/client.js')\n\n/**\n * Returns webchat availability\n * @param options {object}\n * @param options.clientId {string}\n * @param options.clientSecret {string}\n * @param options.accessKey {string}\n * @param options.accessSecret {string}\n * @param options.skillEndpoint {string}\n * @param options.hoursEndpoint {string}\n * @param options.authenticationUri {string}\n * @param options.wellKnownUri {string}\n * @param options.maxQueueCount {string}\n * @returns {Promise<{date: Date, availability: (string)}>}\n */\nmodule.exports = async function getAvailability ({\n clientId,\n clientSecret,\n accessKey,\n accessSecret,\n maxQueueCount,\n skillEndpoint,\n hoursEndpoint,\n wellKnownUri = 'https://cxone.niceincontact.com/.well-known/cxone-configuration',\n authenticationUri = 'https://cxone.niceincontact.com/auth/token'\n}) {\n const authorisation = 'Basic ' + Buffer.from(`${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`).toString('base64')\n\n // Cache authentication and re-authenticate when needed (lasts 1 hour?)\n const { tenantId, token, tokenType } = await authenticate({\n authenticationUri,\n authorisation,\n accessKey,\n accessSecret\n })\n\n const apiBaseUrl = await getApiBaseUrl({ wellKnownUri, tenantId })\n\n const [{ hasCapacity, hasAgentsAvailable }, isOpen] = await Promise.all([\n getActivity({ baseUrl: apiBaseUrl, tokenType, token, skillEndpoint, maxQueueCount }),\n getIsOpen({ baseUrl: apiBaseUrl, token, tokenType, hoursEndpoint })\n ])\n\n const isAvailable = isOpen && hasAgentsAvailable && hasCapacity\n const isExistingOnly = isOpen && hasAgentsAvailable && !hasCapacity\n\n let availability = 'UNAVAILABLE'\n\n if (isAvailable) {\n availability = 'AVAILABLE'\n } else if (isExistingOnly) {\n availability = 'EXISTING'\n }\n\n return {\n date: new Date(),\n availability\n }\n}\n","const querystring = require('querystring')\nconst axios = require('axios')\nconst jwtdecode = require('jwt-decode')\nconst { isWithinHours } = require('./utils.js')\n\nconst contentType = 'application/x-www-form-urlencoded'\n\nconst authenticate = async ({ authenticationUri, authorisation, accessKey, accessSecret }) => {\n const url = new URL(authenticationUri)\n const body = querystring.stringify({\n grant_type: 'password',\n username: accessKey,\n password: accessSecret\n })\n\n const auth = await axios.post(url.href, body, {\n signal: AbortSignal.timeout(3000),\n headers: {\n Host: url.host,\n 'Content-Type': contentType,\n Authorization: authorisation\n }\n })\n\n return {\n token: auth.data.access_token,\n tokenType: auth.data.token_type,\n tenantId: jwtdecode(auth.data.id_token)?.tenantId\n }\n}\n\nconst getApiBaseUrl = async ({ wellKnownUri, tenantId }) => {\n const url = new URL(wellKnownUri)\n url.searchParams.set('tenantId', tenantId)\n\n const { data } = await axios.get(url.href, {\n headers: {\n Host: url.host\n },\n signal: AbortSignal.timeout(3000)\n })\n\n return data.api_endpoint\n}\n\nconst getActivity = async ({ tokenType, token, baseUrl, skillEndpoint, maxQueueCount }) => {\n const url = new URL(skillEndpoint, baseUrl)\n\n const skill = await axios.get(url.href, {\n signal: AbortSignal.timeout(3000),\n headers: {\n Host: url.host,\n Authorization: `${tokenType} ${token}`,\n 'Content-Type': contentType\n }\n })\n\n const activity = skill.data.skillActivity[0]\n\n return {\n hasCapacity: activity.queueCount < maxQueueCount,\n hasAgentsAvailable: activity.agentsAvailable >= 1\n }\n}\n\nconst getIsOpen = async ({ baseUrl, token, tokenType, hoursEndpoint }) => {\n const url = new URL(hoursEndpoint, baseUrl)\n\n const hours = await axios.get(url.href, {\n signal: AbortSignal.timeout(3000),\n headers: {\n Host: url.host,\n Authorization: `${tokenType} ${token}`,\n 'Content-Type': contentType\n }\n })\n\n const days = hours.data.resultSet.hoursOfOperationProfiles[0].days\n\n return isWithinHours(days)\n}\n\nmodule.exports = {\n authenticate,\n getApiBaseUrl,\n getIsOpen,\n getActivity\n}\n","const isWithinHours = (days, date = null) => {\n const now = date ? new Date(date) : new Date()\n\n const name = now.toLocaleDateString('en-GB', { weekday: 'long' })\n const day = days.find(d => d.day.toLowerCase() === name.toLowerCase())\n const dateItems = now.toLocaleDateString('en-GB').split('/')\n const open = `${dateItems[2]}-${dateItems[1]}-${dateItems[0]}T${day.openTime}`\n const close = `${dateItems[2]}-${dateItems[1]}-${dateItems[0]}T${day.closeTime}`\n\n return now.getTime() >= Date.parse(open) && now.getTime() <= Date.parse(close)\n}\n\nmodule.exports = {\n isWithinHours\n}\n","module.exports = require(\"axios\");","module.exports = require(\"jwt-decode\");","module.exports = require(\"querystring\");","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(\"./src/server/index.js\");\n",""],"names":["authenticate","getApiBaseUrl","getIsOpen","getActivity","require","module","exports","getAvailability","clientId","clientSecret","accessKey","accessSecret","maxQueueCount","skillEndpoint","hoursEndpoint","wellKnownUri","authenticationUri","authorisation","Buffer","from","encodeURIComponent","toString","tenantId","token","tokenType","apiBaseUrl","hasCapacity","hasAgentsAvailable","isOpen","Promise","all","baseUrl","isAvailable","isExistingOnly","availability","date","Date","querystring","axios","jwtdecode","isWithinHours","contentType","url","URL","body","stringify","grant_type","username","password","auth","post","href","signal","AbortSignal","timeout","headers","Host","host","Authorization","data","access_token","token_type","id_token","searchParams","set","get","api_endpoint","skill","activity","skillActivity","queueCount","agentsAvailable","hours","days","resultSet","hoursOfOperationProfiles","now","name","toLocaleDateString","weekday","day","find","d","toLowerCase","dateItems","split","open","openTime","close","closeTime","getTime","parse"],"sourceRoot":""}
@@ -1,4 +0,0 @@
1
- import ExternalSyncStore from './external-sync-store'
2
-
3
- export const useWebchatOpenState = ExternalSyncStore.create(false)
4
- export const useMessageThread = ExternalSyncStore.create([])
@@ -1,42 +0,0 @@
1
- import { useSyncExternalStore } from 'react'
2
-
3
- export default class ExternalSyncStore {
4
- constructor (initialValue) {
5
- this._value = initialValue
6
- this._listeners = []
7
- }
8
-
9
- update (value) {
10
- this._value = value
11
- this.emitChange()
12
- }
13
-
14
- subscribe (listener) {
15
- this._listeners = [...this._listeners, listener]
16
- return () => {
17
- this._listeners = this._listeners.filter(l => l !== listener)
18
- }
19
- }
20
-
21
- emitChange () {
22
- for (const listener of this._listeners) {
23
- listener()
24
- }
25
- }
26
-
27
- getSnapshot () {
28
- return this._value
29
- }
30
-
31
- static create (initialValue) {
32
- const store = new ExternalSyncStore(initialValue)
33
- const boundSubscribe = store.subscribe.bind(store)
34
- const boundGetSnapshot = store.getSnapshot.bind(store)
35
- const boundUpdate = store.update.bind(store)
36
-
37
- return () => [
38
- useSyncExternalStore(boundSubscribe, boundGetSnapshot),
39
- boundUpdate
40
- ]
41
- }
42
- }