@webitel/ui-datalist 26.8.7 → 26.8.9

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 (137) hide show
  1. package/package.json +1 -1
  2. package/src/modules/filter-presets/components/apply-preset/apply-preset-action.vue +16 -4
  3. package/src/modules/filter-presets/components/save-preset/save-preset-action.vue +18 -4
  4. package/src/modules/filters/components/column/column-filter-preview.vue +70 -0
  5. package/src/modules/filters/components/column/column-filter.vue +77 -0
  6. package/src/modules/filters/components/config/dynamic-view/dynamic-filter-config-form-value-input.vue +2 -0
  7. package/src/modules/filters/components/config/dynamic-view/dynamic-filter-config-form.vue +43 -5
  8. package/src/modules/filters/components/config/static-view/static-filter-field.vue +6 -21
  9. package/src/modules/filters/components/filters-actions-menu.vue +177 -0
  10. package/src/modules/filters/components/preview/dynamic-filter-preview.vue +5 -60
  11. package/src/modules/filters/components/table-filters-panel.vue +6 -28
  12. package/src/modules/filters/components/types/Filter.types.ts +5 -0
  13. package/src/modules/filters/composables/useColumnFilter.ts +116 -0
  14. package/src/modules/filters/composables/useFilterValueChange.ts +58 -0
  15. package/src/modules/filters/composables/useFilterValuePreview.ts +87 -0
  16. package/src/modules/filters/composables/useSelectedFilters.ts +64 -0
  17. package/src/modules/filters/index.ts +4 -0
  18. package/src/modules/filters/modules/filterConfig/components/_custom/type-extension-filter-value-field.vue +9 -5
  19. package/src/modules/filters/modules/filterConfig/components/_shared/date-time-filter/date-time-filter-value-field.vue +7 -4
  20. package/src/modules/filters/modules/filterConfig/components/_shared/date-time-filter/date-time-options/date-time-options-filter-value-field.vue +7 -4
  21. package/src/modules/filters/modules/filterConfig/components/_shared/durations/duration-filter-value-field.vue +7 -4
  22. package/src/modules/filters/modules/filterConfig/components/_shared/string-filter/string-filter-value-field.vue +6 -3
  23. package/src/modules/filters/modules/filterConfig/components/agent/agent-filter-value-field.vue +6 -3
  24. package/src/modules/filters/modules/filterConfig/components/amd-result/amd-result-filter-value-field.vue +6 -3
  25. package/src/modules/filters/modules/filterConfig/components/call-direction/call-direction-filter-value-field.vue +6 -3
  26. package/src/modules/filters/modules/filterConfig/components/case-assignee/case-assignee-filter-value-field.vue +4 -4
  27. package/src/modules/filters/modules/filterConfig/components/case-author/case-author-filter-value-field.vue +6 -3
  28. package/src/modules/filters/modules/filterConfig/components/case-close-reason-groups/case-close-reason-groups-filter-value-field.vue +7 -4
  29. package/src/modules/filters/modules/filterConfig/components/case-impacted/case-impacted-filter-value-field.vue +6 -3
  30. package/src/modules/filters/modules/filterConfig/components/case-priority/case-priority-filter-value-field.vue +6 -3
  31. package/src/modules/filters/modules/filterConfig/components/case-reporter/case-reporter-filter-value-field.vue +6 -3
  32. package/src/modules/filters/modules/filterConfig/components/case-service/case-service-filter-value-field.vue +1 -32
  33. package/src/modules/filters/modules/filterConfig/components/case-sla/case-sla-filter-value-field.vue +6 -3
  34. package/src/modules/filters/modules/filterConfig/components/case-sla-condition/case-sla-condition-filter-value-field.vue +7 -4
  35. package/src/modules/filters/modules/filterConfig/components/case-source/case-source-filter-value-field.vue +6 -3
  36. package/src/modules/filters/modules/filterConfig/components/case-status/case-status-filter-value-field.vue +7 -4
  37. package/src/modules/filters/modules/filterConfig/components/contact/contact-filter-value-field.vue +6 -3
  38. package/src/modules/filters/modules/filterConfig/components/contact-owner/contact-owner-filter-value-field.vue +3 -3
  39. package/src/modules/filters/modules/filterConfig/components/gateway/gateway-filter-value-field.vue +6 -3
  40. package/src/modules/filters/modules/filterConfig/components/grantee/grantee-filter-value-field.vue +6 -3
  41. package/src/modules/filters/modules/filterConfig/components/hangup-cause/hangup-cause-filter-value-field.vue +6 -3
  42. package/src/modules/filters/modules/filterConfig/components/has-attachment/has-attachment-filter-value-field.vue +6 -3
  43. package/src/modules/filters/modules/filterConfig/components/rated-by/rated-by-filter-value-field.vue +6 -3
  44. package/src/modules/filters/modules/filterConfig/components/rating/rating-from-to-filter-value-field.vue +7 -4
  45. package/src/modules/filters/modules/filterConfig/components/score/score-from-to-filter-value-field.vue +4 -4
  46. package/src/modules/filters/modules/filterConfig/components/tag/tag-filter-value-field.vue +6 -3
  47. package/src/modules/filters/modules/filterConfig/components/user/user-filter-value-field.vue +6 -3
  48. package/src/modules/filters/modules/filterConfig/components/variable/variable-filter-value-field.vue +6 -3
  49. package/src/modules/table/createTableStore.store.ts +24 -3
  50. package/src/modules/types/tableStore.types.ts +18 -1
  51. package/types/.tsbuildinfo +1 -1
  52. package/types/modules/filter-presets/components/apply-preset/apply-preset-action.vue.d.ts +15 -1
  53. package/types/modules/filter-presets/components/save-preset/save-preset-action.vue.d.ts +17 -1
  54. package/types/modules/filter-presets/stores/createFilterPresetsStore.d.ts +2 -35
  55. package/types/modules/filters/components/column/column-filter-preview.vue.d.ts +21 -0
  56. package/types/modules/filters/components/column/column-filter.vue.d.ts +36 -0
  57. package/types/modules/filters/components/config/dynamic-view/dynamic-filter-config-form-label.vue.d.ts +2 -2
  58. package/types/modules/filters/components/config/dynamic-view/dynamic-filter-config-form-value-input.vue.d.ts +3 -2
  59. package/types/modules/filters/components/config/dynamic-view/dynamic-filter-config-form.vue.d.ts +13 -1
  60. package/types/modules/filters/components/config/static-view/static-filter-field.vue.d.ts +2 -2
  61. package/types/modules/filters/components/filters-actions-menu.vue.d.ts +29 -0
  62. package/types/modules/filters/components/table-filters-panel.vue.d.ts +6 -6
  63. package/types/modules/filters/components/types/Filter.types.d.ts +4 -0
  64. package/types/modules/filters/composables/useColumnFilter.d.ts +27 -0
  65. package/types/modules/filters/composables/useFilterValueChange.d.ts +23 -0
  66. package/types/modules/filters/composables/useFilterValuePreview.d.ts +21 -0
  67. package/types/modules/filters/composables/useSelectedFilters.d.ts +22 -0
  68. package/types/modules/filters/index.d.ts +3 -1
  69. package/types/modules/filters/modules/filterConfig/components/_custom/type-extension-filter-value-field.vue.d.ts +3 -2
  70. package/types/modules/filters/modules/filterConfig/components/_shared/date-time-filter/date-time-filter-value-field.vue.d.ts +8 -4
  71. package/types/modules/filters/modules/filterConfig/components/_shared/date-time-filter/date-time-options/date-time-options-filter-value-field.vue.d.ts +8 -4
  72. package/types/modules/filters/modules/filterConfig/components/_shared/durations/duration-filter-value-field.vue.d.ts +8 -4
  73. package/types/modules/filters/modules/filterConfig/components/_shared/string-filter/string-filter-value-field.vue.d.ts +8 -4
  74. package/types/modules/filters/modules/filterConfig/components/agent/agent-filter-value-field.vue.d.ts +8 -4
  75. package/types/modules/filters/modules/filterConfig/components/agent-status/agent-status-filter-value-field.vue.d.ts +2 -2
  76. package/types/modules/filters/modules/filterConfig/components/amd-result/amd-result-filter-value-field.vue.d.ts +8 -4
  77. package/types/modules/filters/modules/filterConfig/components/auditor/auditor-filter-value-field.vue.d.ts +2 -2
  78. package/types/modules/filters/modules/filterConfig/components/auditor/index.d.ts +2 -2
  79. package/types/modules/filters/modules/filterConfig/components/bucket/bucket-filter-value-field.vue.d.ts +2 -2
  80. package/types/modules/filters/modules/filterConfig/components/bucket/index.d.ts +2 -2
  81. package/types/modules/filters/modules/filterConfig/components/call-direction/call-direction-filter-value-field.vue.d.ts +8 -4
  82. package/types/modules/filters/modules/filterConfig/components/call-reporting-result/call-reporting-result-filter-value-field.vue.d.ts +2 -2
  83. package/types/modules/filters/modules/filterConfig/components/case-assignee/case-assignee-filter-value-field.vue.d.ts +3 -2
  84. package/types/modules/filters/modules/filterConfig/components/case-assignee/filterConfig.d.ts +4 -2
  85. package/types/modules/filters/modules/filterConfig/components/case-author/case-author-filter-value-field.vue.d.ts +8 -4
  86. package/types/modules/filters/modules/filterConfig/components/case-close-reason-groups/case-close-reason-groups-filter-value-field.vue.d.ts +8 -4
  87. package/types/modules/filters/modules/filterConfig/components/case-impacted/case-impacted-filter-value-field.vue.d.ts +8 -4
  88. package/types/modules/filters/modules/filterConfig/components/case-priority/case-priority-filter-value-field.vue.d.ts +8 -4
  89. package/types/modules/filters/modules/filterConfig/components/case-reporter/case-reporter-filter-value-field.vue.d.ts +8 -4
  90. package/types/modules/filters/modules/filterConfig/components/case-sla/case-sla-filter-value-field.vue.d.ts +8 -4
  91. package/types/modules/filters/modules/filterConfig/components/case-sla-condition/case-sla-condition-filter-value-field.vue.d.ts +8 -4
  92. package/types/modules/filters/modules/filterConfig/components/case-source/case-source-filter-value-field.vue.d.ts +8 -4
  93. package/types/modules/filters/modules/filterConfig/components/case-status/case-status-filter-value-field.vue.d.ts +8 -4
  94. package/types/modules/filters/modules/filterConfig/components/contact/contact-filter-value-field.vue.d.ts +8 -4
  95. package/types/modules/filters/modules/filterConfig/components/contact-group/contact-group-filter-value-field.vue.d.ts +2 -2
  96. package/types/modules/filters/modules/filterConfig/components/contact-group/index.d.ts +2 -2
  97. package/types/modules/filters/modules/filterConfig/components/contact-label/contact-label-filter-value-field.vue.d.ts +2 -2
  98. package/types/modules/filters/modules/filterConfig/components/contact-label/index.d.ts +2 -2
  99. package/types/modules/filters/modules/filterConfig/components/contact-owner/contact-owner-filter-value-field.vue.d.ts +3 -2
  100. package/types/modules/filters/modules/filterConfig/components/contact-owner/index.d.ts +4 -2
  101. package/types/modules/filters/modules/filterConfig/components/gateway/gateway-filter-value-field.vue.d.ts +8 -4
  102. package/types/modules/filters/modules/filterConfig/components/grantee/grantee-filter-value-field.vue.d.ts +8 -4
  103. package/types/modules/filters/modules/filterConfig/components/hangup-cause/hangup-cause-filter-value-field.vue.d.ts +8 -4
  104. package/types/modules/filters/modules/filterConfig/components/has-attachment/has-attachment-filter-value-field.vue.d.ts +8 -4
  105. package/types/modules/filters/modules/filterConfig/components/has-file/has-file-filter-value-field.vue.d.ts +2 -2
  106. package/types/modules/filters/modules/filterConfig/components/has-rating/has-rating-filter-value-field.vue.d.ts +2 -2
  107. package/types/modules/filters/modules/filterConfig/components/has-transcription/has-transcription-filter-value-field.vue.d.ts +2 -2
  108. package/types/modules/filters/modules/filterConfig/components/has-user/has-user-filter-value-field.vue.d.ts +2 -2
  109. package/types/modules/filters/modules/filterConfig/components/index.d.ts +24 -22
  110. package/types/modules/filters/modules/filterConfig/components/queue/index.d.ts +2 -2
  111. package/types/modules/filters/modules/filterConfig/components/queue/queue-filter-value-field.vue.d.ts +2 -2
  112. package/types/modules/filters/modules/filterConfig/components/queue-period/queue-period-filter-value-field.vue.d.ts +2 -2
  113. package/types/modules/filters/modules/filterConfig/components/queue-type/queue-type-filter-value-field.vue.d.ts +2 -2
  114. package/types/modules/filters/modules/filterConfig/components/rated-by/rated-by-filter-value-field.vue.d.ts +8 -4
  115. package/types/modules/filters/modules/filterConfig/components/rating/rating-from-to-filter-value-field.vue.d.ts +8 -4
  116. package/types/modules/filters/modules/filterConfig/components/region/index.d.ts +2 -2
  117. package/types/modules/filters/modules/filterConfig/components/region/region-filter-value-field.vue.d.ts +2 -2
  118. package/types/modules/filters/modules/filterConfig/components/score/score-from-to-filter-value-field.vue.d.ts +3 -2
  119. package/types/modules/filters/modules/filterConfig/components/skill/index.d.ts +2 -2
  120. package/types/modules/filters/modules/filterConfig/components/skill/skill-filter-value-field.vue.d.ts +2 -2
  121. package/types/modules/filters/modules/filterConfig/components/stop-cause/stop-cause-filter-value-field.vue.d.ts +2 -2
  122. package/types/modules/filters/modules/filterConfig/components/supervisor/index.d.ts +2 -2
  123. package/types/modules/filters/modules/filterConfig/components/supervisor/supervisor-filter-value-field.vue.d.ts +2 -2
  124. package/types/modules/filters/modules/filterConfig/components/tag/tag-filter-value-field.vue.d.ts +8 -4
  125. package/types/modules/filters/modules/filterConfig/components/tags/index.d.ts +2 -2
  126. package/types/modules/filters/modules/filterConfig/components/tags/tags-filter-value-field.vue.d.ts +2 -2
  127. package/types/modules/filters/modules/filterConfig/components/talk-duration/talk-duration-filter-value-field.vue.d.ts +2 -2
  128. package/types/modules/filters/modules/filterConfig/components/team/index.d.ts +2 -2
  129. package/types/modules/filters/modules/filterConfig/components/team/team-filter-value-field.vue.d.ts +2 -2
  130. package/types/modules/filters/modules/filterConfig/components/total-duration/total-duration-filter-value-field.vue.d.ts +2 -2
  131. package/types/modules/filters/modules/filterConfig/components/user/user-filter-value-field.vue.d.ts +8 -4
  132. package/types/modules/filters/modules/filterConfig/components/utilization-progress/utilization-progress-filter-value-field.vue.d.ts +2 -2
  133. package/types/modules/filters/modules/filterConfig/components/variable/variable-filter-value-field.vue.d.ts +8 -4
  134. package/types/modules/headers/createTableHeadersStore.d.ts +24 -0
  135. package/types/modules/permissions-page/stores/createPermissionsStore.d.ts +4 -70
  136. package/types/modules/table/createTableStore.store.d.ts +76 -141
  137. package/types/modules/types/tableStore.types.d.ts +15 -1
