diff --git a/package-lock.json b/package-lock.json index 476f54153..57d1f2f2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,8 @@ "@vuelidate/validators": "^2.0.0", "@vueuse/core": "^10.3.0", "@webitel/flow-ui-sdk": "^0.1.14", - "@webitel/ui-sdk": "^24.4.20", - "axios": "^1.6.5", + "@webitel/ui-sdk": "^24.4.28", + "axios": "^1.6.8", "clipboard-copy": "^4.0.1", "cron-validator": "^1.3.1", "cronstrue": "^2.13.0", @@ -33,7 +33,7 @@ "vue-router": "^4.2.5", "vue2-dropzone": "^3.6.0", "vuex": "^4.1.0", - "webitel-sdk": "^24.02.06" + "webitel-sdk": "^24.2.9" }, "devDependencies": { "@vitejs/plugin-vue": "^4.4.0", @@ -1725,15 +1725,16 @@ } }, "node_modules/@webitel/ui-sdk": { - "version": "24.4.20", - "resolved": "https://registry.npmjs.org/@webitel/ui-sdk/-/ui-sdk-24.4.20.tgz", - "integrity": "sha512-CYJgfc1zJzi1TFNc2VGz2EpCWkWLqqjwPyPWQZXN/DNtNgdG68TzhuU1avL/ZcYId6opdXeNPnynCtBvrs1KWA==", + "version": "24.4.28", + "resolved": "https://registry.npmjs.org/@webitel/ui-sdk/-/ui-sdk-24.4.28.tgz", + "integrity": "sha512-LRDfoIhYiyWts8jnDU6ZlXwyrM8OgaXoCwuVHeEouaSsHQhxHIX6etid+BE13Nkdr954Xbx1QJ/BCID5eDeHqw==", "dependencies": { "@floating-ui/vue": "^1.0.1", "@vuelidate/core": "^2.0.3", "@vuelidate/validators": "^2.0.4", "@vuepic/vue-datepicker": "^4.4.0", "@vueuse/components": "^10.7.2", + "axios": "^1.6.8", "clipboard-copy": "^4.0.1", "csv-stringify": "^5.5.3", "deep-copy": "^1.4.2", @@ -1752,7 +1753,7 @@ "vue-multiselect": "^3.0.0-beta.3", "vue-observe-visibility": "^2.0.0-alpha.1", "vue-router": "^4.1.6", - "webitel-sdk": "^23.7.5" + "webitel-sdk": "^24.2.6" } }, "node_modules/@webitel/ui-sdk/node_modules/decode-uri-component": { @@ -1837,64 +1838,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webitel/ui-sdk/node_modules/webitel-sdk": { - "version": "23.12.28", - "resolved": "https://registry.npmjs.org/webitel-sdk/-/webitel-sdk-23.12.28.tgz", - "integrity": "sha512-ukTNoZl/J6BHLfcYVoO7BXLmS/czPJBrItJDZmMk/CcYrWx7+mXsUgqIqIV6UoDrRz6rSiskeUpeeX+xMlj4Mw==", - "dependencies": { - "@types/webrtc": "~0.0.41", - "deep-copy": "1.4.2", - "ee-ts": "1.0.1", - "json-refs": "3.0.13", - "jssip": "=3.10.1", - "query-string": "7.0.0" - }, - "peerDependencies": { - "axios": "1.6.5", - "ee-ts": "1.0.1" - } - }, - "node_modules/@webitel/ui-sdk/node_modules/webitel-sdk/node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/@webitel/ui-sdk/node_modules/webitel-sdk/node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@webitel/ui-sdk/node_modules/webitel-sdk/node_modules/query-string": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.0.tgz", - "integrity": "sha512-Iy7moLybliR5ZgrK/1R3vjrXq03S13Vz4Rbm5Jg3EFq1LUmQppto0qtXz4vqZ386MSRjZgnTSZ9QC+NZOSd/XA==", - "dependencies": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@webitel/ui-sdk/node_modules/webitel-sdk/node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "engines": { - "node": ">=6" - } - }, "node_modules/abbrev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", @@ -2142,11 +2085,11 @@ } }, "node_modules/axios": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", - "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -3912,9 +3855,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -8583,9 +8526,9 @@ } }, "node_modules/webitel-sdk": { - "version": "24.2.6", - "resolved": "https://registry.npmjs.org/webitel-sdk/-/webitel-sdk-24.2.6.tgz", - "integrity": "sha512-pesFApYZJwKXSTAzxuFhkt/aVGqodEbOn0zUlDkALdj/6Za0/dZmAAZ4iBXar8YDUOeLxCnLRLrJxsQVAC7i1w==", + "version": "24.2.9", + "resolved": "https://registry.npmjs.org/webitel-sdk/-/webitel-sdk-24.2.9.tgz", + "integrity": "sha512-R9Clg4m9d1nPkcc4boIQCJZ/yvtC7YPH7bRizej/QDQSM3fqJFozS0xMxJHEZKbF77QyQN95Yt6VXMXjJjfOFQ==", "dependencies": { "@types/webrtc": "~0.0.41", "deep-copy": "1.4.2", @@ -8595,7 +8538,7 @@ "query-string": "7.0.0" }, "peerDependencies": { - "axios": "1.6.5", + "axios": "^1.6.5", "ee-ts": "1.0.1" } }, diff --git a/package.json b/package.json index 874927531..5ef490b7c 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ "@vuelidate/validators": "^2.0.0", "@vueuse/core": "^10.3.0", "@webitel/flow-ui-sdk": "^0.1.14", - "@webitel/ui-sdk": "^24.4.20", - "axios": "^1.6.5", + "@webitel/ui-sdk": "^24.4.28", + "axios": "^1.6.8", "clipboard-copy": "^4.0.1", "cron-validator": "^1.3.1", "cronstrue": "^2.13.0", @@ -36,7 +36,7 @@ "vue-router": "^4.2.5", "vue2-dropzone": "^3.6.0", "vuex": "^4.1.0", - "webitel-sdk": "^24.02.06" + "webitel-sdk": "^24.2.9" }, "devDependencies": { "@vitejs/plugin-vue": "^4.4.0", diff --git a/src/app/locale/en/en.js b/src/app/locale/en/en.js index fff9b80b6..7bf0db36f 100644 --- a/src/app/locale/en/en.js +++ b/src/app/locale/en/en.js @@ -624,8 +624,8 @@ export default { }, }, customChat: { - customChat: 'Custom chat', - appSecretHint: 'Enter value only if you want to edit (or create new) password', + customChat: 'Custom Chat', + appSecretHint: 'Altering this field will interrupt the integration', metadata: { appSecret: 'App Secret', callback: 'Callback', diff --git a/src/app/locale/kz/kz.js b/src/app/locale/kz/kz.js index e70975004..67421e881 100644 --- a/src/app/locale/kz/kz.js +++ b/src/app/locale/kz/kz.js @@ -542,14 +542,6 @@ export default { email: 'Электрондық пошта', }, }, - customChat: { - customChat: 'Custom chat', - appSecretHint: 'Қайта жазу (немесе жаңасын жасау) қажет болса, мәнді енгізіңіз App Secret', - metadata: { - appSecret: 'App Secret', - callback: 'Callback', - }, - }, newChatGateway: 'Жаңа мәтіндік шлюз', metadata: {}, }, diff --git a/src/app/locale/ru/ru.js b/src/app/locale/ru/ru.js index 61f5a01a6..6f6d8e875 100644 --- a/src/app/locale/ru/ru.js +++ b/src/app/locale/ru/ru.js @@ -624,8 +624,8 @@ export default { }, }, customChat: { - customChat: 'Custom chat', - appSecretHint: 'Введите значение, если хотите перезаписать (или создать новый) App Secret', + customChat: 'Custom Chat', + appSecretHint: 'Изменение этого поля прервет интеграцию', metadata: { appSecret: 'App Secret', callback: 'Callback', diff --git a/src/app/locale/ua/ua.js b/src/app/locale/ua/ua.js index 05fe22bb8..87b84454b 100644 --- a/src/app/locale/ua/ua.js +++ b/src/app/locale/ua/ua.js @@ -625,8 +625,8 @@ export default { }, }, customChat: { - customChat: 'Custom chat', - appSecretHint: 'Введіть значення, якщо хочете перезаписати (або створити новий) App Secret', + customChat: 'Custom Chat', + appSecretHint: 'Зміна цього поля перерве інтеграцію', metadata: { appSecret: 'App Secret', callback: 'Callback', diff --git a/src/modules/directory/modules/users/api/__tests__/users.spec.js b/src/modules/directory/modules/users/api/__tests__/users.spec.js deleted file mode 100644 index 69935704f..000000000 --- a/src/modules/directory/modules/users/api/__tests__/users.spec.js +++ /dev/null @@ -1,200 +0,0 @@ -import axios from 'axios'; -import UsersAPI from '../users'; - -// axios mock should be copy-pasted :( -// https://stackoverflow.com/questions/65554910/jest-referenceerror-cannot-access-before-initialization -vi.mock('axios', () => { - return { - default: { - post: vi.fn(), - get: vi.fn(), - delete: vi.fn(), - put: vi.fn(), - patch: vi.fn(), - create: vi.fn().mockReturnThis(), - interceptors: { - request: { - use: vi.fn(), eject: vi.fn(), - }, response: { - use: vi.fn(), eject: vi.fn(), - }, - }, - }, - }; -}); - -describe('UsersAPI', () => { - it('correctly computes "getList" method api call', async () => { - const inputParams = { - fields: ['id', 'name', 'vitest'], - }; - const url = '/users?fields=id&fields=name&fields=vitest&page=1&size=10'; - const mock = axios.get.mockImplementationOnce(() => Promise.resolve({ - data: {}, - })); - await UsersAPI.getList(inputParams); - expect(mock).toHaveBeenCalledWith(url); - }); - - it('correctly computes "getList" method output', async () => { - const output = { - items: [ - { - dnd: false, - id: 1, - name: '', - state: true, - status: '', - shouldCaseConvert: '', - }, - ], next: true, - }; - - const response = { - data: { - items: [ - { id: 1, should_case_convert: '' }, - ], next: true, - }, - }; - axios.get.mockImplementationOnce(() => Promise.resolve(response)); - expect(await UsersAPI.getList({})).toEqual(output); - }); - - it('correctly computes "get" method api call', async () => { - const inputParams = { - itemId: 1, - }; - const url = '/users/1'; - const mock = axios.get.mockImplementationOnce(() => Promise.resolve({ - data: {}, - })); - await UsersAPI.get(inputParams); - expect(mock).toHaveBeenCalledWith(url); - }); - - it('correctly computes "get" method output', async () => { - const output = { - id: 1, device: {}, devices: [], license: [], roles: [], variables: [ - { - key: '', value: '', - }, - ], - }; - - const response = { - data: { - id: 1, - }, - }; - axios.get.mockImplementationOnce(() => Promise.resolve(response)); - expect(await UsersAPI.get({})).toEqual(output); - }); - - it('correctly computes "add" method api call', async () => { - const input = { - itemInstance: { - name: 'test', - }, - }; - - const body = { - name: 'test', - profile: {}, // variables field (?) - }; - - const url = '/users'; - const mock = axios.post.mockImplementationOnce(() => Promise.resolve({ - data: {}, - })); - await UsersAPI.add(input); - expect(mock).toHaveBeenCalledWith(url, body); - }); - - it('correctly computes "add" method output', async () => { - const output = { - id: 1, - checkCase: '', - }; - - const response = { - data: { - id: 1, - check_case: '', - }, - }; - axios.post.mockImplementationOnce(() => Promise.resolve(response)); - expect(await UsersAPI.add({ itemInstance: {} })).toEqual(output); - }); - - it('correctly computes "update" method api call', async () => { - const input = { - itemInstance: { - name: 'test', - }, - itemId: 1, - }; - - const body = { - name: 'test', - profile: {}, // variables field (?) - }; - - const url = '/users/1'; - const mock = axios.put.mockImplementationOnce(() => Promise.resolve({ - data: {}, - })); - await UsersAPI.update(input); - expect(mock).toHaveBeenCalledWith(url, body); - }); - - it('correctly computes "update" method output', async () => { - const output = { - id: 1, - checkCase: '', - }; - - const response = { - data: { - id: 1, - check_case: '', - }, - }; - axios.put.mockImplementationOnce(() => Promise.resolve(response)); - expect(await UsersAPI.update({ itemInstance: {}, itemId: 1 })) - .toEqual(output); - }); - - it('correctly computes "patch" method api call', async () => { - const input = { - changes: { - name: 'test', - }, - id: 1, - }; - - const body = { - name: 'test', - }; - - const url = '/users/1'; - const mock = axios.patch.mockImplementationOnce(() => Promise.resolve({ - data: {}, - })); - await UsersAPI.patch(input); - expect(mock).toHaveBeenCalledWith(url, body); - }); - - it('correctly computes "delete" method api call', async () => { - const input = { - id: 1, - }; - - const url = '/users/1?permanent=true'; - const mock = axios.delete.mockImplementationOnce(() => Promise.resolve({ - data: {}, - })); - await UsersAPI.delete(input); - expect(mock).toHaveBeenCalledWith(url); - }); -}); diff --git a/src/modules/directory/modules/users/api/users.js b/src/modules/directory/modules/users/api/users.js index c39cf070f..84bc9ec90 100644 --- a/src/modules/directory/modules/users/api/users.js +++ b/src/modules/directory/modules/users/api/users.js @@ -1,253 +1,3 @@ -import { - getDefaultGetListResponse, - getDefaultGetParams, -} from '@webitel/ui-sdk/src/api/defaults'; -import applyTransform, { - camelToSnake, - generateUrl, - merge, - mergeEach, - notify, - sanitize, - snakeToCamel, - starToSearch, -} from '@webitel/ui-sdk/src/api/transformers'; -import deepCopy from 'deep-copy'; -import instance from '../../../../../app/api/instance'; +import users from '@webitel/ui-sdk/src/api/clients/users/users.js'; -const baseUrl = '/users'; -const fieldsToSend = [ - 'name', - 'username', - 'password', - 'extension', - 'status', - 'note', - 'roles', - 'license', - 'devices', - 'device', - 'profile', - 'email', -]; - -const getUsersList = async (params) => { - const fieldsToSend = ['page', 'size', 'q', 'sort', 'fields', 'id']; - - const defaultObject = { - name: '', - status: '', - state: true, - dnd: false, - }; - - const url = applyTransform(params, [ - merge(getDefaultGetParams()), - starToSearch('search'), - (params) => ({ ...params, q: params.search }), - sanitize(fieldsToSend), - camelToSnake(), - generateUrl(baseUrl), - ]); - try { - const response = await instance.get(url); - const { items, next } = applyTransform(response.data, [ - snakeToCamel(['profile']), - merge(getDefaultGetListResponse()), - ]); - return { - items: applyTransform(items, [ - mergeEach(defaultObject), - ]), - next, - }; - } catch (err) { - throw applyTransform(err, [ - notify, - ]); - } -}; - -const getUser = async ({ itemId: id }) => { - const defaultObject = { - roles: [], - license: [], - devices: [], - device: {}, - variables: [ - { key: '', value: '' }, - ], - }; - - const itemResponseHandler = (item) => { - const copy = deepCopy(item); - if (copy.license) { - copy.license.forEach((item) => { - // eslint-disable-next-line no-param-reassign - item.name = item.prod; - }); - } - if (copy.profile) { - copy.variables = Object.keys(copy.profile).map((key) => ({ - key, - value: copy.profile[key], - })); - } else { - copy.variables = [{ key: '', value: '' }]; - } - return copy; - }; - - const url = `${baseUrl}/${id}`; - - try { - const response = await instance.get(url); - return applyTransform(response.data, [ - snakeToCamel(['profile']), - merge(defaultObject), - itemResponseHandler, - ]); - } catch (err) { - throw applyTransform(err, [ - notify, - ]); - } -}; - -const preRequestHandler = (item) => { - const copy = deepCopy(item); - if (item.device && !item.device.id) delete copy.device; - // eslint-disable-next-line no-param-reassign - if (copy.roles) copy.roles.forEach((copy) => delete copy.text); - // eslint-disable-next-line no-param-reassign - if (copy.devices) copy.devices.forEach((copy) => delete copy.text); - if (copy.license) { - copy.license = copy.license.map((copy) => ({ id: copy.id })); - } - copy.profile = {}; - if (copy.variables) { - copy.variables.forEach((variable) => { - copy.profile[variable.key] = variable.value; - }); - } - return copy; -}; - -const addUser = async ({ itemInstance }) => { - const item = applyTransform(itemInstance, [ - preRequestHandler, - sanitize(fieldsToSend), - camelToSnake(['profile']), - ]); - try { - const response = await instance.post(baseUrl, item); - return applyTransform(response.data, [ - snakeToCamel(['profile']), - ]); - } catch (err) { - throw applyTransform(err, [ - notify, - ]); - } -}; - -const updateUser = async ({ itemInstance, itemId: id }) => { - const item = applyTransform(itemInstance, [ - preRequestHandler, - sanitize(fieldsToSend), - camelToSnake(['profile']), - ]); - - const url = `${baseUrl}/${id}`; - try { - const response = await instance.put(url, item); - return applyTransform(response.data, [ - snakeToCamel(['profile']), - ]); - } catch (err) { - throw applyTransform(err, [ - notify, - ]); - } -}; - -const patchUser = async ({ changes, id }) => { - const body = applyTransform(changes, [ - sanitize(fieldsToSend), - camelToSnake(['profile']), - ]); - const url = `${baseUrl}/${id}`; - try { - const response = await instance.patch(url, body); - return applyTransform(response.data, [ - snakeToCamel(['profile']), - ]); - } catch (err) { - throw applyTransform(err, [ - notify, - ]); - } -}; - -const patchUserPresence = async ({ changes, id }) => { - const body = applyTransform(changes, [ - sanitize(fieldsToSend), - camelToSnake(['profile']), - ]); - const url = `${baseUrl}/${id}/presence`; - try { - const response = await instance.patch(url, body); - return applyTransform(response.data, [ - snakeToCamel(['profile']), - ]); - } catch (err) { - throw applyTransform(err, [ - notify, - ]); - } -}; - -const deleteUser = async ({ id }) => { - const url = `${baseUrl}/${id}?permanent=true`; - // permanent=true for complete deletion - try { - const response = await instance.delete(url); - return applyTransform(response.data, []); - } catch (err) { - throw applyTransform(err, [ - notify, - ]); - } -}; - -const getUsersLookup = (params) => getUsersList({ - ...params, - fields: params.fields || ['id', 'name'], -}); - -const logoutUser = async ({ id }) => { - const url = `${baseUrl}/${id}/logout`; - try { - const response = await instance.post(url, {}); - return applyTransform(response.data, []); - } catch (err) { - throw applyTransform(err, [ - - notify, - ]); - } -}; - -const UsersAPI = { - getList: getUsersList, - get: getUser, - add: addUser, - patch: patchUser, - update: updateUser, - delete: deleteUser, - getLookup: getUsersLookup, - patchUserPresence, - logoutUser, -}; - -export default UsersAPI; +export default users; diff --git a/src/modules/routing/modules/chat-gateways/components/create-chat-gateway-popup.vue b/src/modules/routing/modules/chat-gateways/components/create-chat-gateway-popup.vue index 810540b49..5149b0608 100644 --- a/src/modules/routing/modules/chat-gateways/components/create-chat-gateway-popup.vue +++ b/src/modules/routing/modules/chat-gateways/components/create-chat-gateway-popup.vue @@ -78,7 +78,7 @@ export default { }; const custom = { value: ChatGatewayProvider.CUSTOM, - title: this.$t('objects.routing.chatGateways.customChat.customChat'), + title: this.$t('objects.routing.chatGateways.customChat.customChat') + this.$tc('objects.routing.gateways.gateways', 1), icon: 'custom-chat-gateway', }; return [telegramBot, telegramApp, infobip, messenger, viber, webchat, custom]; diff --git a/src/modules/routing/modules/chat-gateways/components/opened-chat-gateway.vue b/src/modules/routing/modules/chat-gateways/components/opened-chat-gateway.vue index 3baa055c6..f5da2b7b5 100644 --- a/src/modules/routing/modules/chat-gateways/components/opened-chat-gateway.vue +++ b/src/modules/routing/modules/chat-gateways/components/opened-chat-gateway.vue @@ -367,6 +367,9 @@ export default { case ChatGatewayProvider.TELEGRAM_BOT: chatTypeLocale = 'telegramBot.telegramBot'; break; + case ChatGatewayProvider.CUSTOM: + chatTypeLocale = 'customChat.customChat'; + break; case ChatGatewayProvider.MESSENGER: chatTypeLocale = `${this.chatType}.meta`; break;