@webitel/ui-sdk 24.12.171 → 24.12.173

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 (141) hide show
  1. package/dist/ui-sdk.css +1 -1
  2. package/dist/ui-sdk.js +2630 -2624
  3. package/dist/ui-sdk.umd.cjs +15 -15
  4. package/package.json +7 -4
  5. package/src/api/transformers/index.js +9 -6
  6. package/src/api/transformers/skipIf/skipIf.ts +6 -0
  7. package/src/components/wt-icon-action/iconMappings.js +2 -0
  8. package/src/components/wt-icon-action/wt-icon-action.vue +2 -0
  9. package/src/components/wt-label/_variables.scss +2 -0
  10. package/src/components/wt-label/wt-label.vue +1 -0
  11. package/src/enums/IconAction/IconAction.enum.js +2 -0
  12. package/src/locale/en/en.js +40 -0
  13. package/src/locale/ru/ru.js +40 -1
  14. package/src/locale/ua/ua.js +40 -1
  15. package/src/mixins/validationMixin/useValidation.js +3 -0
  16. package/src/modules/Filters/v2/filter-presets/api/PresetQuery.api.ts +123 -0
  17. package/src/modules/Filters/v2/filter-presets/components/_shared/input-fields/preset-description-field.vue +36 -0
  18. package/src/modules/Filters/v2/filter-presets/components/_shared/input-fields/preset-name-field.vue +31 -0
  19. package/src/modules/Filters/v2/filter-presets/components/_shared/preset-filters-preview.vue +52 -0
  20. package/src/modules/Filters/v2/filter-presets/components/apply-preset/apply-preset-action.vue +226 -0
  21. package/src/modules/Filters/v2/filter-presets/components/apply-preset/preset-preview.vue +197 -0
  22. package/src/modules/Filters/v2/filter-presets/components/save-preset/overwrite-preset-popup.vue +62 -0
  23. package/src/modules/Filters/v2/filter-presets/components/save-preset/save-preset-action.vue +123 -0
  24. package/src/modules/Filters/v2/filter-presets/components/save-preset/save-preset-popup.vue +130 -0
  25. package/src/modules/Filters/v2/filter-presets/index.ts +10 -0
  26. package/src/modules/Filters/v2/filter-presets/stores/createFilterPresetsStore.ts +14 -0
  27. package/src/modules/Filters/v2/filter-presets/stores/headers/headers.ts +24 -0
  28. package/src/modules/Filters/v2/filters/components/dynamic-filter-panel-wrapper.vue +32 -10
  29. package/src/modules/Filters/v2/filters/components/{values → filter-options}/_shared/has-options/has-option-filter-value-field.vue +1 -1
  30. package/src/modules/Filters/v2/filters/components/{values → filter-options}/amd-result/amd-result-filter-value-field.vue +1 -1
  31. package/src/modules/Filters/v2/filters/components/{values → filter-options}/cause/cause-filter-value-field.vue +1 -1
  32. package/src/modules/Filters/v2/filters/components/{values → filter-options}/direction/direction-filter-value-field.vue +1 -1
  33. package/src/modules/Filters/v2/filters/components/{values → filter-options}/has-attachment/has-attachment-filter-value-preview.vue +1 -1
  34. package/src/modules/Filters/v2/filters/components/{values/index.js → filter-options/index.ts} +87 -0
  35. package/src/modules/Filters/v2/filters/components/{values → filter-options}/tag/tag-filter-value-field.vue +1 -1
  36. package/src/modules/Filters/v2/filters/enums/FilterOption.ts +43 -0
  37. package/src/modules/Filters/v2/filters/{components/enums → enums}/amd-result-options.ts +1 -1
  38. package/src/modules/Filters/v2/filters/{components/enums → enums}/boolean-options.ts +1 -1
  39. package/src/modules/Filters/v2/filters/{components/enums → enums}/hangup-cause-options.ts +1 -1
  40. package/src/modules/Filters/v2/filters/{components/enums → enums}/tag-options.ts +1 -1
  41. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/_shared/composables/booleanFilterToolkit.ts +0 -0
  42. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/_shared/composables/useFromToSecToPreviewTime.ts +0 -0
  43. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/_shared/date-time-filter/date-time-filter-value-field.vue +0 -0
  44. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/_shared/durations/duration-filter-value-field.vue +0 -0
  45. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/_shared/types/BooleanFilter.ts +0 -0
  46. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/actual-reaction-time/actual-reaction-time-filter-value-field.vue +0 -0
  47. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/actual-reaction-time/actual-reaction-time-filter-value-preview.vue +0 -0
  48. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/actual-resolution-time/actual-resolution-time-filter-value-field.vue +0 -0
  49. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/actual-resolution-time/actual-resolution-time-filter-value-preview.vue +0 -0
  50. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/agent/agent-filter-value-field.vue +0 -0
  51. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/agent/agent-filter-value-preview.vue +0 -0
  52. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/agent/config.js +0 -0
  53. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/amd-result/amd-result-filter-value-preview.vue +0 -0
  54. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/assignee/assignee-filter-value-field.vue +0 -0
  55. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/assignee/assignee-filter-value-preview.vue +0 -0
  56. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/assignee/config.js +0 -0
  57. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/author/author-filter-value-field.vue +0 -0
  58. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/author/author-filter-value-preview.vue +0 -0
  59. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/author/config.js +0 -0
  60. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/cause/cause-filter-value-preview.vue +0 -0
  61. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/close-reason-groups-case/close-reason-groups-case-filter-value-field.vue +0 -0
  62. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/close-reason-groups-case/close-reason-groups-case-filter-value-preview.vue +0 -0
  63. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/close-reason-groups-case/config.js +0 -0
  64. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/contact/config.js +0 -0
  65. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/contact/contact-filter-value-field.vue +0 -0
  66. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/contact/contact-filter-value-preview.vue +0 -0
  67. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/contact-group/config.js +0 -0
  68. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/contact-group/contact-group-filter-value-field.vue +0 -0
  69. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/contact-group/contact-group-filter-value-preview.vue +0 -0
  70. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/created-at-from/created-at-from-filter-value-field.vue +0 -0
  71. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/created-at-from/created-at-from-filter-value-preview.vue +0 -0
  72. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/created-at-to/created-at-to-filter-value-field.vue +0 -0
  73. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/created-at-to/created-at-to-filter-value-preview.vue +0 -0
  74. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/direction/direction-filter-value-preview.vue +0 -0
  75. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/gateway/config.js +0 -0
  76. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/gateway/gateway-filter-value-field.vue +0 -0
  77. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/gateway/gateway-filter-value-preview.vue +0 -0
  78. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/grantee/config.js +0 -0
  79. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/grantee/grantee-filter-value-field.vue +0 -0
  80. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/grantee/grantee-filter-value-preview.vue +0 -0
  81. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/has-attachment/has-attachment-filter-value-field.vue +0 -0
  82. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/has-file/has-file-filter-value-field.vue +0 -0
  83. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/has-file/has-file-filter-value-preview.vue +0 -0
  84. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/has-rating/has-rating-filter-value-field.vue +0 -0
  85. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/has-rating/has-rating-filter-value-preview.vue +0 -0
  86. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/has-transcription/has-transcription-filter-value-field.vue +0 -0
  87. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/has-transcription/has-transcription-filter-value-preview.vue +0 -0
  88. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/impacted/config.js +0 -0
  89. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/impacted/impacted-filter-value-field.vue +0 -0
  90. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/impacted/impacted-filter-value-preview.vue +0 -0
  91. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/priority-case/config.js +0 -0
  92. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/priority-case/priority-case-filter-value-field.vue +0 -0
  93. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/priority-case/priority-case-filter-value-preview.vue +0 -0
  94. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/queue/config.js +0 -0
  95. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/queue/queue-filter-value-field.vue +0 -0
  96. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/queue/queue-filter-value-preview.vue +0 -0
  97. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/rated-by/config.js +0 -0
  98. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/rated-by/rated-by-filter-value-field.vue +0 -0
  99. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/rated-by/rated-by-filter-value-preview.vue +0 -0
  100. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/rating/rating-from-to-filter-value-field.vue +0 -0
  101. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/rating/rating-from-to-filter-value-preview.vue +0 -0
  102. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/reaction-time/reaction-time-filter-value-field.vue +0 -0
  103. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/reaction-time/reaction-time-filter-value-preview.vue +0 -0
  104. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/reporter/config.js +0 -0
  105. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/reporter/reporter-filter-value-field.vue +0 -0
  106. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/reporter/reporter-filter-value-preview.vue +0 -0
  107. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/resolution-time/resolution-time-filter-value-field.vue +0 -0
  108. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/resolution-time/resolution-time-filter-value-preview.vue +0 -0
  109. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/score/score-from-to-filter-value-field.vue +0 -0
  110. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/score/score-from-to-filter-value-preview.vue +0 -0
  111. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/service-case/config.js +0 -0
  112. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/service-case/service-case-filter-value-field.vue +0 -0
  113. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/service-case/service-case-filter-value-preview.vue +0 -0
  114. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/sla/config.js +0 -0
  115. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/sla/sla-filter-value-field.vue +0 -0
  116. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/sla/sla-filter-value-preview.vue +0 -0
  117. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/sla-condition/config.js +0 -0
  118. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/sla-condition/sla-condition-filter-value-field.vue +0 -0
  119. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/sla-condition/sla-condition-filter-value-preview.vue +0 -0
  120. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/source-case/config.js +0 -0
  121. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/source-case/source-case-filter-value-field.vue +0 -0
  122. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/source-case/source-case-filter-value-preview.vue +0 -0
  123. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/status-case/config.js +0 -0
  124. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/status-case/status-case-filter-value-field.vue +0 -0
  125. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/status-case/status-case-filter-value-preview.vue +0 -0
  126. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/tag/tag-filter-value-preview.vue +0 -0
  127. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/talk-duration/TalkDurationFilter.d.ts +0 -0
  128. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/talk-duration/talk-duration-filter-value-field.vue +0 -0
  129. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/talk-duration/talk-duration-filter-value-preview.vue +0 -0
  130. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/team/config.js +0 -0
  131. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/team/team-filter-value-field.vue +0 -0
  132. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/team/team-filter-value-preview.vue +0 -0
  133. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/total-duration/TotalDurationFilter.d.ts +0 -0
  134. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/total-duration/total-duration-filter-value-field.vue +0 -0
  135. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/total-duration/total-duration-filter-value-preview.vue +0 -0
  136. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/user/config.js +0 -0
  137. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/user/user-filter-value-field.vue +0 -0
  138. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/user/user-filter-value-preview.vue +0 -0
  139. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/variable/variable-filter-value-field.vue +0 -0
  140. /package/src/modules/Filters/v2/filters/components/{values → filter-options}/variable/variable-filter-value-preview.vue +0 -0
  141. /package/src/modules/Filters/v2/filters/{components/enums → enums}/direction-options.ts +0 -0
