@webitel/ui-datalist 1.0.0 → 1.0.2

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 +122 -0
  4. package/src/filter-presets/components/_shared/input-fields/preset-description-field.vue +32 -0
  5. package/src/filter-presets/components/_shared/input-fields/preset-name-field.vue +28 -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 +190 -0
  19. package/src/filters/components/config/dynamic-filter-config-form-label.vue +49 -0
  20. package/src/filters/components/config/dynamic-filter-config-form.vue +139 -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 +60 -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 +73 -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 +100 -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 +49 -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 +83 -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
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <wt-popup
3
+ size="md"
4
+ @close="emit('close')"
5
+ >
6
+ <template #title>
7
+ {{
8
+ `${t('reusable.save')} ${t('webitelUI.filters.presets.preset').toLowerCase()}`
9
+ }}
10
+ </template>
11
+ <template #main>
12
+ <p class="overwrite-preset-popup-text">
13
+ {{ t('webitelUI.filters.presets.overwritePresetTitle') }}
14
+ </p>
15
+ <p class="overwrite-preset-popup-text">
16
+ {{ t('webitelUI.filters.presets.overwritePresetText') }}
17
+ </p>
18
+ </template>
19
+ <template #actions>
20
+ <wt-button
21
+ color="error"
22
+ :loading="isSaving"
23
+ @click="confirm"
24
+ >
25
+ {{ t('reusable.replace') }}
26
+ </wt-button>
27
+ <wt-button
28
+ color="secondary"
29
+ @click="emit('cancel')"
30
+ >{{ t('reusable.cancel') }}
31
+ </wt-button>
32
+ </template>
33
+ </wt-popup>
34
+ </template>
35
+
36
+ <script setup lang="ts">
37
+ import { WtPopup } from '@webitel/ui-sdk/src/components/index';
38
+ import { ref } from 'vue';
39
+ import { useI18n } from 'vue-i18n';
40
+
41
+ import { SubmitConfig } from './save-preset-popup.vue';
42
+
43
+ const emit = defineEmits<{
44
+ confirm: [SubmitConfig];
45
+ cancel: [];
46
+ close: [];
47
+ }>();
48
+
49
+ const { t } = useI18n();
50
+
51
+ const isSaving = ref(false);
52
+
53
+ const confirm = () => {
54
+ emit('confirm', {
55
+ onCompleted: () => (isSaving.value = false),
56
+ });
57
+ };
58
+ </script>
59
+
60
+ <style scoped lang="scss">
61
+ .overwrite-preset-popup-text {
62
+ text-align: center;
63
+ }
64
+ </style>
@@ -0,0 +1,139 @@
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="
11
+ showSaveForm /* v-if is used to re-mount component on each open/close so that each time component data re-inits*/
12
+ "
13
+ v-show="
14
+ !presetToOverwriteWith /* on 'overwrite preset' popup hide this popup, but don't reset it*/
15
+ "
16
+ :shown="true /* coz visibility is controlled by v-if*/"
17
+ :filters-manager="props.filtersManager"
18
+ :namespace="namespace"
19
+ @submit="handlePresetSubmit"
20
+ @close="showSaveForm = false"
21
+ />
22
+
23
+ <overwrite-preset-popup
24
+ v-if="presetToOverwriteWith"
25
+ @confirm="handlePresetOverwriteConfirmation"
26
+ @cancel="presetToOverwriteWith = null"
27
+ @close="presetToOverwriteWith = null"
28
+ />
29
+ </div>
30
+ </template>
31
+
32
+ <script lang="ts" setup>
33
+ import { WtIconAction } from '@webitel/ui-sdk/src/components/index';
34
+ import { IconAction } from '@webitel/ui-sdk/src/enums';
35
+ import { computed, inject, type Ref, ref } from 'vue';
36
+ import { useI18n } from 'vue-i18n';
37
+ import { EnginePresetQuery } from 'webitel-sdk';
38
+
39
+ import { IFiltersManager } from '../../../filters/index';
40
+ import {
41
+ addPreset,
42
+ getPresetList,
43
+ updatePreset,
44
+ } from '../../api/PresetQuery.api.ts';
45
+ import OverwritePresetPopup from './overwrite-preset-popup.vue';
46
+ import SavePresetPopup, { SubmitConfig } from './save-preset-popup.vue';
47
+
48
+ const props = defineProps<{
49
+ /**
50
+ * presets "section" namespace
51
+ */
52
+ namespace: string;
53
+ filtersManager: IFiltersManager;
54
+ }>();
55
+
56
+ const eventBus = inject('$eventBus');
57
+
58
+ const { t } = useI18n();
59
+
60
+ /**
61
+ * disable "save" btn if there's nothing to save
62
+ * */
63
+ const disableAction = computed(() => {
64
+ return !props.filtersManager.getAllKeys().length;
65
+ });
66
+
67
+ /**
68
+ * visibility flag
69
+ * */
70
+ const showSaveForm = ref(false);
71
+
72
+ /**
73
+ * if preset with the same name already exists, this will be suggested to set to that preset
74
+ */
75
+ const presetToOverwriteWith: Ref<EnginePresetQuery | null> = ref(null);
76
+
77
+ const handlePresetSubmit = async (
78
+ preset: EnginePresetQuery,
79
+ { onCompleted }: SubmitConfig,
80
+ ) => {
81
+ try {
82
+ await addPreset({ preset, namespace: props.namespace });
83
+
84
+ eventBus.$emit('notification', {
85
+ type: 'success',
86
+ text: t('systemNotifications.success.create', {
87
+ entity: t('webitelUI.filters.presets.preset'),
88
+ }),
89
+ });
90
+
91
+ showSaveForm.value = false;
92
+ } catch (err) {
93
+ if (err?.status === 409) {
94
+ presetToOverwriteWith.value = preset;
95
+ }
96
+ throw err;
97
+ } finally {
98
+ if (onCompleted) onCompleted();
99
+ }
100
+ };
101
+
102
+ const handlePresetOverwriteConfirmation = async ({
103
+ onCompleted,
104
+ }: SubmitConfig) => {
105
+ try {
106
+ const { items } = await getPresetList(
107
+ {
108
+ search: presetToOverwriteWith.value.name,
109
+ presetNamespace: props.namespace,
110
+ },
111
+ {
112
+ transformers: { useStarToSearch: false },
113
+ },
114
+ );
115
+ const { id: existingPresetId } = items[0];
116
+ await updatePreset({
117
+ id: existingPresetId,
118
+ item: {
119
+ ...presetToOverwriteWith.value,
120
+ },
121
+ namespace: props.namespace,
122
+ });
123
+
124
+ eventBus.$emit('notification', {
125
+ type: 'success',
126
+ text: t('systemNotifications.success.update', {
127
+ entity: t('webitelUI.filters.presets.preset'),
128
+ }),
129
+ });
130
+
131
+ presetToOverwriteWith.value = null;
132
+ showSaveForm.value = false;
133
+ } finally {
134
+ if (onCompleted) onCompleted();
135
+ }
136
+ };
137
+ </script>
138
+
139
+ <style lang="scss" scoped></style>
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <wt-popup
3
+ size="md"
4
+ @close="emit('close')"
5
+ >
6
+ <template #title>
7
+ {{
8
+ `${t('reusable.save')} ${t('webitelUI.filters.presets.preset').toLowerCase()}`
9
+ }}
10
+ </template>
11
+
12
+ <template #main>
13
+ <form
14
+ class="save-preset-form"
15
+ @submit.prevent="() => !v$.$invalid && save"
16
+ >
17
+ <preset-name-field
18
+ v-model:model-value="presetForm.name"
19
+ :v="v$.name"
20
+ />
21
+
22
+ <preset-description-field
23
+ v-model:model-value="presetForm.description"
24
+ />
25
+ </form>
26
+
27
+ <preset-filters-preview :filters="appliedFilters" />
28
+ </template>
29
+
30
+ <template #actions>
31
+ <wt-button
32
+ :disabled="v$.$invalid"
33
+ :loading="isSaving"
34
+ @click="save"
35
+ >
36
+ {{ t('reusable.save') }}
37
+ </wt-button>
38
+ <wt-button
39
+ color="secondary"
40
+ @click="emit('close')"
41
+ >
42
+ {{ t('reusable.cancel') }}
43
+ </wt-button>
44
+ </template>
45
+ </wt-popup>
46
+ </template>
47
+
48
+ <script setup lang="ts">
49
+ import { useVuelidate } from '@vuelidate/core';
50
+ import { required } from '@vuelidate/validators';
51
+ import { WtPopup } from '@webitel/ui-sdk/src/components/index';
52
+ import { computed, reactive, ref } from 'vue';
53
+ import { useI18n } from 'vue-i18n';
54
+ import { EnginePresetQuery } from 'webitel-sdk';
55
+
56
+ import type { IFiltersManager } from '../../../index';
57
+ import PresetDescriptionField from '../_shared/input-fields/preset-description-field.vue';
58
+ import PresetNameField from '../_shared/input-fields/preset-name-field.vue';
59
+ import PresetFiltersPreview from '../_shared/preset-filters-preview.vue';
60
+
61
+ export type SubmitConfig = {
62
+ onSuccess?: () => void;
63
+ onFailure?: (err: Error) => void;
64
+ onCompleted?: () => void;
65
+ };
66
+
67
+ const props = defineProps<{
68
+ filtersManager: IFiltersManager;
69
+ }>();
70
+
71
+ const emit = defineEmits<{
72
+ submit: [EnginePresetQuery, SubmitConfig?];
73
+ close: [];
74
+ }>();
75
+
76
+ const { t } = useI18n();
77
+
78
+ const isSaving = ref(false);
79
+
80
+ const presetForm = reactive({
81
+ name: '',
82
+ description: '',
83
+ });
84
+
85
+ const v$ = useVuelidate(
86
+ computed(() => {
87
+ return {
88
+ name: {
89
+ required,
90
+ },
91
+ };
92
+ }),
93
+ presetForm,
94
+ { $autoDirty: true },
95
+ );
96
+ v$.value.$touch();
97
+
98
+ const appliedFilters = computed(() => {
99
+ return props.filtersManager.getFiltersList();
100
+ });
101
+
102
+ const save = () => {
103
+ isSaving.value = true;
104
+
105
+ const preset: EnginePresetQuery = {
106
+ ...presetForm,
107
+ preset: {
108
+ 'filtersManager.toString': props.filtersManager.toString(),
109
+ },
110
+ };
111
+
112
+ emit('submit', preset, {
113
+ onCompleted: () => {
114
+ isSaving.value = false;
115
+ },
116
+ });
117
+ };
118
+ </script>
119
+
120
+ <style scoped lang="scss">
121
+ @use '@webitel/styleguide/scroll' as *;
122
+
123
+ .save-preset-form {
124
+ display: flex;
125
+ flex-direction: column;
126
+ gap: var(--spacing-sm);
127
+ margin-bottom: var(--spacing-sm);
128
+ }
129
+
130
+ .save-preset-filters-preview-wrapper {
131
+ @extend %wt-scrollbar;
132
+ margin-top: var(--spacing-sm);
133
+ max-height: 140px;
134
+ overflow-y: auto;
135
+ }
136
+ </style>
@@ -0,0 +1,5 @@
1
+ import ApplyPresetAction from './components/apply-preset/apply-preset-action.vue';
2
+ import SavePresetAction from './components/save-preset/save-preset-action.vue';
3
+ import { createFilterPresetsStore } from './stores/createFilterPresetsStore.ts';
4
+
5
+ export { ApplyPresetAction, createFilterPresetsStore, SavePresetAction };
@@ -0,0 +1,17 @@
1
+ import { EnginePresetQuery } from 'webitel-sdk';
2
+
3
+ import { createTableStore } from '../../table/createTableStore.store.ts';
4
+ import PresetQueryAPI from '../api/PresetQuery.api.ts';
5
+ import { headers } from './headers/headers.ts';
6
+
7
+ export const createFilterPresetsStore = (namespace = 'presets') => {
8
+ const presetsNamespace = namespace.endsWith('presets')
9
+ ? namespace
10
+ : `${namespace}/presets`;
11
+
12
+ return createTableStore<EnginePresetQuery>(presetsNamespace, {
13
+ apiModule: PresetQueryAPI,
14
+ headers,
15
+ disablePersistence: true,
16
+ });
17
+ };
@@ -0,0 +1,24 @@
1
+ import { WtTableHeader } from 'src/components/wt-table/types/WtTable.d.ts';
2
+
3
+ export const headers: WtTableHeader[] = [
4
+ {
5
+ value: 'id',
6
+ field: 'id',
7
+ show: true,
8
+ },
9
+ {
10
+ value: 'name',
11
+ field: 'name',
12
+ show: true,
13
+ },
14
+ {
15
+ value: 'preset',
16
+ field: 'preset',
17
+ show: true,
18
+ },
19
+ {
20
+ value: 'description',
21
+ field: 'description',
22
+ show: true,
23
+ },
24
+ ];
@@ -0,0 +1,30 @@
1
+ import type {
2
+ FilterConfig,
3
+ FilterInitParams,
4
+ FilterLabel,
5
+ FilterName,
6
+ FilterValue,
7
+ IFilter,
8
+ } from '../types/Filter';
9
+
10
+ export class Filter implements IFilter {
11
+ readonly name: FilterName;
12
+ label: FilterLabel;
13
+ value: FilterValue;
14
+
15
+ constructor(
16
+ { name, value, label }: FilterInitParams,
17
+ public payload: object | undefined,
18
+ public config: FilterConfig,
19
+ ) {
20
+ this.name = name;
21
+ this.value = value;
22
+ this.label = label;
23
+ }
24
+
25
+ set({ value, label }: { value?: FilterValue; label?: FilterLabel }): IFilter {
26
+ this.value = value;
27
+ this.label = label;
28
+ return this;
29
+ }
30
+ }
@@ -0,0 +1,34 @@
1
+ import { FilterValue } from '../types/Filter';
2
+ import type { FilterStorageOptions } from './FilterStorageOptions';
3
+
4
+ export class BrowserFilterStorage implements FilterStorageOptions {
5
+ constructor(private prefix: string) {}
6
+
7
+ get(name: string): FilterValue {
8
+ const value = localStorage.getItem(`filters:${this.prefix}:${name}`);
9
+ return value ? JSON.parse(value) : undefined;
10
+ }
11
+
12
+ set(name: string, value: FilterValue): void {
13
+ localStorage.setItem(
14
+ `filters:${this.prefix}:${name}`,
15
+ JSON.stringify(value),
16
+ );
17
+ }
18
+ }
19
+
20
+ export class QueryFilterStorage implements FilterStorageOptions {
21
+ constructor(private prefix: string) {}
22
+
23
+ get(name: string): FilterValue {
24
+ const url = new URL(window.location.href);
25
+ const value = url.searchParams.get(`${this.prefix}:${name}`);
26
+ return value ? JSON.parse(value) : undefined;
27
+ }
28
+
29
+ set(name: string, value: FilterValue): void {
30
+ const url = new URL(window.location.href);
31
+ url.searchParams.set(`${this.prefix}:${name}`, JSON.stringify(value));
32
+ window.history.pushState({}, '', url.toString());
33
+ }
34
+ }
@@ -0,0 +1,6 @@
1
+ import { FilterValue } from '../types/Filter';
2
+
3
+ export interface FilterStorageOptions {
4
+ get: (name: string) => FilterValue;
5
+ set: (name: string, value: FilterValue) => void;
6
+ }
@@ -0,0 +1,190 @@
1
+ import { isEmpty } from '@webitel/ui-sdk/src/scripts';
2
+
3
+ import {
4
+ filterLabelToSnapshotKey,
5
+ filterNameFromSnapshotKey,
6
+ filterValuePropFromSnapshotKey,
7
+ filterValueToSnapshotKey,
8
+ } from '../scripts/utils.ts';
9
+ import type {
10
+ FilterConfig,
11
+ FilterData,
12
+ FilterInitParams,
13
+ FilterLabel,
14
+ FilterName,
15
+ FilterValue,
16
+ IFilter,
17
+ } from '../types/Filter';
18
+ import { FiltersManagerConfig, IFiltersManager } from '../types/FiltersManager';
19
+ import { Filter } from './Filter.ts';
20
+
21
+ class FiltersManager implements IFiltersManager {
22
+ filters = new Map<FilterName, IFilter>();
23
+
24
+ constructor(private config?: FiltersManagerConfig) {}
25
+
26
+ hasFilter(name: FilterName): boolean {
27
+ return this.filters.has(name);
28
+ }
29
+
30
+ getFilter(name: FilterName): IFilter {
31
+ return this.filters.get(name);
32
+ }
33
+
34
+ addFilter(
35
+ filterInitParams: FilterInitParams,
36
+ payload?: object,
37
+ config?: FilterConfig,
38
+ ): IFilter {
39
+ const filter = new Filter(filterInitParams, payload, config || this.config);
40
+ this.filters.set(filterInitParams.name, filter);
41
+ return filter;
42
+ }
43
+
44
+ updateFilter({
45
+ name,
46
+ value,
47
+ label,
48
+ }: {
49
+ name: FilterName;
50
+ value?: FilterValue;
51
+ label?: FilterLabel;
52
+ }): IFilter {
53
+ const filter = this.filters.get(name);
54
+ filter.set({ value, label });
55
+ return filter;
56
+ }
57
+
58
+ deleteFilter(name: FilterName): IFilter {
59
+ const filter = this.filters.get(name);
60
+ this.filters.delete(name);
61
+ return filter;
62
+ }
63
+
64
+ getAllValues(): { [name: FilterName]: FilterValue } {
65
+ const filters = [...this.filters.values()].reduce((acc, filter) => {
66
+ acc[filter.name] = filter.value;
67
+ return acc;
68
+ }, {});
69
+
70
+ return filters;
71
+ }
72
+
73
+ toString(): string {
74
+ const filtersData = [...this.filters.values()].reduce(
75
+ (acc, { name, label, value }) => {
76
+ if (isEmpty(value) && value == null) return acc;
77
+
78
+ acc[filterValueToSnapshotKey(name)] = value;
79
+
80
+ if (label) {
81
+ acc[filterLabelToSnapshotKey(name)] = label;
82
+ }
83
+
84
+ return acc;
85
+ },
86
+ {},
87
+ );
88
+
89
+ return JSON.stringify(filtersData);
90
+ }
91
+
92
+ fromString(snapshotStr: string): void {
93
+ const snapshot = JSON.parse(snapshotStr);
94
+
95
+ /*
96
+ first, make transition object from snapshot,
97
+ because filter should bw always added with value
98
+ */
99
+ const filtersData: { FilterName: FilterData } = Object.entries(
100
+ snapshot,
101
+ ).reduce(
102
+ (filtersAcc, [snapshotKey, snapshotValue]) => {
103
+ const name = filterNameFromSnapshotKey(snapshotKey);
104
+ const valueProp = filterValuePropFromSnapshotKey(snapshotKey);
105
+
106
+ if (filtersAcc[name]) {
107
+ filtersAcc[name][valueProp] = snapshotValue;
108
+ } else {
109
+ filtersAcc[name] = {
110
+ [valueProp]: snapshotValue,
111
+ };
112
+ }
113
+
114
+ return filtersAcc;
115
+ },
116
+ {} as { FilterName: FilterData },
117
+ );
118
+
119
+ Object.entries(filtersData).forEach(([name, filterData]) => {
120
+ this.addFilter({ ...filterData, name });
121
+ });
122
+ }
123
+
124
+ getAllKeys(): FilterName[] {
125
+ return [...this.filters.keys()];
126
+ }
127
+
128
+ getFiltersList({
129
+ include,
130
+ exclude,
131
+ }: {
132
+ include?: FilterName[];
133
+ exclude?: FilterName[];
134
+ } = {}): IFilter[] {
135
+ const useInclude = !isEmpty(include);
136
+ const useExclude = !isEmpty(exclude) && !useInclude;
137
+
138
+ if (!useInclude && !useExclude) {
139
+ return [...this.filters.values()];
140
+ }
141
+
142
+ return [...this.filters.values()].filter(({ name }) => {
143
+ if (useInclude) {
144
+ return include.includes(name);
145
+ } else if (useExclude) {
146
+ return !exclude.includes(name);
147
+ }
148
+
149
+ return true;
150
+ });
151
+ }
152
+
153
+ reset({
154
+ include,
155
+ exclude,
156
+ }: {
157
+ include?: FilterName[];
158
+ exclude?: FilterName[];
159
+ } = {}): void {
160
+ const useInclude = !isEmpty(include);
161
+ const useExclude = !isEmpty(exclude) && !useInclude;
162
+
163
+ if (!useInclude && !useExclude) {
164
+ this.filters.clear();
165
+ return;
166
+ }
167
+
168
+ if (useInclude) {
169
+ include.forEach((name) => {
170
+ this.filters.delete(name);
171
+ });
172
+ return;
173
+ }
174
+
175
+ if (useExclude) {
176
+ this.filters.forEach((_, filterName) => {
177
+ if (!exclude.includes(filterName)) {
178
+ this.filters.delete(filterName);
179
+ }
180
+ });
181
+ return;
182
+ }
183
+ }
184
+ }
185
+
186
+ export const createFiltersManager = (
187
+ config?: FiltersManagerConfig,
188
+ ): IFiltersManager => {
189
+ return new FiltersManager(config);
190
+ };
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <wt-input
3
+ :value="model"
4
+ :label="t('webitelUI.filters.filterLabel')"
5
+ :v="v$.model"
6
+ @input="model = $event"
7
+ />
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { useVuelidate } from '@vuelidate/core';
12
+ import { maxLength } from '@vuelidate/validators';
13
+ import WtInput from '@webitel/ui-sdk/src/components/wt-input/wt-input.vue';
14
+ import { computed, watch } from 'vue';
15
+ import { useI18n } from 'vue-i18n';
16
+
17
+ const MAX_STRING_LENGTH = 50;
18
+
19
+ const model = defineModel<string>();
20
+
21
+ interface Emits {
22
+ 'update:invalid': [boolean];
23
+ }
24
+ const emit = defineEmits<Emits>();
25
+
26
+ const { t } = useI18n();
27
+
28
+ const v$ = useVuelidate(
29
+ computed(() => ({
30
+ model: {
31
+ // maybe make maxLength value by props
32
+ maxLength: maxLength(MAX_STRING_LENGTH),
33
+ },
34
+ })),
35
+ { model },
36
+ { $autoDirty: true },
37
+ );
38
+ v$.value.$touch();
39
+
40
+ watch(
41
+ () => v$.value.$invalid,
42
+ (invalid) => {
43
+ emit('update:invalid', invalid);
44
+ },
45
+ { immediate: true },
46
+ );
47
+ </script>
48
+
49
+ <style scoped lang="scss"></style>