@webitel/ui-datalist 1.0.0 → 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 (155) hide show
  1. package/Readme.md +19 -0
  2. package/package.json +3 -1
  3. package/src/filter-presets/api/PresetQuery.api.ts +123 -0
  4. package/src/filter-presets/components/_shared/input-fields/preset-description-field.vue +33 -0
  5. package/src/filter-presets/components/_shared/input-fields/preset-name-field.vue +29 -0
  6. package/src/filter-presets/components/_shared/preset-filters-preview.vue +45 -0
  7. package/src/filter-presets/components/apply-preset/apply-preset-action.vue +234 -0
  8. package/src/filter-presets/components/apply-preset/preset-preview.vue +210 -0
  9. package/src/filter-presets/components/save-preset/overwrite-preset-popup.vue +64 -0
  10. package/src/filter-presets/components/save-preset/save-preset-action.vue +139 -0
  11. package/src/filter-presets/components/save-preset/save-preset-popup.vue +136 -0
  12. package/src/filter-presets/index.ts +5 -0
  13. package/src/filter-presets/stores/createFilterPresetsStore.ts +17 -0
  14. package/src/filter-presets/stores/headers/headers.ts +24 -0
  15. package/src/filters/classes/Filter.ts +30 -0
  16. package/src/filters/classes/FilterStorage.ts +34 -0
  17. package/src/filters/classes/FilterStorageOptions.d.ts +6 -0
  18. package/src/filters/classes/FiltersManager.ts +189 -0
  19. package/src/filters/components/config/dynamic-filter-config-form-label.vue +50 -0
  20. package/src/filters/components/config/dynamic-filter-config-form.vue +140 -0
  21. package/src/filters/components/config/dynamic-filter-config-view.vue +40 -0
  22. package/src/filters/components/dynamic-filter-add-action.vue +54 -0
  23. package/src/filters/components/dynamic-filter-panel-wrapper.vue +57 -0
  24. package/src/filters/components/dynamic-filter-search.vue +61 -0
  25. package/src/filters/components/filter-options/_shared/composables/booleanFilterToolkit.ts +39 -0
  26. package/src/filters/components/filter-options/_shared/composables/useFromToSecToPreviewTime.ts +41 -0
  27. package/src/filters/components/filter-options/_shared/date-time-filter/date-time-filter-value-field.vue +56 -0
  28. package/src/filters/components/filter-options/_shared/durations/duration-filter-value-field.vue +84 -0
  29. package/src/filters/components/filter-options/_shared/has-options/has-option-filter-value-field.vue +38 -0
  30. package/src/filters/components/filter-options/_shared/types/BooleanFilter.ts +1 -0
  31. package/src/filters/components/filter-options/actual-reaction-time/actual-reaction-time-filter-value-field.vue +18 -0
  32. package/src/filters/components/filter-options/actual-reaction-time/actual-reaction-time-filter-value-preview.vue +44 -0
  33. package/src/filters/components/filter-options/actual-resolution-time/actual-resolution-time-filter-value-field.vue +18 -0
  34. package/src/filters/components/filter-options/actual-resolution-time/actual-resolution-time-filter-value-preview.vue +44 -0
  35. package/src/filters/components/filter-options/agent/agent-filter-value-field.vue +58 -0
  36. package/src/filters/components/filter-options/agent/agent-filter-value-preview.vue +31 -0
  37. package/src/filters/components/filter-options/agent/config.js +4 -0
  38. package/src/filters/components/filter-options/amd-result/amd-result-filter-value-field.vue +52 -0
  39. package/src/filters/components/filter-options/amd-result/amd-result-filter-value-preview.vue +18 -0
  40. package/src/filters/components/filter-options/assignee/assignee-filter-value-field.vue +74 -0
  41. package/src/filters/components/filter-options/assignee/assignee-filter-value-preview.vue +36 -0
  42. package/src/filters/components/filter-options/assignee/config.js +4 -0
  43. package/src/filters/components/filter-options/author/author-filter-value-field.vue +56 -0
  44. package/src/filters/components/filter-options/author/author-filter-value-preview.vue +31 -0
  45. package/src/filters/components/filter-options/author/config.js +4 -0
  46. package/src/filters/components/filter-options/cause/cause-filter-value-field.vue +52 -0
  47. package/src/filters/components/filter-options/cause/cause-filter-value-preview.vue +18 -0
  48. package/src/filters/components/filter-options/close-reason-groups-case/close-reason-groups-case-filter-value-field.vue +102 -0
  49. package/src/filters/components/filter-options/close-reason-groups-case/close-reason-groups-case-filter-value-preview.vue +34 -0
  50. package/src/filters/components/filter-options/close-reason-groups-case/config.js +7 -0
  51. package/src/filters/components/filter-options/contact/config.js +4 -0
  52. package/src/filters/components/filter-options/contact/contact-filter-value-field.vue +56 -0
  53. package/src/filters/components/filter-options/contact/contact-filter-value-preview.vue +31 -0
  54. package/src/filters/components/filter-options/contact-group/config.js +4 -0
  55. package/src/filters/components/filter-options/contact-group/contact-group-filter-value-field.vue +74 -0
  56. package/src/filters/components/filter-options/contact-group/contact-group-filter-value-preview.vue +38 -0
  57. package/src/filters/components/filter-options/created-at-from/created-at-from-filter-value-field.vue +24 -0
  58. package/src/filters/components/filter-options/created-at-from/created-at-from-filter-value-preview.vue +15 -0
  59. package/src/filters/components/filter-options/created-at-to/created-at-to-filter-value-field.vue +24 -0
  60. package/src/filters/components/filter-options/created-at-to/created-at-to-filter-value-preview.vue +15 -0
  61. package/src/filters/components/filter-options/direction/direction-filter-value-field.vue +51 -0
  62. package/src/filters/components/filter-options/direction/direction-filter-value-preview.vue +17 -0
  63. package/src/filters/components/filter-options/gateway/config.js +4 -0
  64. package/src/filters/components/filter-options/gateway/gateway-filter-value-field.vue +56 -0
  65. package/src/filters/components/filter-options/gateway/gateway-filter-value-preview.vue +31 -0
  66. package/src/filters/components/filter-options/grantee/config.js +4 -0
  67. package/src/filters/components/filter-options/grantee/grantee-filter-value-field.vue +56 -0
  68. package/src/filters/components/filter-options/grantee/grantee-filter-value-preview.vue +31 -0
  69. package/src/filters/components/filter-options/has-attachment/has-attachment-filter-value-field.vue +43 -0
  70. package/src/filters/components/filter-options/has-attachment/has-attachment-filter-value-preview.vue +24 -0
  71. package/src/filters/components/filter-options/has-file/has-file-filter-value-field.vue +33 -0
  72. package/src/filters/components/filter-options/has-file/has-file-filter-value-preview.vue +15 -0
  73. package/src/filters/components/filter-options/has-rating/has-rating-filter-value-field.vue +33 -0
  74. package/src/filters/components/filter-options/has-rating/has-rating-filter-value-preview.vue +15 -0
  75. package/src/filters/components/filter-options/has-transcription/has-transcription-filter-value-field.vue +33 -0
  76. package/src/filters/components/filter-options/has-transcription/has-transcription-filter-value-preview.vue +15 -0
  77. package/src/filters/components/filter-options/impacted/config.js +4 -0
  78. package/src/filters/components/filter-options/impacted/impacted-filter-value-field.vue +56 -0
  79. package/src/filters/components/filter-options/impacted/impacted-filter-value-preview.vue +31 -0
  80. package/src/filters/components/filter-options/index.ts +250 -0
  81. package/src/filters/components/filter-options/priority-case/config.js +4 -0
  82. package/src/filters/components/filter-options/priority-case/priority-case-filter-value-field.vue +57 -0
  83. package/src/filters/components/filter-options/priority-case/priority-case-filter-value-preview.vue +31 -0
  84. package/src/filters/components/filter-options/queue/config.js +4 -0
  85. package/src/filters/components/filter-options/queue/queue-filter-value-field.vue +56 -0
  86. package/src/filters/components/filter-options/queue/queue-filter-value-preview.vue +31 -0
  87. package/src/filters/components/filter-options/rated-by/config.js +4 -0
  88. package/src/filters/components/filter-options/rated-by/rated-by-filter-value-field.vue +56 -0
  89. package/src/filters/components/filter-options/rated-by/rated-by-filter-value-preview.vue +31 -0
  90. package/src/filters/components/filter-options/rating/rating-from-to-filter-value-field.vue +101 -0
  91. package/src/filters/components/filter-options/rating/rating-from-to-filter-value-preview.vue +39 -0
  92. package/src/filters/components/filter-options/reaction-time/reaction-time-filter-value-field.vue +18 -0
  93. package/src/filters/components/filter-options/reaction-time/reaction-time-filter-value-preview.vue +44 -0
  94. package/src/filters/components/filter-options/reporter/config.js +4 -0
  95. package/src/filters/components/filter-options/reporter/reporter-filter-value-field.vue +56 -0
  96. package/src/filters/components/filter-options/reporter/reporter-filter-value-preview.vue +31 -0
  97. package/src/filters/components/filter-options/resolution-time/resolution-time-filter-value-field.vue +18 -0
  98. package/src/filters/components/filter-options/resolution-time/resolution-time-filter-value-preview.vue +44 -0
  99. package/src/filters/components/filter-options/score/score-from-to-filter-value-field.vue +100 -0
  100. package/src/filters/components/filter-options/score/score-from-to-filter-value-preview.vue +39 -0
  101. package/src/filters/components/filter-options/service-case/config.js +6 -0
  102. package/src/filters/components/filter-options/service-case/service-case-filter-value-field.vue +82 -0
  103. package/src/filters/components/filter-options/service-case/service-case-filter-value-preview.vue +34 -0
  104. package/src/filters/components/filter-options/sla/config.js +4 -0
  105. package/src/filters/components/filter-options/sla/sla-filter-value-field.vue +57 -0
  106. package/src/filters/components/filter-options/sla/sla-filter-value-preview.vue +31 -0
  107. package/src/filters/components/filter-options/sla-condition/config.js +6 -0
  108. package/src/filters/components/filter-options/sla-condition/sla-condition-filter-value-field.vue +98 -0
  109. package/src/filters/components/filter-options/sla-condition/sla-condition-filter-value-preview.vue +37 -0
  110. package/src/filters/components/filter-options/source-case/config.js +4 -0
  111. package/src/filters/components/filter-options/source-case/source-case-filter-value-field.vue +57 -0
  112. package/src/filters/components/filter-options/source-case/source-case-filter-value-preview.vue +31 -0
  113. package/src/filters/components/filter-options/status-case/config.js +7 -0
  114. package/src/filters/components/filter-options/status-case/status-case-filter-value-field.vue +102 -0
  115. package/src/filters/components/filter-options/status-case/status-case-filter-value-preview.vue +37 -0
  116. package/src/filters/components/filter-options/tag/tag-filter-value-field.vue +52 -0
  117. package/src/filters/components/filter-options/tag/tag-filter-value-preview.vue +38 -0
  118. package/src/filters/components/filter-options/talk-duration/TalkDurationFilter.d.ts +4 -0
  119. package/src/filters/components/filter-options/talk-duration/talk-duration-filter-value-field.vue +19 -0
  120. package/src/filters/components/filter-options/talk-duration/talk-duration-filter-value-preview.vue +44 -0
  121. package/src/filters/components/filter-options/team/config.js +4 -0
  122. package/src/filters/components/filter-options/team/team-filter-value-field.vue +56 -0
  123. package/src/filters/components/filter-options/team/team-filter-value-preview.vue +31 -0
  124. package/src/filters/components/filter-options/total-duration/TotalDurationFilter.d.ts +4 -0
  125. package/src/filters/components/filter-options/total-duration/total-duration-filter-value-field.vue +19 -0
  126. package/src/filters/components/filter-options/total-duration/total-duration-filter-value-preview.vue +44 -0
  127. package/src/filters/components/filter-options/user/config.js +4 -0
  128. package/src/filters/components/filter-options/user/user-filter-value-field.vue +56 -0
  129. package/src/filters/components/filter-options/user/user-filter-value-preview.vue +31 -0
  130. package/src/filters/components/filter-options/variable/variable-filter-value-field.vue +50 -0
  131. package/src/filters/components/filter-options/variable/variable-filter-value-preview.vue +39 -0
  132. package/src/filters/components/preview/dynamic-filter-preview-info.vue +35 -0
  133. package/src/filters/components/preview/dynamic-filter-preview.vue +76 -0
  134. package/src/filters/components/table-filters-panel.vue +87 -0
  135. package/src/filters/createTableFiltersStore.ts +81 -0
  136. package/src/filters/enums/FilterOption.ts +43 -0
  137. package/src/filters/enums/amd-result-options.ts +38 -0
  138. package/src/filters/enums/boolean-options.ts +16 -0
  139. package/src/filters/enums/direction-options.ts +20 -0
  140. package/src/filters/enums/hangup-cause-options.ts +265 -0
  141. package/src/filters/enums/tag-options.ts +8 -0
  142. package/src/filters/index.ts +27 -0
  143. package/src/filters/scripts/utils.ts +31 -0
  144. package/src/filters/types/Filter.d.ts +46 -0
  145. package/src/filters/types/FiltersManager.d.ts +76 -0
  146. package/src/headers/createTableHeadersStore.ts +140 -0
  147. package/src/index.d.ts +0 -0
  148. package/src/index.ts +3 -0
  149. package/src/pagination/createTablePaginationStore.ts +64 -0
  150. package/src/persist/PersistedStorage.types.ts +51 -0
  151. package/src/persist/useLocalStoragePersistedStorage.ts +37 -0
  152. package/src/persist/usePersistedStorage.ts +151 -0
  153. package/src/persist/useRoutePersistedStorage.ts +41 -0
  154. package/src/table/createTableStore.store.ts +206 -0
  155. package/src/types/tableStore.types.ts +61 -0