@@ -0,0 +1,226 @@
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
+ {{ `${t('vocabulary.apply')} ${t('webitelUI.filters.presets.preset').toLowerCase()}` }}
14
+ </template>
15
+
16
+ <template #main>
17
+ <section class="apply-preset-main-content">
18
+ <wt-search-bar
19
+ :value="search"
20
+ @search="search = $event"
21
+ />
22
+
23
+ <wt-empty
24
+ v-show="showEmpty"
25
+ :image="imageEmpty"
26
+ :text="textEmpty"
27
+ />
28
+
29
+ <section class="available-presets-list">
30
+ <preset-preview
31
+ v-for="(preset) of dataList"
32
+ :key="preset.id"
33
+ collapsed
34
+ :is-selected="preset === selectedPreset"
35
+ :preset="preset"
36
+ @preset:select="selectedPreset = preset"
37
+ @preset:update="updatePreset"
38
+ @preset:delete="() => deletePreset(preset)"
39
+ />
40
+ </section>
41
+ <!-- TODO: infinite scroll -->
42
+ <!-- <wt-intersection-observer-->
43
+ <!-- :loading="isLoading"-->
44
+ <!-- :next="false"-->
45
+ <!-- @next="updatePage(page + 1)"-->
46
+ <!-- />-->
47
+ </section>
48
+ </template>
49
+
50
+ <template #actions>
51
+ <wt-button
52
+ :disabled="!selectedPreset"
53
+ @click="applySelectedPreset"
54
+ >
55
+ {{ t('vocabulary.apply') }}
56
+ </wt-button>
57
+ <wt-button
58
+ color="secondary"
59
+ @click="showPresetsList = false"
60
+ >
61
+ {{ t('reusable.cancel') }}
62
+ </wt-button>
63
+ </template>
64
+ </wt-popup>
65
+ </div>
66
+ </template>
67
+
68
+ <script lang="ts" setup>
69
+ import {type StoreDefinition, storeToRefs } from "pinia";
70
+ import {computed, inject, ref, watch, onUnmounted} from "vue";
71
+ import {useI18n} from "vue-i18n";
72
+
73
+ import {WtButton, WtEmpty, WtPopup, WtSearchBar, WtIconAction} from "../../../../../../components/index";
74
+ import {useTableEmpty} from "../../../../../TableComponentModule/composables/useTableEmpty";
75
+ import { IconAction } from "../../../../../../enums";
76
+ import PresetQueryAPI from '../../api/PresetQuery.api.ts';
77
+ import PresetPreview from "./preset-preview.vue";
78
+ import {EnginePresetQuery} from "webitel-sdk";
79
+
80
+ const props = defineProps<{
81
+ /**
82
+ * presets "section" namespace
83
+ */
84
+ namespace: string;
85
+ usePresetsStore: StoreDefinition;
86
+ }>();
87
+
88
+ const emit = defineEmits<{
89
+ apply: [string];
90
+ }>();
91
+
92
+ const eventBus = inject('$eventBus');
93
+
94
+ const {t} = useI18n();
95
+
96
+ const showPresetsList = ref(false);
97
+
98
+ const presetsStore = props.usePresetsStore();
99
+ const {
100
+ dataList,
101
+ error,
102
+ isLoading,
103
+ filtersManager,
104
+ page,
105
+ } = storeToRefs(presetsStore);
106
+
107
+ const {
108
+ loadDataList,
109
+ initialize,
110
+ updateSize,
111
+ deleteEls,
112
+ } = presetsStore;
113
+
114
+ updateSize(1000);
115
+ filtersManager.value.addFilter({name: 'presetNamespace', value: props.namespace});
116
+
117
+ const search = computed({
118
+ get: () => {
119
+ return filtersManager.value.getFilter('search')?.value || '';
120
+ },
121
+ set: (value) => {
122
+ filtersManager.value.addFilter({name: 'search', value});
123
+ }
124
+ });
125
+
126
+ const {
127
+ showEmpty,
128
+ image: imageEmpty,
129
+ text: textEmpty,
130
+ } = useTableEmpty({
131
+ dataList,
132
+ isLoading,
133
+ error,
134
+ filters: computed(() => {
135
+ return {
136
+ search: search.value,
137
+ };
138
+ }),
139
+ });
140
+
141
+ watch(showPresetsList, () => {
142
+ initialize();
143
+
144
+ watch(showPresetsList, (value) => {
145
+ if (value) {
146
+ search.value = '';
147
+ /* search.value reset causes re-fetch as filter change, so
148
+ loadDataList() is commented.
149
+ TODO: implement ability to set filters "silently" and refactor this code */
150
+ // loadDataList();
151
+ }
152
+ });
153
+ }, {once: true});
154
+
155
+ const selectedPreset = ref();
156
+
157
+ const applySelectedPreset = () => {
158
+ const filtersSnapshot = selectedPreset.value.preset['filtersManager.toString'];
159
+ emit('apply', filtersSnapshot);
160
+
161
+ selectedPreset.value = null;
162
+ showPresetsList.value = false;
163
+ };
164
+
165
+ const updatePreset = async ({preset, onSuccess, onFailure}) => {
166
+ try {
167
+ await PresetQueryAPI.update({
168
+ item: { ...preset },
169
+ id: preset.id,
170
+ namespace: props.namespace,
171
+ });
172
+ eventBus.$emit('notification', {
173
+ type: 'success',
174
+ text: t('systemNotifications.success.update', {
175
+ entity: t('webitelUI.filters.presets.preset'),
176
+ }),
177
+ });
178
+ onSuccess();
179
+ return loadDataList();
180
+ } catch (err) {
181
+ onFailure(err);
182
+ throw err;
183
+ }
184
+ };
185
+
186
+ const deletePreset = async (preset: EnginePresetQuery) => {
187
+ await deleteEls([preset]);
188
+ eventBus.$emit('notification', {
189
+ type: 'success',
190
+ text: t('systemNotifications.success.delete', {
191
+ entity: t('webitelUI.filters.presets.preset'),
192
+ }),
193
+ });
194
+ };
195
+ </script>
196
+
197
+ <style lang="scss" scoped>
198
+ .apply-preset-action .wt-popup {
199
+ :deep(.wt-popup__popup) {
200
+ height: 480px;
201
+ }
202
+ }
203
+
204
+ .apply-preset-main-content {
205
+ display: flex;
206
+ height: 100%;
207
+ flex-direction: column;
208
+ gap: var(--spacing-xs);
209
+
210
+ .wt-empty {
211
+ flex-grow: 1;
212
+ max-width: 100%;
213
+ }
214
+ }
215
+
216
+ .available-presets-list {
217
+ @extend %wt-scrollbar;
218
+
219
+ display: flex;
220
+ overflow-y: auto;
221
+ flex-direction: column;
222
+ max-height: 400px;
223
+ gap: var(--spacing-xs);
224
+ scrollbar-gutter: stable;
225
+ }
226
+ </style>
@@ -0,0 +1,197 @@
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
65
+ :filters="presetFilters"
66
+ />
67
+
68
+ <preset-description-field
69
+ v-model:model-value="editDraft.description"
70
+ :preview-mode="!editMode"
71
+ />
72
+ </div>
73
+ </template>
74
+ </wt-expansion-panel>
75
+ </article>
76
+ </template>
77
+
78
+ <script lang="ts" setup>
79
+ import {EnginePresetQuery} from "webitel-sdk";
80
+ import {computed, ref} from "vue";
81
+ import {useVuelidate} from "@vuelidate/core";
82
+ import {required} from "@vuelidate/validators";
83
+ import {AxiosError} from "axios";
84
+ import {WtExpansionPanel, WtIconAction, WtRadio} from "../../../../../../components/index";
85
+ import {createFiltersManager} from "../../../filters/index";
86
+ import PresetFiltersPreview from "../_shared/preset-filters-preview.vue";
87
+ import PresetNameField from "../_shared/input-fields/preset-name-field.vue";
88
+ import PresetDescriptionField from "../_shared/input-fields/preset-description-field.vue";
89
+
90
+ type Props = {
91
+ preset: EnginePresetQuery;
92
+ isSelected: boolean;
93
+ collapsed: boolean;
94
+ }
95
+
96
+ const props = defineProps<Props>();
97
+
98
+ const emit = defineEmits<{
99
+ 'preset:select': [EnginePresetQuery];
100
+ 'preset:update': [{ preset: EnginePresetQuery, onSuccess: () => void, onFailure: (err: AxiosError) => void }];
101
+ 'preset:delete': [EnginePresetQuery];
102
+ }>();
103
+
104
+ const presetFilters = computed(() => {
105
+ const snapshot = props.preset?.preset?.['filtersManager.toString'];
106
+ if (!snapshot) return [];
107
+
108
+ const filtersManager = createFiltersManager();
109
+ filtersManager.fromString(snapshot);
110
+
111
+ return filtersManager.getFiltersList();
112
+ });
113
+
114
+ const editMode = ref(false);
115
+
116
+ /**
117
+ * updating request in progress flag
118
+ * */
119
+ const editing = ref(false);
120
+
121
+ const nameAlreadyExistsError = ref(false);
122
+
123
+ const editDraft = ref({
124
+ name: '',
125
+ description: '',
126
+ });
127
+
128
+ const fillDraft = () => {
129
+ editDraft.value = {
130
+ name: props.preset.name,
131
+ description: props.preset.description,
132
+ };
133
+ };
134
+
135
+ fillDraft();
136
+
137
+ const v$ = useVuelidate(computed(() => {
138
+ return {
139
+ name: {
140
+ required,
141
+ nameAlreadyInUse: () => !nameAlreadyExistsError.value,
142
+ },
143
+ };
144
+ }), editDraft, {$autoDirty: true});
145
+ v$.value.$touch();
146
+
147
+ const startEdit = ({open: openExpansion}) => {
148
+ openExpansion();
149
+ editMode.value = true;
150
+ };
151
+
152
+ const clearEdit = () => {
153
+ editing.value = false;
154
+ editMode.value = false;
155
+ };
156
+
157
+ const submitEdit = () => {
158
+ const preset: EnginePresetQuery = {
159
+ ...props.preset,
160
+ ...editDraft.value,
161
+ };
162
+ const onFailure = (err: AxiosError) => {
163
+ if (err.status === 409) {
164
+ nameAlreadyExistsError.value = true;
165
+ }
166
+ editing.value = false;
167
+ };
168
+
169
+ emit('preset:update', {
170
+ preset,
171
+ onSuccess: clearEdit,
172
+ onFailure,
173
+ });
174
+ };
175
+ </script>
176
+
177
+ <style lang="scss" scoped>
178
+ .preset-preview-title-wrapper {
179
+ display: flex;
180
+ min-width: 0;
181
+ gap: var(--spacing-xs);
182
+ }
183
+
184
+ .preset-preview-name {
185
+ overflow: hidden;
186
+ flex: 1 1 0;
187
+ white-space: nowrap;
188
+ text-overflow: ellipsis;
189
+ }
190
+
191
+ .preset-preview-content {
192
+ display: flex;
193
+ flex-direction: column;
194
+ gap: var(--spacing-xs);
195
+ padding: var(--spacing-xs);
196
+ }
197
+ </style>
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <wt-popup
3
+ size="md"
4
+ @close="emit('close')"
5
+ >
6
+ <template #title>
7
+ {{ `${t('reusable.save')} ${t('webitelUI.filters.presets.preset').toLowerCase()}` }}
8
+ </template>
9
+ <template #main>
10
+ <p class="overwrite-preset-popup-text">
11
+ {{ t('webitelUI.filters.presets.overwritePresetTitle') }}
12
+ </p>
13
+ <p class="overwrite-preset-popup-text">
14
+ {{ t('webitelUI.filters.presets.overwritePresetText') }}
15
+ </p>
16
+ </template>
17
+ <template #actions>
18
+ <wt-button
19
+ color="error"
20
+ :loading="isSaving"
21
+ @click="confirm"
22
+ >
23
+ {{ t('reusable.replace') }}
24
+ </wt-button>
25
+ <wt-button
26
+ color="secondary"
27
+ @click="emit('cancel')"
28
+ >{{ t('reusable.cancel') }}
29
+ </wt-button>
30
+ </template>
31
+ </wt-popup>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import { ref } from 'vue';
36
+ import { useI18n } from 'vue-i18n';
37
+
38
+ import { WtPopup } from '../../../../../../components/index';
39
+ import { SubmitConfig } from './save-preset-popup.vue';
40
+
41
+ const emit = defineEmits<{
42
+ confirm: [SubmitConfig];
43
+ cancel: [];
44
+ close: [];
45
+ }>();
46
+
47
+ const { t } = useI18n();
48
+
49
+ const isSaving = ref(false);
50
+
51
+ const confirm = () => {
52
+ emit('confirm', {
53
+ onCompleted: () => (isSaving.value = false),
54
+ });
55
+ };
56
+ </script>
57
+
58
+ <style scoped lang="scss">
59
+ .overwrite-preset-popup-text {
60
+ text-align: center;
61
+ }
62
+ </style>
@@ -0,0 +1,123 @@
1
+ <template>
2
+ <div class="save-preset-action">
3
+ <wt-icon-action
4
+ :action="IconAction.SAVE_PRESET"
5
+ :disabled="disableAction"
6
+ @click="showSaveForm = true"
7
+ />
8
+
9
+ <save-preset-popup
10
+ v-if="showSaveForm /* v-if is used to re-mount component on each open/close so that each time component data re-inits*/"
11
+ v-show="!presetToOverwriteWith /* on 'overwrite preset' popup hide this popup, but don't reset it*/"
12
+ :shown="true /* coz visibility is controlled by v-if*/"
13
+ :filters-manager="props.filtersManager"
14
+ :namespace="namespace"
15
+ @submit="handlePresetSubmit"
16
+ @close="showSaveForm = false"
17
+ />
18
+
19
+ <overwrite-preset-popup
20
+ v-if="presetToOverwriteWith"
21
+ @confirm="handlePresetOverwriteConfirmation"
22
+ @cancel="presetToOverwriteWith = null"
23
+ @close="presetToOverwriteWith = null"
24
+ />
25
+ </div>
26
+ </template>
27
+
28
+ <script lang="ts" setup>
29
+ import {computed, inject, type Ref,ref} from 'vue';
30
+ import {useI18n} from "vue-i18n";
31
+ import {EnginePresetQuery} from "webitel-sdk";
32
+
33
+ import { WtIconAction } from '../../../../../../components/index';
34
+ import {IFiltersManager} from "../../../filters/index";
35
+ import {addPreset, getPresetList, updatePreset} from '../../api/PresetQuery.api.ts';
36
+ import OverwritePresetPopup from "./overwrite-preset-popup.vue";
37
+ import { IconAction } from "../../../../../../enums";
38
+ import SavePresetPopup, {SubmitConfig} from "./save-preset-popup.vue";
39
+
40
+ const props = defineProps<{
41
+ /**
42
+ * presets "section" namespace
43
+ */
44
+ namespace: string;
45
+ filtersManager: IFiltersManager;
46
+ }>();
47
+
48
+ const eventBus = inject('$eventBus');
49
+
50
+ const { t } = useI18n();
51
+
52
+ /**
53
+ * disable "save" btn if there's nothing to save
54
+ * */
55
+ const disableAction = computed(() => {
56
+ return !props.filtersManager.getAllKeys().length;
57
+ });
58
+
59
+ /**
60
+ * visibility flag
61
+ * */
62
+ const showSaveForm = ref(false);
63
+
64
+ /**
65
+ * if preset with the same name already exists, this will be suggested to set to that preset
66
+ */
67
+ const presetToOverwriteWith: Ref<EnginePresetQuery | null> = ref(null);
68
+
69
+ const handlePresetSubmit = async (preset: EnginePresetQuery, { onCompleted }: SubmitConfig) => {
70
+ try {
71
+ await addPreset({ preset, namespace: props.namespace });
72
+
73
+ eventBus.$emit('notification', {
74
+ type: 'success', text: t('systemNotifications.success.create', {
75
+ entity: t('webitelUI.filters.presets.preset'),
76
+ }),
77
+ });
78
+
79
+ showSaveForm.value = false;
80
+ } catch (err) {
81
+ if (err?.status === 409) {
82
+ presetToOverwriteWith.value = preset;
83
+ }
84
+ throw err;
85
+ } finally {
86
+ if (onCompleted) onCompleted();
87
+ }
88
+ };
89
+
90
+ const handlePresetOverwriteConfirmation = async ({ onCompleted }: SubmitConfig) => {
91
+ try {
92
+ const {items} = await getPresetList({
93
+ search: presetToOverwriteWith.value.name,
94
+ presetNamespace: props.namespace,
95
+ }, {
96
+ transformers: {useStarToSearch: false},
97
+ });
98
+ const {id: existingPresetId} = items[0];
99
+ await updatePreset({
100
+ id: existingPresetId,
101
+ item: {
102
+ ...presetToOverwriteWith.value,
103
+ },
104
+ namespace: props.namespace,
105
+ });
106
+
107
+ eventBus.$emit('notification', {
108
+ type: 'success', text: t('systemNotifications.success.update', {
109
+ entity: t('webitelUI.filters.presets.preset'),
110
+ }),
111
+ });
112
+
113
+ presetToOverwriteWith.value = null;
114
+ showSaveForm.value = false;
115
+ } finally {
116
+ if (onCompleted) onCompleted();
117
+ }
118
+ };
119
+ </script>
120
+
121
+ <style lang="scss" scoped>
122
+
123
+ </style>