diff --git a/src/app/locale/en/en.js b/src/app/locale/en/en.js index f4998954..5faf9024 100644 --- a/src/app/locale/en/en.js +++ b/src/app/locale/en/en.js @@ -7,6 +7,8 @@ import AccessMode from '../../../modules/contacts/modules/permissions/enums/AccessMode.enum.js'; import TimelineTaskStatusEnum from '../../../modules/contacts/modules/timeline/enums/TimelineTaskStatus.enum.js'; +import TypesSourcesEnum + from '../../../modules/configuration/modules/lookups/modules/sources/enums/TypesSources.enum.js'; export default { crm: 'CRM', @@ -109,8 +111,19 @@ export default { validFrom: 'Valid from', validTo: 'Valid to', }, + sources: { + sources: 'Case source | Case sources', + types: { + [TypesSourcesEnum.CALL]: 'Call', + [TypesSourcesEnum.CHAT]: 'Chat', + [TypesSourcesEnum.SOCIAL_MEDIA]: 'Social Media', + [TypesSourcesEnum.EMAIL]: 'Email', + [TypesSourcesEnum.API]: 'API', + [TypesSourcesEnum.MANUAL]: 'Manual', + } + }, + [CrmSections.CONTACT_GROUPS]: 'Contact groups', [CrmSections.STATUSES]: 'Statuses', - [CrmSections.SOURCES]: 'Sources', }, }; diff --git a/src/app/locale/ru/ru.js b/src/app/locale/ru/ru.js index 30c90702..9f16cf68 100644 --- a/src/app/locale/ru/ru.js +++ b/src/app/locale/ru/ru.js @@ -2,6 +2,8 @@ import { WebitelContactsTimelineEventType } from 'webitel-sdk'; import ChatGatewayProvider from '@webitel/ui-sdk/src/enums/ChatGatewayProvider/ChatGatewayProvider.enum.js'; import CrmSections from '@webitel/ui-sdk/src/enums/WebitelApplications/CrmSections.enum'; +import TypesSourcesEnum + from '../../../modules/configuration/modules/lookups/modules/sources/enums/TypesSources.enum.js'; import AccessMode from '../../../modules/contacts/modules/permissions/enums/AccessMode.enum.js'; import TimelineTaskStatusEnum @@ -108,8 +110,19 @@ export default { validFrom: 'Действителен с', validTo: 'Действителен до', }, + + sources: { + sources: 'Источник обращений | Источники обращений', + types: { + [TypesSourcesEnum.CALL]: 'Звонок', + [TypesSourcesEnum.CHAT]: 'Чат', + [TypesSourcesEnum.SOCIAL_MEDIA]: 'Социальная сеть', + [TypesSourcesEnum.EMAIL]: 'Письмо', + [TypesSourcesEnum.API]: 'API', + [TypesSourcesEnum.MANUAL]: 'Созданное вручную', + }, + }, [CrmSections.CONTACT_GROUPS]: 'Группы контактов', [CrmSections.STATUSES]: 'Статусы', - [CrmSections.SOURCES]: 'Источники', }, }; diff --git a/src/app/locale/ua/ua.js b/src/app/locale/ua/ua.js index 594c4054..e23fa9af 100644 --- a/src/app/locale/ua/ua.js +++ b/src/app/locale/ua/ua.js @@ -2,6 +2,8 @@ import { WebitelContactsTimelineEventType } from 'webitel-sdk'; import ChatGatewayProvider from '@webitel/ui-sdk/src/enums/ChatGatewayProvider/ChatGatewayProvider.enum.js'; import CrmSections from '@webitel/ui-sdk/src/enums/WebitelApplications/CrmSections.enum'; +import TypesSourcesEnum + from '../../../modules/configuration/modules/lookups/modules/sources/enums/TypesSources.enum.js'; import AccessMode from '../../../modules/contacts/modules/permissions/enums/AccessMode.enum.js'; import TimelineTaskStatusEnum @@ -109,8 +111,19 @@ export default { validTo: 'Дійсний до', }, + sources: { + sources: 'Джерело звернень | Джерела звернень', + + types: { + [TypesSourcesEnum.CALL]: 'Дзвінок', + [TypesSourcesEnum.CHAT]: 'Чат', + [TypesSourcesEnum.SOCIAL_MEDIA]: 'Соціальна мережа', + [TypesSourcesEnum.EMAIL]: 'Лист', + [TypesSourcesEnum.API]: 'API', + [TypesSourcesEnum.MANUAL]: 'Створене вручну', + }, + }, [CrmSections.CONTACT_GROUPS]: 'Групи контактів', [CrmSections.STATUSES]: 'Статуси', - [CrmSections.SOURCES]: 'Джерела', }, }; diff --git a/src/app/router/index.js b/src/app/router/index.js index 45270e30..7e2cb17a 100644 --- a/src/app/router/index.js +++ b/src/app/router/index.js @@ -20,6 +20,12 @@ import TheSlas from '../../modules/configuration/modules/lookups/modules/slas/components/the-slas.vue'; import OpenedSla from '../../modules/configuration/modules/lookups/modules/slas/components/opened-sla.vue'; import OpenedSlaGeneral from '../../modules/configuration/modules/lookups/modules/slas/components/opened-sla-general.vue'; +import TheSources + from '../../modules/configuration/modules/lookups/modules/sources/components/the-sources.vue'; +import OpenedSource + from '../../modules/configuration/modules/lookups/modules/sources/components/opened-source.vue'; +import OpenedSourceGeneral + from '../../modules/configuration/modules/lookups/modules/sources/components/opened-source-general.vue'; import store from '../store'; @@ -148,7 +154,26 @@ const routes = [ // component: SlasConditions, // }, ], - } + }, + { + path: 'sources', + name: CrmSections.SOURCES, + component: TheSources, + // beforeEnter: checkRouteAccess, + }, + { + path: 'sources/:id', + name: `${CrmSections.SOURCES}-card`, + component: OpenedSource, + redirect: { name: `${CrmSections.SOURCES}-general` }, + children: [ + { + path: 'general', + name: `${CrmSections.SOURCES}-general`, + component: OpenedSourceGeneral, + }, + ], + }, ], }, ], diff --git a/src/modules/configuration/components/the-configuration.vue b/src/modules/configuration/components/the-configuration.vue index 13711b9a..06902861 100644 --- a/src/modules/configuration/components/the-configuration.vue +++ b/src/modules/configuration/components/the-configuration.vue @@ -20,7 +20,7 @@ const nav = [ subNav: [ { value: CrmSections.SOURCES, - name: t('lookups.sources'), + name: t('lookups.sources.sources', 2), route: 'lookups/sources', }, { diff --git a/src/modules/configuration/modules/lookups/modules/sources/api/sources.js b/src/modules/configuration/modules/lookups/modules/sources/api/sources.js new file mode 100644 index 00000000..5a185b1b --- /dev/null +++ b/src/modules/configuration/modules/lookups/modules/sources/api/sources.js @@ -0,0 +1,145 @@ +import { + getDefaultGetListResponse, + getDefaultGetParams, + getDefaultInstance, + getDefaultOpenAPIConfig, +} from '@webitel/ui-sdk/src/api/defaults/index.js'; +import applyTransform, { + camelToSnake, + merge, + notify, + sanitize, + snakeToCamel, + starToSearch, +} from '@webitel/ui-sdk/src/api/transformers/index.js'; +import { SourcesApiFactory } from 'webitel-sdk'; + +const instance = getDefaultInstance(); +const configuration = getDefaultOpenAPIConfig(); + +const sourceService = new SourcesApiFactory(configuration, '', instance); + +const fieldsToSend = ['name', 'description', 'type']; + +const getSourcesList = async (params) => { + const fieldsToSend = ['page', 'size', 'q', 'sort', 'fields', 'id']; + + const listResponseHandler = (items) => { + return items.map((item) => ({ + ...item, + type: item.type.toLowerCase(), + })); + }; + + const { + page, + size, + fields, + sort, + id, + q, + type, + } = applyTransform(params, [ + merge(getDefaultGetParams()), + starToSearch('search'), + (params) => ({ ...params, q: params.search }), + sanitize(fieldsToSend), + camelToSnake(), + ]); + + try { + const response = await sourceService.listSources( + page, + size, + fields, + sort, + id, + q, + type, + ) + const { items, next } = applyTransform(response.data, [ + merge(getDefaultGetListResponse()), + ]); + return { + items: applyTransform(items, [listResponseHandler]), + next, + }; + } catch (err) { + throw applyTransform(err, [notify]); + } +}; + +const getSource = async ({ itemId: id }) => { + const itemResponseHandler = (item) => { + if(item.source.type) { + item.source.type = item.source.type.toLowerCase(); + } + return item.source; + }; + + try { + const response = await sourceService.locateSource(id); + return applyTransform(response.data, [ + snakeToCamel(), + itemResponseHandler, + ]); + } catch (err) { + throw applyTransform(err, [notify]); + } +}; + +const preRequestHandler = (item) => { + return { + ...item, + type: item.type.toUpperCase(), + } +}; + +const addSource = async ({ itemInstance }) => { + const item = applyTransform(itemInstance, [ + preRequestHandler, + sanitize(fieldsToSend), + camelToSnake(), + ]); + try { + const response = await sourceService.createSource(item); + return applyTransform(response.data, [ + snakeToCamel() + ]); + } catch (err) { + throw applyTransform(err, [notify]); + } +}; + +const updateSource = async ({ itemInstance, itemId: id }) => { + const item = applyTransform(itemInstance, [ + preRequestHandler, + camelToSnake(), + sanitize(fieldsToSend)]); + + try { + const response = await sourceService.updateSource(id, item); + return applyTransform(response.data, [snakeToCamel()]); + } catch (err) { + throw applyTransform(err, [notify]); + } +}; + +const deleteSource = async ({ id }) => { + try { + const response = await sourceService.deleteSource(id); + return applyTransform(response.data, []); + } catch (err) { + throw applyTransform(err, [notify]); + } +}; + +const SourcesAPI = { + getList: getSourcesList, + get: getSource, + add: addSource, + update: updateSource, + delete: deleteSource, +} + +export default SourcesAPI; diff --git a/src/modules/configuration/modules/lookups/modules/sources/components/opened-source-general.vue b/src/modules/configuration/modules/lookups/modules/sources/components/opened-source-general.vue new file mode 100644 index 00000000..97416ded --- /dev/null +++ b/src/modules/configuration/modules/lookups/modules/sources/components/opened-source-general.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/modules/configuration/modules/lookups/modules/sources/components/opened-source.vue b/src/modules/configuration/modules/lookups/modules/sources/components/opened-source.vue new file mode 100644 index 00000000..64e93818 --- /dev/null +++ b/src/modules/configuration/modules/lookups/modules/sources/components/opened-source.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/src/modules/configuration/modules/lookups/modules/sources/components/the-sources.vue b/src/modules/configuration/modules/lookups/modules/sources/components/the-sources.vue new file mode 100644 index 00000000..d1a62846 --- /dev/null +++ b/src/modules/configuration/modules/lookups/modules/sources/components/the-sources.vue @@ -0,0 +1,208 @@ + + + + + diff --git a/src/modules/configuration/modules/lookups/modules/sources/enums/TypesSources.enum.js b/src/modules/configuration/modules/lookups/modules/sources/enums/TypesSources.enum.js new file mode 100644 index 00000000..20927759 --- /dev/null +++ b/src/modules/configuration/modules/lookups/modules/sources/enums/TypesSources.enum.js @@ -0,0 +1,10 @@ +const TypesSources = Object.freeze({ + CALL: 'call', + CHAT: 'chat', + SOCIAL_MEDIA: 'social_media', + EMAIL: 'email', + API: 'api', + MANUAL: 'manual', +}); + +export default TypesSources; diff --git a/src/modules/configuration/modules/lookups/modules/sources/modules/filters/store/filters.js b/src/modules/configuration/modules/lookups/modules/sources/modules/filters/store/filters.js new file mode 100644 index 00000000..a5e65c90 --- /dev/null +++ b/src/modules/configuration/modules/lookups/modules/sources/modules/filters/store/filters.js @@ -0,0 +1,25 @@ +import FiltersStoreModule + from '@webitel/ui-sdk/src/modules/Filters/store/FiltersStoreModule'; + +const filtersList = [ + { + name: 'page', + value: 1, + defaultValue: 1, + }, + { + name: 'size', + value: 10, + defaultValue: 10, + }, + { + name: 'sort', + }, + { name: 'search' }, +]; + +const filters = new FiltersStoreModule() +.addFilter(filtersList) +.getModule(); + +export default filters; diff --git a/src/modules/configuration/modules/lookups/modules/sources/store/_internals/headers.js b/src/modules/configuration/modules/lookups/modules/sources/store/_internals/headers.js new file mode 100644 index 00000000..b888b22b --- /dev/null +++ b/src/modules/configuration/modules/lookups/modules/sources/store/_internals/headers.js @@ -0,0 +1,25 @@ +import { SortSymbols } from '@webitel/ui-sdk/src/scripts/sortQueryAdapters'; + +export default [ + { + value: 'name', + locale: 'reusable.name', + show: true, + field: 'name', + sort: SortSymbols.NONE, + }, + { + value: 'type', + locale: 'vocabulary.type', + show: true, + field: 'type', + sort: SortSymbols.NONE, + }, + { + value: 'description', + locale: 'vocabulary.description', + show: true, + field: 'description', + sort: SortSymbols.NONE, + }, +]; diff --git a/src/modules/configuration/modules/lookups/modules/sources/store/sources.js b/src/modules/configuration/modules/lookups/modules/sources/store/sources.js new file mode 100644 index 00000000..790d650d --- /dev/null +++ b/src/modules/configuration/modules/lookups/modules/sources/store/sources.js @@ -0,0 +1,49 @@ +import { + createApiStoreModule, + createBaseStoreModule, + createCardStoreModule, + createTableStoreModule, +} from '@webitel/ui-sdk/store'; +import SourcesAPI from '../api/sources.js'; +import headers from './_internals/headers'; +import filters from '../modules/filters/store/filters'; + +const resettableState = { + itemInstance: { + name: '', + description: '', + type: '', + }, +}; + +const api = createApiStoreModule({ + state: { + api: SourcesAPI, + }, +}); + +const table = createTableStoreModule({ + state: { + headers, + }, + modules: { + filters, + api, + }, +}); + +const card = createCardStoreModule({ + state: { _resettable: resettableState }, + modules: { + api, + }, +}); + +const sources = createBaseStoreModule({ + modules: { + table, + card, + }, +}); + +export default sources; diff --git a/src/modules/configuration/modules/lookups/store/lookups.js b/src/modules/configuration/modules/lookups/store/lookups.js index 2745b445..8aee8af3 100644 --- a/src/modules/configuration/modules/lookups/store/lookups.js +++ b/src/modules/configuration/modules/lookups/store/lookups.js @@ -1,9 +1,11 @@ import { createBaseStoreModule } from '@webitel/ui-sdk/src/store/new/index.js'; import slas from '../modules/slas/store/slas.js'; +import sources from '../modules/sources/store/sources.js'; const lookups = createBaseStoreModule({ modules: { slas, + sources, }, });