package/Readme.md ADDED
@@ -0,0 +1,19 @@
1
+ # @webitel/ui-datalist
2
+
3
+ This package contains necessary tools for building datalists in Webitel frontend projects.
4
+
5
+ This package contains:
6
+
7
+ * table tools (store, components, etc)
8
+ * table filters (store, components, etc)
9
+
10
+ ## Versions
11
+
12
+ ### 1.0.x
13
+
14
+ #### Contents:
15
+
16
+ * separate table, pagination, headers, filters pinia stores
17
+ * filtersManager, filter components
18
+ * vue-based persistence tools for this storages data
19
+ * TS types for all the above
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-datalist",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Toolkit for building data lists in webitel ui system",
5
5
  "scripts": {
6
6
  "lint:fix": "eslint --fix ./src && prettier --write --check ./src",
@@ -27,6 +27,8 @@
27
27
  }
28
28
  },
29
29
  "files": [
30
+ "src/*",
31
+ "Readme.md",
30
32
  "CHANGELOG.md"
31
33
  ],
32
34
  "peerDependencies": {
@@ -0,0 +1,123 @@
1
+ import {
2
+ EngineCreatePresetQueryRequest,
3
+ EnginePresetQuery,
4
+ PresetQueryServiceApiFactory,
5
+ } from 'webitel-sdk';
6
+
7
+ import {
8
+ getDefaultGetListResponse,
9
+ getDefaultGetParams,
10
+ getDefaultInstance,
11
+ getDefaultOpenAPIConfig,
12
+ } from '../../../../../api/defaults/index';
13
+ import applyTransform, {
14
+ camelToSnake,
15
+ merge,
16
+ notify,
17
+ skipIf,
18
+ snakeToCamel,
19
+ starToSearch,
20
+ } from '../../../../../api/transformers/index';
21
+
22
+ const instance = getDefaultInstance();
23
+ const configuration = getDefaultOpenAPIConfig();
24
+
25
+ const service = PresetQueryServiceApiFactory(configuration, '', instance);
26
+
27
+ type GetPresetListRequestConfig = {
28
+ transformers: {
29
+ useStarToSearch?: boolean;
30
+ };
31
+ };
32
+
33
+ const getPresetList = async (params, config: GetPresetListRequestConfig) => {
34
+ const useStarToSearch = config?.transformers?.useStarToSearch ?? true;
35
+
36
+ const { page, size, search, sort, fields, presetNamespace, id } =
37
+ applyTransform(params, [
38
+ merge(getDefaultGetParams()),
39
+ (params) => (useStarToSearch ? starToSearch('search')(params) : params),
40
+ ]);
41
+
42
+ try {
43
+ const response = await service.searchPresetQuery(
44
+ page,
45
+ size,
46
+ search,
47
+ sort || '-created_at',
48
+ fields || ['id', 'name', 'preset', 'description'],
49
+ id,
50
+ );
51
+ const { items, next } = applyTransform(response.data, [
52
+ snakeToCamel(),
53
+ merge(getDefaultGetListResponse()),
54
+ ]);
55
+ return {
56
+ items: applyTransform(items, [
57
+ (items) =>
58
+ items.filter(({ preset }) => preset.namespace === presetNamespace),
59
+ ]),
60
+ next,
61
+ };
62
+ } catch (err) {
63
+ throw applyTransform(err, [notify]);
64
+ }
65
+ };
66
+
67
+ const addPreset = async ({
68
+ preset,
69
+ namespace,
70
+ }: {
71
+ preset: EngineCreatePresetQueryRequest;
72
+ namespace: string;
73
+ }): Promise<EnginePresetQuery> => {
74
+ const item = applyTransform(preset, [
75
+ camelToSnake(),
76
+ (item) => {
77
+ item.preset.namespace = namespace;
78
+ return item;
79
+ },
80
+ ]);
81
+ try {
82
+ const response = await service.createPresetQuery(item);
83
+ return applyTransform(response.data, [snakeToCamel()]);
84
+ } catch (err) {
85
+ throw applyTransform(err, [skipIf(notify, (err) => err.status === 409)]);
86
+ }
87
+ };
88
+
89
+ const updatePreset = async ({ item: itemInstance, id, namespace }) => {
90
+ const item = applyTransform(itemInstance, [
91
+ camelToSnake(),
92
+ (item) => {
93
+ item.preset.namespace = namespace;
94
+ return item;
95
+ },
96
+ ]);
97
+ try {
98
+ const response = await service.updatePresetQuery(id, item);
99
+ return applyTransform(response.data, [snakeToCamel()]);
100
+ } catch (err) {
101
+ throw applyTransform(err, [skipIf(notify, (err) => err.status === 409)]);
102
+ }
103
+ };
104
+
105
+ const deletePreset = async ({ id }) => {
106
+ try {
107
+ const response = await service.deletePresetQuery(id);
108
+ return applyTransform(response.data, []);
109
+ } catch (err) {
110
+ throw applyTransform(err, [notify]);
111
+ }
112
+ };
113
+
114
+ const PresetQueryAPI = {
115
+ getList: getPresetList,
116
+ add: addPreset,
117
+ update: updatePreset,
118
+ delete: deletePreset,
119
+ };
120
+
121
+ export { addPreset, deletePreset, getPresetList, updatePreset };
122
+
123
+ export default PresetQueryAPI;
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <div v-if="props.previewMode && model">
3
+ <wt-label>
4
+ {{ t('vocabulary.description') }}
5
+ </wt-label>
6
+ <p>
7
+ {{ model }}
8
+ </p>
9
+ </div>
10
+
11
+ <wt-textarea
12
+ v-if="!props.previewMode"
13
+ :value="model"
14
+ :label="t('vocabulary.description')"
15
+ @input="model = $event"
16
+ />
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { useI18n } from 'vue-i18n';
21
+
22
+ import { WtLabel,WtTextarea } from '../../../../../../../components/index';
23
+
24
+ const model = defineModel<string>();
25
+
26
+ const props = defineProps<{
27
+ previewMode?: boolean;
28
+ }>();
29
+
30
+ const { t } = useI18n();
31
+ </script>
32
+
33
+ <style scoped lang="scss"></style>
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <wt-input
3
+ v-model="model"
4
+ :v="props.v"
5
+ :label="t('reusable.name')"
6
+ required
7
+ />
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { Validation } from '@vuelidate/core';
12
+ import { useI18n } from 'vue-i18n';
13
+
14
+ import { WtInput } from '../../../../../../../components/index';
15
+
16
+ type ModelValue = string;
17
+
18
+ const model = defineModel<ModelValue>();
19
+
20
+ type Props = {
21
+ v: Validation;
22
+ };
23
+
24
+ const props = defineProps<Props>();
25
+
26
+ const { t } = useI18n();
27
+ </script>
28
+
29
+ <style scoped lang="scss"></style>
@@ -0,0 +1,45 @@
1
+ <template>
2
+ <div class="save-preset-filters-preview">
3
+ <wt-label>
4
+ {{ t('webitelUI.filters.filterName') }}
5
+ </wt-label>
6
+ <dynamic-filter-panel-wrapper size="sm">
7
+ <template #filters>
8
+ <dynamic-filter-preview
9
+ v-for="filter of props.filters"
10
+ :key="filter.name"
11
+ :filter="filter"
12
+ dummy
13
+ >
14
+ <template #info>
15
+ <component
16
+ :is="FilterOptionToPreviewComponentMap[filter.name]"
17
+ :value="filter.value"
18
+ >
19
+ </component>
20
+ </template>
21
+ </dynamic-filter-preview>
22
+ </template>
23
+ </dynamic-filter-panel-wrapper>
24
+ </div>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { useI18n } from 'vue-i18n';
29
+
30
+ import { WtLabel } from '../../../../../../components/index';
31
+ import type { IFilter } from '../../../filters';
32
+ import DynamicFilterPanelWrapper from '../../../filters/components/dynamic-filter-panel-wrapper.vue';
33
+ import { FilterOptionToPreviewComponentMap } from '../../../filters/components/filter-options';
34
+ import DynamicFilterPreview from '../../../filters/components/preview/dynamic-filter-preview.vue';
35
+
36
+ type Props = {
37
+ filters: IFilter[];
38
+ };
39
+
40
+ const props = defineProps<Props>();
41
+
42
+ const { t } = useI18n();
43
+ </script>
44
+
45
+ <style scoped lang="scss"></style>
@@ -0,0 +1,234 @@
1
+ <template>
2
+ <div class="apply-preset-action">
3
+ <wt-icon-action
4
+ :action="IconAction.APPLY_PRESET"
5
+ @click="showPresetsList = true"
6
+ />
7
+ <wt-popup
8
+ :shown="showPresetsList"
9
+ size="md"
10
+ @close="showPresetsList = false"
11
+ >
12
+ <template #title>
13
+ {{
14
+ `${t('vocabulary.apply')} ${t('webitelUI.filters.presets.preset').toLowerCase()}`
15
+ }}
16
+ </template>
17
+
18
+ <template #main>
19
+ <section class="apply-preset-main-content">
20
+ <wt-search-bar
21
+ :value="search"
22
+ @search="search = $event"
23
+ />
24
+
25
+ <wt-empty
26
+ v-show="showEmpty"
27
+ :image="imageEmpty"
28
+ :text="textEmpty"
29
+ />
30
+
31
+ <section class="available-presets-list">
32
+ <preset-preview
33
+ v-for="preset of dataList"
34
+ :key="preset.id"
35
+ collapsed
36
+ :is-selected="preset === selectedPreset"
37
+ :preset="preset"
38
+ @preset:select="selectedPreset = preset"
39
+ @preset:update="updatePreset"
40
+ @preset:delete="() => deletePreset(preset)"
41
+ />
42
+ </section>
43
+ <!-- TODO: infinite scroll -->
44
+ <!-- <wt-intersection-observer-->
45
+ <!-- :loading="isLoading"-->
46
+ <!-- :next="false"-->
47
+ <!-- @next="updatePage(page + 1)"-->
48
+ <!-- />-->
49
+ </section>
50
+ </template>
51
+
52
+ <template #actions>
53
+ <wt-button
54
+ :disabled="!selectedPreset"
55
+ @click="applySelectedPreset"
56
+ >
57
+ {{ t('vocabulary.apply') }}
58
+ </wt-button>
59
+ <wt-button
60
+ color="secondary"
61
+ @click="showPresetsList = false"
62
+ >
63
+ {{ t('reusable.cancel') }}
64
+ </wt-button>
65
+ </template>
66
+ </wt-popup>
67
+ </div>
68
+ </template>
69
+
70
+ <script lang="ts" setup>
71
+ import { type StoreDefinition, storeToRefs } from 'pinia';
72
+ import { computed, inject, onUnmounted,ref, watch } from 'vue';
73
+ import { useI18n } from 'vue-i18n';
74
+ import { EnginePresetQuery } from 'webitel-sdk';
75
+
76
+ import {
77
+ WtButton,
78
+ WtEmpty,
79
+ WtIconAction,
80
+ WtPopup,
81
+ WtSearchBar,
82
+ } from '../../../../../../components/index';
83
+ import { IconAction } from '../../../../../../enums';
84
+ import { useTableEmpty } from '../../../../../TableComponentModule/composables/useTableEmpty';
85
+ import PresetQueryAPI from '../../api/PresetQuery.api.ts';
86
+ import PresetPreview from './preset-preview.vue';
87
+
88
+ const props = defineProps<{
89
+ /**
90
+ * presets "section" namespace
91
+ */
92
+ namespace: string;
93
+ usePresetsStore: StoreDefinition;
94
+ }>();
95
+
96
+ const emit = defineEmits<{
97
+ apply: [string];
98
+ }>();
99
+
100
+ const eventBus = inject('$eventBus');
101
+
102
+ const { t } = useI18n();
103
+
104
+ const showPresetsList = ref(false);
105
+
106
+ const presetsStore = props.usePresetsStore();
107
+ const { dataList, error, isLoading, filtersManager, page } =
108
+ storeToRefs(presetsStore);
109
+
110
+ const { loadDataList, initialize, updateSize, deleteEls } = presetsStore;
111
+
112
+ updateSize(1000);
113
+ filtersManager.value.addFilter({
114
+ name: 'presetNamespace',
115
+ value: props.namespace,
116
+ });
117
+
118
+ const search = computed({
119
+ get: () => {
120
+ return filtersManager.value.getFilter('search')?.value || '';
121
+ },
122
+ set: (value) => {
123
+ filtersManager.value.addFilter({ name: 'search', value });
124
+ },
125
+ });
126
+
127
+ const {
128
+ showEmpty,
129
+ image: imageEmpty,
130
+ text: textEmpty,
131
+ } = useTableEmpty({
132
+ dataList,
133
+ isLoading,
134
+ error,
135
+ filters: computed(() => {
136
+ return {
137
+ search: search.value,
138
+ };
139
+ }),
140
+ });
141
+
142
+ watch(
143
+ showPresetsList,
144
+ () => {
145
+ initialize();
146
+
147
+ watch(showPresetsList, (value) => {
148
+ if (value) {
149
+ search.value = '';
150
+ /* search.value reset causes re-fetch as filter change, so
151
+ loadDataList() is commented.
152
+ TODO: implement ability to set filters "silently" and refactor this code */
153
+ // loadDataList();
154
+ }
155
+ });
156
+ },
157
+ { once: true },
158
+ );
159
+
160
+ const selectedPreset = ref();
161
+
162
+ const applySelectedPreset = () => {
163
+ const filtersSnapshot =
164
+ selectedPreset.value.preset['filtersManager.toString'];
165
+ emit('apply', filtersSnapshot);
166
+
167
+ selectedPreset.value = null;
168
+ showPresetsList.value = false;
169
+ };
170
+
171
+ const updatePreset = async ({ preset, onSuccess, onFailure }) => {
172
+ try {
173
+ await PresetQueryAPI.update({
174
+ item: { ...preset },
175
+ id: preset.id,
176
+ namespace: preset.namespace,
177
+ });
178
+ eventBus.$emit('notification', {
179
+ type: 'success',
180
+ text: t('systemNotifications.success.update', {
181
+ entity: t('webitelUI.filters.presets.preset'),
182
+ }),
183
+ });
184
+ onSuccess();
185
+ return loadDataList();
186
+ } catch (err) {
187
+ onFailure(err);
188
+ throw err;
189
+ }
190
+ };
191
+
192
+ const deletePreset = async (preset: EnginePresetQuery) => {
193
+ await deleteEls([preset]);
194
+ eventBus.$emit('notification', {
195
+ type: 'success',
196
+ text: t('systemNotifications.success.delete', {
197
+ entity: t('webitelUI.filters.presets.preset'),
198
+ }),
199
+ });
200
+ };
201
+ </script>
202
+
203
+ <style lang="scss" scoped>
204
+ @use '@webitel/styleguide/scroll' as *;
205
+
206
+ .apply-preset-action .wt-popup {
207
+ :deep(.wt-popup__popup) {
208
+ height: 480px;
209
+ }
210
+ }
211
+
212
+ .apply-preset-main-content {
213
+ display: flex;
214
+ flex-direction: column;
215
+ gap: var(--spacing-xs);
216
+ height: 100%;
217
+
218
+ .wt-empty {
219
+ flex-grow: 1;
220
+ max-width: 100%;
221
+ }
222
+ }
223
+
224
+ .available-presets-list {
225
+ @extend %wt-scrollbar;
226
+
227
+ display: flex;
228
+ flex-direction: column;
229
+ gap: var(--spacing-xs);
230
+ max-height: 400px;
231
+ overflow-y: auto;
232
+ scrollbar-gutter: stable;
233
+ }
234
+ </style>
@@ -0,0 +1,210 @@
1
+ <template>
2
+ <article class="preset-preview">
3
+ <wt-expansion-panel
4
+ :collapsed="collapsed"
5
+ @closed="clearEdit"
6
+ >
7
+ <template #title>
8
+ <header class="preset-preview-title-wrapper">
9
+ <!--
10
+ this <div> is used only for @click.stop to be set, so that expansion panel toggle won't trigger.
11
+ it would be great to use @click.stop to wt-ratio,
12
+ but with @vue/compat build and direct input it won't be working like that :(
13
+ -->
14
+ <div @click.stop>
15
+ <wt-radio
16
+ :selected="isSelected"
17
+ :value="true"
18
+ @input="emit('preset:select', preset)"
19
+ />
20
+ </div>
21
+ <p
22
+ :title="preset.name"
23
+ class="preset-preview-name"
24
+ >
25
+ {{ preset.name }}
26
+ </p>
27
+ </header>
28
+ </template>
29
+ <template #actions="{ open }">
30
+ <wt-icon-action
31
+ v-if="!editMode"
32
+ action="edit"
33
+ @click.stop="startEdit({ open })"
34
+ />
35
+
36
+ <wt-icon-action
37
+ v-if="editMode"
38
+ action="delete"
39
+ @click.stop="emit('preset:delete', preset)"
40
+ />
41
+
42
+ <wt-icon-action
43
+ v-if="editMode"
44
+ :disabled="v$.$invalid"
45
+ action="save"
46
+ @click.stop="submitEdit"
47
+ />
48
+
49
+ <wt-icon-action
50
+ v-if="editMode"
51
+ action="cancel"
52
+ @click.stop="clearEdit"
53
+ />
54
+ </template>
55
+ <template #default>
56
+ <div class="preset-preview-content">
57
+ <preset-name-field
58
+ v-if="editMode"
59
+ v-model:model-value="editDraft.name"
60
+ :v="v$.name"
61
+ @update:model-value="nameAlreadyExistsError = false"
62
+ />
63
+
64
+ <preset-filters-preview :filters="presetFilters" />
65
+
66
+ <preset-description-field
67
+ v-model:model-value="editDraft.description"
68
+ :preview-mode="!editMode"
69
+ />
70
+ </div>
71
+ </template>
72
+ </wt-expansion-panel>
73
+ </article>
74
+ </template>
75
+
76
+ <script lang="ts" setup>
77
+ import { useVuelidate } from '@vuelidate/core';
78
+ import { required } from '@vuelidate/validators';
79
+ import { AxiosError } from 'axios';
80
+ import { computed, ref } from 'vue';
81
+ import { EnginePresetQuery } from 'webitel-sdk';
82
+
83
+ import {
84
+ WtExpansionPanel,
85
+ WtIconAction,
86
+ WtRadio,
87
+ } from '../../../../../../components/index';
88
+ import { createFiltersManager } from '../../../filters/index';
89
+ import PresetDescriptionField from '../_shared/input-fields/preset-description-field.vue';
90
+ import PresetNameField from '../_shared/input-fields/preset-name-field.vue';
91
+ import PresetFiltersPreview from '../_shared/preset-filters-preview.vue';
92
+
93
+ type Props = {
94
+ preset: EnginePresetQuery;
95
+ isSelected: boolean;
96
+ collapsed: boolean;
97
+ };
98
+
99
+ const props = defineProps<Props>();
100
+
101
+ const emit = defineEmits<{
102
+ 'preset:select': [EnginePresetQuery];
103
+ 'preset:update': [
104
+ {
105
+ preset: EnginePresetQuery;
106
+ onSuccess: () => void;
107
+ onFailure: (err: AxiosError) => void;
108
+ },
109
+ ];
110
+ 'preset:delete': [EnginePresetQuery];
111
+ }>();
112
+
113
+ const presetFilters = computed(() => {
114
+ const snapshot = props.preset?.preset?.['filtersManager.toString'];
115
+ if (!snapshot) return [];
116
+
117
+ const filtersManager = createFiltersManager();
118
+ filtersManager.fromString(snapshot);
119
+
120
+ return filtersManager.getFiltersList();
121
+ });
122
+
123
+ const editMode = ref(false);
124
+
125
+ /**
126
+ * updating request in progress flag
127
+ * */
128
+ const editing = ref(false);
129
+
130
+ const nameAlreadyExistsError = ref(false);
131
+
132
+ const editDraft = ref({
133
+ name: '',
134
+ description: '',
135
+ });
136
+
137
+ const fillDraft = () => {
138
+ editDraft.value = {
139
+ name: props.preset.name,
140
+ description: props.preset.description,
141
+ };
142
+ };
143
+
144
+ fillDraft();
145
+
146
+ const v$ = useVuelidate(
147
+ computed(() => {
148
+ return {
149
+ name: {
150
+ required,
151
+ nameAlreadyInUse: () => !nameAlreadyExistsError.value,
152
+ },
153
+ };
154
+ }),
155
+ editDraft,
156
+ { $autoDirty: true },
157
+ );
158
+ v$.value.$touch();
159
+
160
+ const startEdit = ({ open: openExpansion }) => {
161
+ openExpansion();
162
+ editMode.value = true;
163
+ };
164
+
165
+ const clearEdit = () => {
166
+ editing.value = false;
167
+ editMode.value = false;
168
+ };
169
+
170
+ const submitEdit = () => {
171
+ const preset: EnginePresetQuery = {
172
+ ...props.preset,
173
+ ...editDraft.value,
174
+ };
175
+ const onFailure = (err: AxiosError) => {
176
+ if (err.status === 409) {
177
+ nameAlreadyExistsError.value = true;
178
+ }
179
+ editing.value = false;
180
+ };
181
+
182
+ emit('preset:update', {
183
+ preset,
184
+ onSuccess: clearEdit,
185
+ onFailure,
186
+ });
187
+ };
188
+ </script>
189
+
190
+ <style lang="scss" scoped>
191
+ .preset-preview-title-wrapper {
192
+ display: flex;
193
+ gap: var(--spacing-xs);
194
+ min-width: 0;
195
+ }
196
+
197
+ .preset-preview-name {
198
+ flex: 1 1 0;
199
+ overflow: hidden;
200
+ text-overflow: ellipsis;
201
+ white-space: nowrap;
202
+ }
203
+
204
+ .preset-preview-content {
205
+ display: flex;
206
+ flex-direction: column;
207
+ gap: var(--spacing-xs);
208
+ padding: var(--spacing-xs);
209
+ }
210
+ </style>