@@ -79,6 +79,7 @@ import { ApplyPresetAction, SavePresetAction } from '../../filter-presets';
79
79
  import { FilterInitParams, IFilter } from '../classes/Filter';
80
80
  import { IFiltersManager } from '../classes/FiltersManager';
81
81
  import { useFilterConfigsToolkit } from '../composables/useFilterConfigsToolkit';
82
+ import { useSelectedFilters } from '../composables/useSelectedFilters';
82
83
  import { AnyFilterConfig } from '../modules/filterConfig/classes/FilterConfig';
83
84
  import { FilterOption } from '../modules/filterConfig/enums/FilterOption';
84
85
  import StaticFilterField from './config/static-view/static-filter-field.vue';
@@ -197,36 +198,13 @@ https://webitel.atlassian.net/browse/WTEL-7014
197
198
  Clear filters button should be active only if filters are defined in the table-filter-panel. Excluding the search filter
198
199
  **/
199
200
 
200
- const listSelectedFilters = computed(() => {
201
- const allowedKeys = new Set([
202
- ...props.filterOptions.map((filter) =>
203
- typeof filter === 'string' ? filter : filter.name,
204
- ),
205
- ...(props.filterableExtensionFields ?? []).map((filter) => filter.id),
206
- ]);
207
- return new Map(
208
- [
209
- ...props.filtersManager.filters,
210
- ].filter(([key]) => allowedKeys.has(key)),
211
- );
201
+ const { listSelectedFilters, hasAnyFilters } = useSelectedFilters({
202
+ filtersManager: () => props.filtersManager,
203
+ filterOptions: () => props.filterOptions,
204
+ filterableExtensionFields: () => props.filterableExtensionFields,
205
+ filterConfigs,
212
206
  });
213
207
 
214
- /**
215
- * @description
216
- * Filters applied by the user. `notDeletable` configs are skipped – they are
217
- * seeded defaults, not a user choice, so they must not block preset restore.
218
- */
219
- const hasAnyFilters = computed(() =>
220
- [
221
- ...listSelectedFilters.value.keys(),
222
- ].some((name) => {
223
- const filterConfig = filterConfigs.value.find(
224
- (config) => String(config.name) === String(name),
225
- );
226
- return !filterConfig?.notDeletable;
227
- }),
228
- );
229
-
230
208
  const presetStore = props.usePresetsStore ? props.usePresetsStore() : null;
231
209
 
232
210
  const handleResetFilters = () => {
@@ -18,6 +18,11 @@ export interface StaticFilterEmits extends FilterEmits {
18
18
 
19
19
  export interface DynamicFilterEmits extends FilterEmits {}
20
20
 
21
+ /** column filter lives in a popover, so it also asks the parent to close it */
22
+ export interface ColumnFilterEmits extends StaticFilterEmits {
23
+ close: [];
24
+ }
25
+
21
26
  export interface FilterProps {
22
27
  filter: IFilter;
23
28
  filterConfig: AnyFilterConfig;
@@ -0,0 +1,116 @@
1
+ import type { DataField } from '@webitel/api-services/gen/models';
2
+ import type { WtTableHeader } from '@webitel/ui-sdk/components/wt-table/types/WtTable';
3
+ import { computed, type MaybeRefOrGetter, toValue } from 'vue';
4
+ import { useI18n } from 'vue-i18n';
5
+
6
+ import type { FilterName } from '../classes/Filter';
7
+ import type { IFiltersManager } from '../classes/FiltersManager';
8
+ import { createFilterConfig } from '../modules/filterConfig/classes/createFilterConfig';
9
+ import {
10
+ type AnyFilterConfig,
11
+ FilterConfig,
12
+ } from '../modules/filterConfig/classes/FilterConfig';
13
+ import { createTypeExtensionFilterConfig } from '../modules/filterConfig/components/_custom';
14
+ import type { FilterConfigDefinition } from '../modules/filterConfig/types/FilterConfigDefinition';
15
+
16
+ const isResolvedFilterConfig = (
17
+ filter: WtTableHeader['filter'],
18
+ ): filter is AnyFilterConfig => typeof filter === 'object' && filter !== null;
19
+
20
+ /**
21
+ * Resolves a table header into its filter config and the filter currently applied in the
22
+ * filters manager.
23
+ *
24
+ * `header.filter` is either a plain name — resolved the same way the panel does
25
+ * (useFilterConfigsToolkit): a custom (type extension) column is matched by field id in
26
+ * `filterableExtensionFields`, a pre-configured definition from `filterOptions` (the page's
27
+ * filtersOptions) is reused as is, everything else is a standard filter option with an i18n
28
+ * label fallback — or an already-resolved config, used as is. Passing a resolved config from
29
+ * `headers.ts` skips the `filterOptions`/`filterableExtensionFields` search entirely.
30
+ *
31
+ * [WTEL-7727](https://webitel.atlassian.net/browse/WTEL-7727)
32
+ */
33
+ export const useColumnFilter = ({
34
+ header,
35
+ filtersManager,
36
+ filterOptions = [],
37
+ filterableExtensionFields = [],
38
+ }: {
39
+ header: MaybeRefOrGetter<WtTableHeader>;
40
+ filtersManager: MaybeRefOrGetter<IFiltersManager>;
41
+ filterOptions?: MaybeRefOrGetter<FilterConfigDefinition[]>;
42
+ filterableExtensionFields?: MaybeRefOrGetter<DataField[]>;
43
+ }) => {
44
+ const { t } = useI18n();
45
+
46
+ const resolvedFilter = computed(() => {
47
+ const filter = toValue(header).filter;
48
+ return isResolvedFilterConfig(filter) ? filter : undefined;
49
+ });
50
+
51
+ const filterName = computed(
52
+ () => (resolvedFilter.value?.name ?? toValue(header).filter) as FilterName,
53
+ );
54
+
55
+ const extensionField = computed(() =>
56
+ resolvedFilter.value
57
+ ? undefined
58
+ : toValue(filterableExtensionFields).find(
59
+ (field) => field.id === filterName.value,
60
+ ),
61
+ );
62
+
63
+ const configuredOption = computed(() =>
64
+ resolvedFilter.value
65
+ ? undefined
66
+ : toValue(filterOptions).find(
67
+ (opt): opt is AnyFilterConfig =>
68
+ typeof opt !== 'string' && opt.name === filterName.value,
69
+ ),
70
+ );
71
+
72
+ const filterConfig = computed(() => {
73
+ let config: AnyFilterConfig;
74
+
75
+ if (resolvedFilter.value) {
76
+ config = resolvedFilter.value;
77
+ } else if (extensionField.value) {
78
+ config = createTypeExtensionFilterConfig(
79
+ {
80
+ name: extensionField.value.id,
81
+ },
82
+ {
83
+ field: extensionField.value,
84
+ },
85
+ );
86
+ } else {
87
+ const option = configuredOption.value;
88
+ config =
89
+ option instanceof FilterConfig
90
+ ? option
91
+ : option
92
+ ? new FilterConfig({
93
+ ...option,
94
+ name: option.name,
95
+ })
96
+ : createFilterConfig({
97
+ name: filterName.value,
98
+ });
99
+ }
100
+
101
+ if (!config.label) {
102
+ config.label = t(`webitelUI.filters.${config.name}`);
103
+ }
104
+
105
+ return config;
106
+ });
107
+
108
+ const filter = computed(() =>
109
+ toValue(filtersManager).getFilter(filterName.value),
110
+ );
111
+
112
+ return {
113
+ filterConfig,
114
+ filter,
115
+ };
116
+ };
@@ -0,0 +1,58 @@
1
+ import { isEmpty } from '@webitel/ui-sdk/scripts';
2
+ import { type MaybeRefOrGetter, toValue } from 'vue';
3
+
4
+ import type { FilterValue, IFilter } from '../classes/Filter';
5
+ import type { StaticFilterEmits } from '../components/types/Filter.types';
6
+ import type { AnyFilterConfig } from '../modules/filterConfig';
7
+
8
+ type StaticFilterEmit = <K extends keyof StaticFilterEmits>(
9
+ event: K,
10
+ ...args: StaticFilterEmits[K]
11
+ ) => void;
12
+
13
+ /**
14
+ * Turns a new filter value into the right manager event:
15
+ * - empty value (except booleans) → `delete:filter` (only if the filter exists)
16
+ * - no filter yet → `add:filter`
17
+ * - filter exists → `update:filter`, keeping its label
18
+ *
19
+ * Shared by the static filter field and the column filter.
20
+ *
21
+ * [WTEL-7727](https://webitel.atlassian.net/browse/WTEL-7727)
22
+ */
23
+ export const useFilterValueChange = ({
24
+ filterConfig,
25
+ filter,
26
+ emit,
27
+ }: {
28
+ filterConfig: MaybeRefOrGetter<AnyFilterConfig>;
29
+ filter: MaybeRefOrGetter<IFilter | undefined>;
30
+ emit: StaticFilterEmit;
31
+ }) => {
32
+ const onValueChange = (value: FilterValue) => {
33
+ const currentFilter = toValue(filter);
34
+ const config = toValue(filterConfig);
35
+
36
+ if (isEmpty(value) && typeof value !== 'boolean') {
37
+ if (!currentFilter) return;
38
+ return emit('delete:filter', currentFilter);
39
+ }
40
+
41
+ if (isEmpty(currentFilter?.value)) {
42
+ return emit('add:filter', {
43
+ name: config.name,
44
+ value,
45
+ });
46
+ }
47
+
48
+ emit('update:filter', {
49
+ name: config.name,
50
+ value,
51
+ label: currentFilter?.label,
52
+ });
53
+ };
54
+
55
+ return {
56
+ onValueChange,
57
+ };
58
+ };
@@ -0,0 +1,87 @@
1
+ import { computed, type MaybeRefOrGetter, ref, toValue, watch } from 'vue';
2
+
3
+ import type { IFilter } from '../classes/Filter';
4
+ import type {
5
+ AnyFilterConfig,
6
+ FilterConfigSearchMethodParams,
7
+ } from '../modules/filterConfig/classes/FilterConfig';
8
+ import { FilterOptionToPreviewApiSearchMethodMap } from '../modules/filterConfig/components';
9
+
10
+ /**
11
+ * @author @dlohvinov
12
+ *
13
+ * @description
14
+ * Loads the data shown in a filter's hover preview (records behind the stored ids)
15
+ * once per filter value, in the parent, instead of inside tooltip components —
16
+ * to avoid api requests spam.
17
+ *
18
+ * Shared by the panel chip and the column header icon (WTEL-7727).
19
+ */
20
+ export const useFilterValuePreview = ({
21
+ filter,
22
+ filterConfig,
23
+ }: {
24
+ filter: MaybeRefOrGetter<IFilter | undefined>;
25
+ filterConfig: MaybeRefOrGetter<AnyFilterConfig>;
26
+ }) => {
27
+ const localValue = ref();
28
+
29
+ const fillLocalValue = async (currentFilter = toValue(filter)) => {
30
+ if (!currentFilter) {
31
+ localValue.value = undefined;
32
+ return;
33
+ }
34
+
35
+ const filterName = currentFilter.name;
36
+ const filterValue = currentFilter.value;
37
+ const config = toValue(filterConfig);
38
+
39
+ const valueSearchMethod =
40
+ 'searchRecords' in config
41
+ ? (...params: FilterConfigSearchMethodParams) => {
42
+ /* arrow fn here preserves filterConfig class "this" */
43
+ return config.searchRecords(...params);
44
+ }
45
+ : FilterOptionToPreviewApiSearchMethodMap[filterName];
46
+
47
+ if (valueSearchMethod) {
48
+ const { items } = await valueSearchMethod(
49
+ {
50
+ id: filterValue,
51
+ // -1 returns all records
52
+ size: -1,
53
+ },
54
+ {
55
+ filterValue,
56
+ filterName,
57
+ filterConfig: config,
58
+ },
59
+ );
60
+ localValue.value = items;
61
+ } else {
62
+ localValue.value = filterValue;
63
+ }
64
+ };
65
+
66
+ watch(
67
+ () => toValue(filter)?.value,
68
+ () => {
69
+ fillLocalValue(toValue(filter));
70
+ },
71
+ {
72
+ immediate: true,
73
+ },
74
+ );
75
+
76
+ // [https://webitel.atlassian.net/browse/WTEL-6732]
77
+ // if type filter is boolean and value = false, need display preview
78
+ const isRenderPreview = computed(
79
+ () => localValue.value === false || localValue.value,
80
+ );
81
+
82
+ return {
83
+ localValue,
84
+ isRenderPreview,
85
+ fillLocalValue,
86
+ };
87
+ };
@@ -0,0 +1,64 @@
1
+ import type { DataField } from '@webitel/api-services/gen/models';
2
+ import {
3
+ type ComputedRef,
4
+ computed,
5
+ type MaybeRefOrGetter,
6
+ toValue,
7
+ } from 'vue';
8
+
9
+ import type { FilterName, IFilter } from '../classes/Filter';
10
+ import type { IFiltersManager } from '../classes/FiltersManager';
11
+ import type { BaseFilterConfig } from '../modules/filterConfig/classes/FilterConfig';
12
+ import type { FilterConfigDefinition } from '../modules/filterConfig/types/FilterConfigDefinition';
13
+
14
+ /**
15
+ * Filters from the manager that belong to this panel/menu (its filter options + extension fields),
16
+ * so search filters living in the same manager are ignored.
17
+ *
18
+ * `hasAnyFilters` skips `notDeletable` configs — they are seeded defaults, not a user choice
19
+ * ([WTEL-7014](https://webitel.atlassian.net/browse/WTEL-7014)).
20
+ */
21
+ export const useSelectedFilters = ({
22
+ filtersManager,
23
+ filterOptions,
24
+ filterableExtensionFields = [],
25
+ filterConfigs,
26
+ }: {
27
+ filtersManager: MaybeRefOrGetter<IFiltersManager>;
28
+ filterOptions: MaybeRefOrGetter<FilterConfigDefinition[]>;
29
+ filterableExtensionFields?: MaybeRefOrGetter<DataField[] | undefined>;
30
+ filterConfigs: ComputedRef<BaseFilterConfig[]>;
31
+ }) => {
32
+ const listSelectedFilters = computed(() => {
33
+ const allowedKeys = new Set<FilterName>([
34
+ ...toValue(filterOptions).map((filter) =>
35
+ typeof filter === 'string' ? filter : filter.name,
36
+ ),
37
+ ...(toValue(filterableExtensionFields) ?? [])
38
+ .map((field) => field.id)
39
+ .filter((id): id is string => id !== undefined),
40
+ ]);
41
+
42
+ return new Map<FilterName, IFilter>(
43
+ [
44
+ ...toValue(filtersManager).filters,
45
+ ].filter(([key]) => allowedKeys.has(key)),
46
+ );
47
+ });
48
+
49
+ const hasAnyFilters = computed(() =>
50
+ [
51
+ ...listSelectedFilters.value.keys(),
52
+ ].some((name) => {
53
+ const filterConfig = filterConfigs.value.find(
54
+ (config) => String(config.name) === String(name),
55
+ );
56
+ return !filterConfig?.notDeletable;
57
+ }),
58
+ );
59
+
60
+ return {
61
+ listSelectedFilters,
62
+ hasAnyFilters,
63
+ };
64
+ };
@@ -12,6 +12,8 @@ import {
12
12
  type FiltersManagerConfig,
13
13
  type IFiltersManager,
14
14
  } from './classes/FiltersManager';
15
+ import ColumnFilterComponent from './components/column/column-filter.vue';
16
+ import FiltersActionsMenuComponent from './components/filters-actions-menu.vue';
15
17
  import DynamicFilterSearchComponent from './components/search-bar/dynamic-filter-search.vue';
16
18
  import TableFiltersPanelComponent from './components/table-filters-panel.vue';
17
19
  import { FilterOption } from './modules/filterConfig/enums/FilterOption';
@@ -28,9 +30,11 @@ export type {
28
30
  IFiltersManager,
29
31
  };
30
32
  export {
33
+ ColumnFilterComponent,
31
34
  createFiltersManager,
32
35
  DynamicFilterSearchComponent,
33
36
  Filter,
34
37
  FilterOption,
38
+ FiltersActionsMenuComponent,
35
39
  TableFiltersPanelComponent,
36
40
  };
@@ -4,7 +4,7 @@
4
4
  v-model:model-value="model"
5
5
  :field="props.filterConfig.field"
6
6
  :required="false"
7
- :v="v$.model"
7
+ :v="!disableValidation && v$.model"
8
8
  >
9
9
  <template #[WtTypeExtensionFieldKind.Boolean]="{ defaultProps }">
10
10
  <has-option-filter-value-field v-bind="defaultProps" v-model:model-value="booleanModel"/>
@@ -12,7 +12,7 @@
12
12
  <template #[WtTypeExtensionFieldKind.Select]="{ defaultProps }">
13
13
  <wt-single-select
14
14
  v-bind="defaultProps"
15
- :v="v$.model"
15
+ :v="!disableValidation && v$.model"
16
16
  :model-value="
17
17
  model ??
18
18
  [] /* so that component won't break when model is nullish at init */
@@ -26,7 +26,7 @@
26
26
  <template #[WtTypeExtensionFieldKind.Multiselect]="{ defaultProps }">
27
27
  <wt-multi-select
28
28
  v-bind="defaultProps"
29
- :v="v$.model"
29
+ :v="!disableValidation && v$.model"
30
30
  :model-value="
31
31
  model ??
32
32
  [] /* so that component won't break when model is nullish at init */
@@ -39,7 +39,10 @@
39
39
  />
40
40
  </template>
41
41
  <template #[WtTypeExtensionFieldKind.Calendar]>
42
- <date-time-options-filter-value-field v-model:model-value="dateTimeModel" />
42
+ <date-time-options-filter-value-field
43
+ v-model:model-value="dateTimeModel"
44
+ :disable-validation="disableValidation"
45
+ />
43
46
  </template>
44
47
  </wt-type-extension-value-input>
45
48
  </template>
@@ -66,6 +69,7 @@ const model = defineModel<unknown>();
66
69
 
67
70
  const props = defineProps<{
68
71
  filterConfig: ITypeExtensionFilterConfig;
72
+ disableValidation?: boolean;
69
73
  }>();
70
74
 
71
75
  const emit = defineEmits<{
@@ -116,7 +120,7 @@ const v$ = useVuelidate(
116
120
  $autoDirty: true,
117
121
  },
118
122
  );
119
- v$.value.$touch();
123
+ if (!props?.disableValidation) v$.value.$touch();
120
124
  watch(
121
125
  () => v$.value.$invalid,
122
126
  (invalid) => {
@@ -6,7 +6,7 @@
6
6
  class="date-time-filter-value-field__picker"
7
7
  show-time
8
8
  required
9
- :v="v$.from"
9
+ :v="!disableValidation && v$.from"
10
10
  @update:model-value="handleInput('from', $event)"
11
11
  />
12
12
 
@@ -16,7 +16,7 @@
16
16
  class="date-time-filter-value-field__picker"
17
17
  show-time
18
18
  required
19
- :v="v$.to"
19
+ :v="!disableValidation && v$.to"
20
20
  @update:model-value="handleInput('to', $event)"
21
21
  />
22
22
  </div>
@@ -34,6 +34,10 @@ type ModelValue = {
34
34
  to: number;
35
35
  };
36
36
 
37
+ const props = defineProps<{
38
+ disableValidation?: boolean;
39
+ }>();
40
+
37
41
  const emit = defineEmits<{
38
42
  'update:invalid': [
39
43
  boolean,
@@ -69,8 +73,7 @@ const v$ = useVuelidate(
69
73
  },
70
74
  );
71
75
 
72
- v$.value.$touch();
73
-
76
+ if (!props?.disableValidation) v$.value.$touch();
74
77
  watch(
75
78
  () => v$.value.$invalid,
76
79
  (invalid) => {
@@ -14,7 +14,7 @@
14
14
  :label="t('reusable.from')"
15
15
  show-time
16
16
  required
17
- :v="v$.from"
17
+ :v="!disableValidation && v$.from"
18
18
  @update:model-value="changeAbsoluteValue($event, 'from')"
19
19
  />
20
20
  <wt-datepicker
@@ -23,7 +23,7 @@
23
23
  :label="t('reusable.to')"
24
24
  show-time
25
25
  required
26
- :v="v$.to"
26
+ :v="!disableValidation && v$.to"
27
27
  @update:model-value="changeAbsoluteValue($event, 'to')"
28
28
  />
29
29
  </div>
@@ -39,6 +39,10 @@ import { endOfToday, startOfToday } from 'date-fns';
39
39
  import { computed, ref, watch } from 'vue';
40
40
  import { useI18n } from 'vue-i18n';
41
41
 
42
+ const props = defineProps<{
43
+ disableValidation?: boolean;
44
+ }>();
45
+
42
46
  const model = defineModel<
43
47
  | RelativeDatetimeValue
44
48
  | {
@@ -115,8 +119,7 @@ const v$ = useVuelidate(
115
119
  },
116
120
  );
117
121
 
118
- v$.value.$touch();
119
-
122
+ if (!props?.disableValidation) v$.value.$touch();
120
123
  watch(
121
124
  () => v$.value.$invalid,
122
125
  (invalid) => {
@@ -3,14 +3,14 @@
3
3
  <wt-timepicker
4
4
  :label="t('reusable.from')"
5
5
  :model-value="value.from"
6
- :v="vFrom"
6
+ :v="!disableValidation && vFrom"
7
7
  format="hh:mm:ss"
8
8
  @update:model-value="handleInput('from', $event)"
9
9
  />
10
10
  <wt-timepicker
11
11
  :label="t('reusable.to')"
12
12
  :model-value="value.to"
13
- :v="vTo"
13
+ :v="!disableValidation && vTo"
14
14
  format="hh:mm:ss"
15
15
  @update:model-value="handleInput('to', $event)"
16
16
  />
@@ -22,6 +22,10 @@ import { useVuelidate } from '@vuelidate/core';
22
22
  import { computed, watch } from 'vue';
23
23
  import { useI18n } from 'vue-i18n';
24
24
 
25
+ const props = defineProps<{
26
+ disableValidation?: boolean;
27
+ }>();
28
+
25
29
  const { t } = useI18n();
26
30
 
27
31
  type ModelValue = {
@@ -74,8 +78,7 @@ const v$ = useVuelidate<{
74
78
  $autoDirty: true,
75
79
  },
76
80
  );
77
- v$.value.$touch();
78
-
81
+ if (!props?.disableValidation) v$.value.$touch();
79
82
  const vFrom = computed(() => {
80
83
  const modelValidation = v$.value.model;
81
84
  if (!modelValidation) return undefined;
@@ -2,7 +2,7 @@
2
2
  <wt-input-text
3
3
  v-model:model-value="model"
4
4
  :label="t('webitelUI.filters.filterValue')"
5
- :v="v$.model"
5
+ :v="!disableValidation && v$.model"
6
6
  />
7
7
  </template>
8
8
 
@@ -14,6 +14,10 @@ import { useI18n } from 'vue-i18n';
14
14
 
15
15
  type ModelValue = string;
16
16
 
17
+ const props = defineProps<{
18
+ disableValidation?: boolean;
19
+ }>();
20
+
17
21
  const model = defineModel<ModelValue>();
18
22
  if (!model.value) {
19
23
  model.value = '';
@@ -35,8 +39,7 @@ const v$ = useVuelidate(
35
39
  },
36
40
  );
37
41
 
38
- v$.value.$touch();
39
-
42
+ if (!props?.disableValidation) v$.value.$touch();
40
43
  const emit = defineEmits<{
41
44
  'update:invalid': [
42
45
  boolean,
@@ -3,7 +3,7 @@
3
3
  :label="t('webitelUI.filters.filterValue')"
4
4
  :search-method="searchMethod"
5
5
  :model-value="model"
6
- :v="v$.model"
6
+ :v="!disableValidation && v$.model"
7
7
  option-value="id"
8
8
  @update:model-value="handleInput"
9
9
  />
@@ -20,6 +20,10 @@ import { searchMethod } from './config.js';
20
20
 
21
21
  type ModelValue = number[];
22
22
 
23
+ const props = defineProps<{
24
+ disableValidation?: boolean;
25
+ }>();
26
+
23
27
  const model = defineModel<ModelValue>();
24
28
 
25
29
  const emit = defineEmits<{
@@ -44,8 +48,7 @@ const v$ = useVuelidate(
44
48
  },
45
49
  );
46
50
 
47
- v$.value.$touch();
48
-
51
+ if (!props?.disableValidation) v$.value.$touch();
49
52
  watch(
50
53
  () => v$.value.$invalid,
51
54
  (invalid) => {
@@ -3,7 +3,7 @@
3
3
  :label="t('webitelUI.filters.filterValue')"
4
4
  :options="AmdResultOptions"
5
5
  v-model:model-value="selectValue"
6
- :v="v$.model"
6
+ :v="!disableValidation && v$.model"
7
7
  data-key="value"
8
8
  option-value="value"
9
9
  />
@@ -25,6 +25,10 @@ import { AmdResultOptions } from '../../enums/options/AMDResultOptions';
25
25
 
26
26
  const AMD_RESULT_EMPTY_VALUE = 'EMPTY';
27
27
 
28
+ const props = defineProps<{
29
+ disableValidation?: boolean;
30
+ }>();
31
+
28
32
  const model = defineModel<string[]>();
29
33
  const { t } = useI18n();
30
34
 
@@ -64,8 +68,7 @@ const v$ = useVuelidate(
64
68
  },
65
69
  );
66
70
 
67
- v$.value.$touch();
68
-
71
+ if (!props?.disableValidation) v$.value.$touch();
69
72
  const emit = defineEmits<{
70
73
  'update:invalid': [
71
74
  boolean,