@webitel/ui-datalist 1.0.4 → 1.0.6

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 (25) hide show
  1. package/package.json +4 -3
  2. package/src/filter-presets/components/_shared/preset-filters-preview.vue +2 -11
  3. package/src/filter-presets/components/apply-preset/apply-preset-action.vue +5 -0
  4. package/src/filter-presets/components/save-preset/save-preset-action.vue +31 -4
  5. package/src/filter-presets/components/save-preset/save-preset-popup.vue +5 -1
  6. package/src/filters/classes/FiltersManager.ts +9 -3
  7. package/src/filters/components/dynamic-filter-add-action.vue +2 -3
  8. package/src/filters/components/filter-options/_shared/lookup-filter-preview/lookup-filter-value-preview.vue +21 -0
  9. package/src/filters/components/filter-options/agent/agent-filter-value-preview.vue +4 -20
  10. package/src/filters/components/filter-options/agent/config.js +0 -1
  11. package/src/filters/components/filter-options/contact/contact-filter-value-preview.vue +4 -20
  12. package/src/filters/components/filter-options/created-at/created-at-filter-value-field.vue +97 -0
  13. package/src/filters/components/filter-options/created-at/created-at-filter-value-preview.vue +64 -0
  14. package/src/filters/components/filter-options/gateway/gateway-filter-value-preview.vue +6 -21
  15. package/src/filters/components/filter-options/grantee/grantee-filter-value-preview.vue +6 -21
  16. package/src/filters/components/filter-options/index.ts +45 -0
  17. package/src/filters/components/filter-options/queue/queue-filter-value-preview.vue +4 -20
  18. package/src/filters/components/filter-options/rated-by/rated-by-filter-value-preview.vue +6 -21
  19. package/src/filters/components/filter-options/service-case/config.js +1 -1
  20. package/src/filters/components/filter-options/team/team-filter-value-preview.vue +4 -20
  21. package/src/filters/components/filter-options/user/user-filter-value-preview.vue +6 -21
  22. package/src/filters/components/preview/dynamic-filter-preview-info.vue +1 -0
  23. package/src/filters/components/preview/dynamic-filter-preview.vue +82 -19
  24. package/src/filters/components/table-filters-panel.vue +25 -27
  25. package/src/filters/enums/FilterOption.ts +12 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-datalist",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Toolkit for building data lists in webitel ui system",
5
5
  "scripts": {
6
6
  "lint:fix": "eslint --fix ./src && prettier --write --check ./src",
@@ -29,12 +29,13 @@
29
29
  "peerDependencies": {
30
30
  "vue": "^3.5",
31
31
  "axios": "^1",
32
- "pinia": "^3.x"
32
+ "pinia": "^3.x",
33
+ "date-fns": "^4"
33
34
  },
34
35
  "dependencies": {
35
36
  "@vuelidate/core": "^2.0.3",
36
37
  "@vuelidate/validators": "^2.0.4",
37
- "@webitel/ui-sdk": "^25.4.48"
38
+ "@webitel/ui-sdk": "^25.4.55"
38
39
  },
39
40
  "devDependencies": {
40
41
  "vue-tsc": "^2.2.8",
@@ -9,16 +9,8 @@
9
9
  v-for="filter of props.filters"
10
10
  :key="filter.name"
11
11
  :filter="filter"
12
- dummy
13
- >
14
- <template #info>
15
- <component
16
- :is="FilterOptionToPreviewComponentMap[filter.name]"
17
- :value="filter.value"
18
- >
19
- </component>
20
- </template>
21
- </dynamic-filter-preview>
12
+ readonly
13
+ />
22
14
  </template>
23
15
  </dynamic-filter-panel-wrapper>
24
16
  </div>
@@ -30,7 +22,6 @@ import { useI18n } from 'vue-i18n';
30
22
 
31
23
  import type { IFilter } from '../../../filters';
32
24
  import DynamicFilterPanelWrapper from '../../../filters/components/dynamic-filter-panel-wrapper.vue';
33
- import { FilterOptionToPreviewComponentMap } from '../../../filters/components/filter-options';
34
25
  import DynamicFilterPreview from '../../../filters/components/preview/dynamic-filter-preview.vue';
35
26
 
36
27
  type Props = {
@@ -91,6 +91,11 @@ const props = defineProps<{
91
91
  */
92
92
  namespace: string;
93
93
  usePresetsStore: Store;
94
+ /**
95
+ * @description
96
+ * Is needed for preset to exclude filter values, not related to filters panel
97
+ */
98
+ filterOptions: FilterOption[];
94
99
  }>();
95
100
 
96
101
  const emit = defineEmits<{
@@ -14,7 +14,7 @@
14
14
  !presetToOverwriteWith /* on 'overwrite preset' popup hide this popup, but don't reset it*/
15
15
  "
16
16
  :shown="true /* coz visibility is controlled by v-if*/"
17
- :filters-manager="props.filtersManager"
17
+ :filters-manager="localFiltersManager"
18
18
  :namespace="namespace"
19
19
  @submit="handlePresetSubmit"
20
20
  @close="showSaveForm = false"
@@ -32,11 +32,15 @@
32
32
  <script lang="ts" setup>
33
33
  import { WtIconAction } from '@webitel/ui-sdk/components';
34
34
  import { IconAction } from '@webitel/ui-sdk/enums';
35
- import { computed, inject, type Ref, ref } from 'vue';
35
+ import { computed, inject, reactive, type Ref, ref, watch } from 'vue';
36
36
  import { useI18n } from 'vue-i18n';
37
37
  import { EnginePresetQuery } from 'webitel-sdk';
38
38
 
39
- import { IFiltersManager } from '../../../filters';
39
+ import {
40
+ createFiltersManager,
41
+ FilterOption,
42
+ IFiltersManager,
43
+ } from '../../../filters';
40
44
  import {
41
45
  addPreset,
42
46
  getPresetList,
@@ -47,21 +51,44 @@ import SavePresetPopup, { SubmitConfig } from './save-preset-popup.vue';
47
51
 
48
52
  const props = defineProps<{
49
53
  /**
54
+ * @description
50
55
  * presets "section" namespace
51
56
  */
52
57
  namespace: string;
58
+ /**
59
+ * @author @dlohvinov
60
+ * serves as a source for local filters manager
61
+ */
53
62
  filtersManager: IFiltersManager;
63
+ /**
64
+ * @description
65
+ * Is needed for preset to exclude filter values, not related to filters panel
66
+ */
67
+ filterOptions: FilterOption[];
54
68
  }>();
55
69
 
56
70
  const eventBus = inject('$eventBus');
57
71
 
72
+ const localFiltersManager = reactive(createFiltersManager());
73
+
74
+ watch(
75
+ props.filtersManager,
76
+ () => {
77
+ localFiltersManager.reset();
78
+ localFiltersManager.fromString(
79
+ props.filtersManager.toString({ include: props.filterOptions }),
80
+ );
81
+ },
82
+ { immediate: true },
83
+ );
84
+
58
85
  const { t } = useI18n();
59
86
 
60
87
  /**
61
88
  * disable "save" btn if there's nothing to save
62
89
  * */
63
90
  const disableAction = computed(() => {
64
- return !props.filtersManager.getAllKeys().length;
91
+ return !localFiltersManager.getAllKeys().length;
65
92
  });
66
93
 
67
94
  /**
@@ -53,7 +53,7 @@ import { computed, reactive, ref } from 'vue';
53
53
  import { useI18n } from 'vue-i18n';
54
54
  import { EnginePresetQuery } from 'webitel-sdk';
55
55
 
56
- import type { IFiltersManager } from '../../../index';
56
+ import type { IFiltersManager } from '../../../filters';
57
57
  import PresetDescriptionField from '../_shared/input-fields/preset-description-field.vue';
58
58
  import PresetNameField from '../_shared/input-fields/preset-name-field.vue';
59
59
  import PresetFiltersPreview from '../_shared/preset-filters-preview.vue';
@@ -65,6 +65,10 @@ export type SubmitConfig = {
65
65
  };
66
66
 
67
67
  const props = defineProps<{
68
+ /**
69
+ * @description
70
+ * is needed here for `.toString()`
71
+ */
68
72
  filtersManager: IFiltersManager;
69
73
  }>();
70
74
 
@@ -55,7 +55,7 @@ class FiltersManager implements IFiltersManager {
55
55
  return filter;
56
56
  }
57
57
 
58
- deleteFilter(name: FilterName): IFilter {
58
+ deleteFilter({ name }: IFilter): IFilter {
59
59
  const filter = this.filters.get(name);
60
60
  this.filters.delete(name);
61
61
  return filter;
@@ -70,8 +70,14 @@ class FiltersManager implements IFiltersManager {
70
70
  return filters;
71
71
  }
72
72
 
73
- toString(): string {
74
- const filtersData = [...this.filters.values()].reduce(
73
+ toString({
74
+ include,
75
+ exclude,
76
+ }: {
77
+ include?: FilterName[];
78
+ exclude?: FilterName[];
79
+ } = {}): string {
80
+ const filtersData = this.getFiltersList({ include, exclude }).reduce(
75
81
  (acc, { name, label, value }) => {
76
82
  if (isEmpty(value) && value == null) return acc;
77
83
 
@@ -21,8 +21,7 @@
21
21
  :options="props.filterOptions"
22
22
  @cancel="() => tooltipSlotScope.hide()"
23
23
  @submit="
24
- (payload) =>
25
- submitFilterChange(payload, { hide: tooltipSlotScope.hide })
24
+ (payload) => submit(payload, { hide: tooltipSlotScope.hide })
26
25
  "
27
26
  />
28
27
  </slot>
@@ -54,7 +53,7 @@ const emit = defineEmits<{
54
53
  'add:filter': [FilterInitParams];
55
54
  }>();
56
55
 
57
- const submitFilterChange = (payload: FilterInitParams, { hide }) => {
56
+ const submit = (payload: FilterInitParams, { hide }) => {
58
57
  emit('add:filter', payload);
59
58
  hide();
60
59
  };
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <ul v-if="value">
3
+ <li
4
+ v-for="({ name, id }, index) of value"
5
+ :key="id || index"
6
+ >
7
+ {{ name }}
8
+ </li>
9
+ </ul>
10
+ </template>
11
+
12
+ <script lang="ts" setup>
13
+ defineProps<{
14
+ /**
15
+ * would be great to use a type for this
16
+ * */
17
+ value: Record<string, unknown>[];
18
+ }>();
19
+ </script>
20
+
21
+ <style scoped></style>
@@ -1,31 +1,15 @@
1
1
  <template>
2
- <ul v-if="localValue">
3
- <li
4
- v-for="({ name }, index) of localValue"
5
- :key="index"
6
- >
7
- {{ name }}
8
- </li>
9
- </ul>
2
+ <lookup-filter-value-preview v-bind="props" />
10
3
  </template>
11
4
 
12
5
  <script lang="ts" setup>
13
- import { ref } from 'vue';
6
+ import type { EngineAgent } from 'webitel-sdk';
14
7
 
15
- import { searchMethod } from './config.js';
8
+ import LookupFilterValuePreview from '../_shared/lookup-filter-preview/lookup-filter-value-preview.vue';
16
9
 
17
10
  const props = defineProps<{
18
- value: number[];
11
+ value: EngineAgent[];
19
12
  }>();
20
-
21
- const localValue = ref([]);
22
-
23
- const getLocalValue = async () => {
24
- const { items } = await searchMethod({ id: props.value });
25
- localValue.value = items;
26
- };
27
-
28
- getLocalValue();
29
13
  </script>
30
14
 
31
15
  <style scoped></style>
@@ -1,4 +1,3 @@
1
1
  import AgentsAPI from '@webitel/ui-sdk/api/clients/agents/agents';
2
2
 
3
3
  export const searchMethod = AgentsAPI.getLookup;
4
- export const localePath = '';
@@ -1,31 +1,15 @@
1
1
  <template>
2
- <ul v-if="localValue">
3
- <li
4
- v-for="({ name }, index) of localValue"
5
- :key="index"
6
- >
7
- {{ name }}
8
- </li>
9
- </ul>
2
+ <lookup-filter-value-preview v-bind="props" />
10
3
  </template>
11
4
 
12
5
  <script lang="ts" setup>
13
- import { ref } from 'vue';
6
+ import type { WebitelContactsContact } from 'webitel-sdk';
14
7
 
15
- import { searchMethod } from './config.js';
8
+ import LookupFilterValuePreview from '../_shared/lookup-filter-preview/lookup-filter-value-preview.vue';
16
9
 
17
10
  const props = defineProps<{
18
- value: number[];
11
+ value: WebitelContactsContact[];
19
12
  }>();
20
-
21
- const localValue = ref([]);
22
-
23
- const getLocalValue = async () => {
24
- const { items } = await searchMethod({ id: props.value });
25
- localValue.value = items;
26
- };
27
-
28
- getLocalValue();
29
13
  </script>
30
14
 
31
15
  <style scoped></style>
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <div class="created-at-filter-value-field">
3
+ <wt-radio
4
+ v-for="value of radioOpts"
5
+ :key="value"
6
+ v-model="selectedRadioValue"
7
+ :label="t(`webitelUI.filters.datetime.${value}`)"
8
+ :value="value"
9
+ @input="handleRadioChange"
10
+ />
11
+ <wt-datepicker
12
+ v-if="showDatepickers"
13
+ :value="model.from"
14
+ :label="t('reusable.from')"
15
+ mode="datetime"
16
+ @input="changeAbsoluteValue($event, 'from')"
17
+ />
18
+ <wt-datepicker
19
+ v-if="showDatepickers"
20
+ :value="model.to"
21
+ :label="t('reusable.to')"
22
+ mode="datetime"
23
+ @input="changeAbsoluteValue($event, 'to')"
24
+ />
25
+ </div>
26
+ </template>
27
+
28
+ <script lang="ts" setup>
29
+ import { WtRadio } from '@webitel/ui-sdk/components';
30
+ import { RelativeDatetimeValue } from '@webitel/ui-sdk/enums';
31
+ import { endOfToday, startOfToday } from 'date-fns';
32
+ import { computed, ref } from 'vue';
33
+ import { useI18n } from 'vue-i18n';
34
+
35
+ const model = defineModel<
36
+ RelativeDatetimeValue | { from: number; to: number }
37
+ >();
38
+
39
+ const { t } = useI18n();
40
+
41
+ const radioOpts = [
42
+ RelativeDatetimeValue.Today,
43
+ RelativeDatetimeValue.ThisWeek,
44
+ RelativeDatetimeValue.ThisMonth,
45
+ RelativeDatetimeValue.Custom,
46
+ ];
47
+
48
+ const selectedRadioValue = ref();
49
+
50
+ const initialize = () => {
51
+ if (!model.value) {
52
+ /* initialize */
53
+ selectedRadioValue.value = radioOpts[0];
54
+ model.value = selectedRadioValue.value;
55
+ } else if (typeof model.value === 'string') {
56
+ /* RelativeDatetimeValue */
57
+ selectedRadioValue.value = model.value;
58
+ } else {
59
+ /* { from, to } */
60
+ selectedRadioValue.value = RelativeDatetimeValue.Custom;
61
+ }
62
+ };
63
+
64
+ initialize();
65
+
66
+ const showDatepickers = computed(() => {
67
+ return selectedRadioValue.value === RelativeDatetimeValue.Custom;
68
+ });
69
+
70
+ const handleRadioChange = (value: RelativeDatetimeValue) => {
71
+ if (value === RelativeDatetimeValue.Custom) {
72
+ model.value = {
73
+ from: startOfToday().getTime(),
74
+ to: endOfToday().getTime(),
75
+ };
76
+ } else {
77
+ model.value = value;
78
+ }
79
+ };
80
+
81
+ const changeAbsoluteValue = (value: number, prop: 'from' | 'to') => {
82
+ const newModelValue = {
83
+ ...(model.value as { from: number; to: number }),
84
+ [prop]: value,
85
+ };
86
+
87
+ model.value = newModelValue;
88
+ };
89
+ </script>
90
+
91
+ <style scoped>
92
+ .created-at-filter-value-field {
93
+ display: flex;
94
+ flex-direction: column;
95
+ gap: var(--spacing-xs);
96
+ }
97
+ </style>
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <div class="created-at-filter-value-preview">
3
+ <p v-if="isRelativeValue">
4
+ {{ t(`webitelUI.filters.datetime.${props.value}`) }}
5
+ </p>
6
+
7
+ <template v-else>
8
+ <div>
9
+ <p class="created-at-filter-value-preview__title">
10
+ {{ t('reusable.from') }}
11
+ </p>
12
+
13
+ <span>{{ from }}</span>
14
+ </div>
15
+
16
+ <div>
17
+ <p class="created-at-filter-value-preview__title">
18
+ {{ t('reusable.to') }}
19
+ </p>
20
+
21
+ <span>{{ to }}</span>
22
+ </div>
23
+ </template>
24
+ </div>
25
+ </template>
26
+
27
+ <script lang="ts" setup>
28
+ import { RelativeDatetimeValue } from '@webitel/ui-sdk/enums';
29
+ import { isRelativeDatetimeValue } from '@webitel/ui-sdk/scripts';
30
+ import { computed } from 'vue';
31
+ import { useI18n } from 'vue-i18n';
32
+
33
+ const props = defineProps<{
34
+ value: { from: number; to: number } | RelativeDatetimeValue;
35
+ }>();
36
+
37
+ const { t } = useI18n();
38
+
39
+ const isRelativeValue = computed(() => {
40
+ return isRelativeDatetimeValue(props.value);
41
+ });
42
+
43
+ const from = computed(() => {
44
+ return isRelativeDatetimeValue.value
45
+ ? false
46
+ : new Date(props.value.from).toLocaleString();
47
+ });
48
+
49
+ const to = computed(() => {
50
+ return isRelativeDatetimeValue.value
51
+ ? false
52
+ : new Date(props.value.to).toLocaleString();
53
+ });
54
+ </script>
55
+
56
+ <style lang="scss" scoped>
57
+ @use '@webitel/styleguide/typography' as *;
58
+
59
+ .duration-filter-value-preview {
60
+ &__title {
61
+ @extend %typo-subtitle-1;
62
+ }
63
+ }
64
+ </style>
@@ -1,31 +1,16 @@
1
1
  <template>
2
- <ul v-if="localValue">
3
- <li
4
- v-for="({ name }, index) of localValue"
5
- :key="index"
6
- >
7
- {{ name }}
8
- </li>
9
- </ul>
2
+ <lookup-filter-value-preview v-bind="props" />
10
3
  </template>
11
4
 
12
5
  <script lang="ts" setup>
13
- import { ref } from 'vue';
14
-
15
- import { searchMethod } from './config.js';
6
+ import LookupFilterValuePreview from '../_shared/lookup-filter-preview/lookup-filter-value-preview.vue';
16
7
 
17
8
  const props = defineProps<{
18
- value: number[];
9
+ /**
10
+ * would be great to use generated type for this
11
+ * */
12
+ value: Record<string, unknown>[];
19
13
  }>();
20
-
21
- const localValue = ref([]);
22
-
23
- const getLocalValue = async () => {
24
- const { items } = await searchMethod({ id: props.value });
25
- localValue.value = items;
26
- };
27
-
28
- getLocalValue();
29
14
  </script>
30
15
 
31
16
  <style scoped></style>
@@ -1,31 +1,16 @@
1
1
  <template>
2
- <ul v-if="localValue">
3
- <li
4
- v-for="({ name }, index) of localValue"
5
- :key="index"
6
- >
7
- {{ name }}
8
- </li>
9
- </ul>
2
+ <lookup-filter-value-preview v-bind="props" />
10
3
  </template>
11
4
 
12
5
  <script lang="ts" setup>
13
- import { ref } from 'vue';
14
-
15
- import { searchMethod } from './config.js';
6
+ import LookupFilterValuePreview from '../_shared/lookup-filter-preview/lookup-filter-value-preview.vue';
16
7
 
17
8
  const props = defineProps<{
18
- value: number[];
9
+ /**
10
+ * would be great to use generated type for this
11
+ * */
12
+ value: Record<string, unknown>[];
19
13
  }>();
20
-
21
- const localValue = ref([]);
22
-
23
- const getLocalValue = async () => {
24
- const { items } = await searchMethod({ ids: props.value });
25
- localValue.value = items;
26
- };
27
-
28
- getLocalValue();
29
14
  </script>
30
15
 
31
16
  <style scoped></style>
@@ -7,28 +7,37 @@ import ActualResolutionTimeFilter from './actual-resolution-time/actual-resoluti
7
7
  import ActualResolutionTimeFilterPreview from './actual-resolution-time/actual-resolution-time-filter-value-preview.vue';
8
8
  import AgentFilter from './agent/agent-filter-value-field.vue';
9
9
  import AgentFilterPreview from './agent/agent-filter-value-preview.vue';
10
+ import { searchMethod as agentSearchMethod } from './agent/config';
10
11
  import AmdResultFilter from './amd-result/amd-result-filter-value-field.vue';
11
12
  import AmdResultFilterPreview from './amd-result/amd-result-filter-value-preview.vue';
12
13
  import AssigneeFilter from './assignee/assignee-filter-value-field.vue';
13
14
  import AssigneeFilterPreview from './assignee/assignee-filter-value-preview.vue';
15
+ import { searchMethod as assigneeSearchMethod } from './assignee/config';
14
16
  import AuthorFilter from './author/author-filter-value-field.vue';
15
17
  import AuthorFilterPreview from './author/author-filter-value-preview.vue';
18
+ import { searchMethod as authorSearchMethod } from './author/config';
16
19
  import CauseFilter from './cause/cause-filter-value-field.vue';
17
20
  import CauseFilterPreview from './cause/cause-filter-value-preview.vue';
18
21
  import CloseReasonGroupsCaseFilter from './close-reason-groups-case/close-reason-groups-case-filter-value-field.vue';
19
22
  import CloseReasonGroupsCaseFilterPreview from './close-reason-groups-case/close-reason-groups-case-filter-value-preview.vue';
23
+ import { searchMethod as contactSearchMethod } from './contact/config';
20
24
  import ContactFilter from './contact/contact-filter-value-field.vue';
21
25
  import ContactFilterPreview from './contact/contact-filter-value-preview.vue';
26
+ import { searchMethod as contactGroupSearchMethod } from './contact-group/config';
22
27
  import ContactGroupFilter from './contact-group/contact-group-filter-value-field.vue';
23
28
  import ContactGroupFilterPreview from './contact-group/contact-group-filter-value-preview.vue';
29
+ import CreatedAtFilterValueField from './created-at/created-at-filter-value-field.vue';
30
+ import CreatedAtFilterPreview from './created-at/created-at-filter-value-preview.vue';
24
31
  import CreatedAtFromFilter from './created-at-from/created-at-from-filter-value-field.vue';
25
32
  import CreatedAtFromFilterPreview from './created-at-from/created-at-from-filter-value-preview.vue';
26
33
  import CreatedAtToFilter from './created-at-to/created-at-to-filter-value-field.vue';
27
34
  import CreatedAtToFilterPreview from './created-at-to/created-at-to-filter-value-preview.vue';
28
35
  import DirectionFilter from './direction/direction-filter-value-field.vue';
29
36
  import DirectionFilterPreview from './direction/direction-filter-value-preview.vue';
37
+ import { searchMethod as gatewaySearchMethod } from './gateway/config';
30
38
  import GatewayFilter from './gateway/gateway-filter-value-field.vue';
31
39
  import GatewayFilterPreview from './gateway/gateway-filter-value-preview.vue';
40
+ import { searchMethod as granteeSearchMethod } from './grantee/config';
32
41
  import GranteeFilter from './grantee/grantee-filter-value-field.vue';
33
42
  import GranteeFilterPreview from './grantee/grantee-filter-value-preview.vue';
34
43
  import HasAttachmentFilter from './has-attachment/has-attachment-filter-value-field.vue';
@@ -39,30 +48,38 @@ import HasRatingFilterValueField from './has-rating/has-rating-filter-value-fiel
39
48
  import HasRatingFilterValuePreview from './has-rating/has-rating-filter-value-preview.vue';
40
49
  import HasTranscriptionFilter from './has-transcription/has-transcription-filter-value-field.vue';
41
50
  import HasTranscriptionFilterPreview from './has-transcription/has-transcription-filter-value-preview.vue';
51
+ import { searchMethod as impactedSearchMethod } from './impacted/config';
42
52
  import ImpactedFilter from './impacted/impacted-filter-value-field.vue';
43
53
  import ImpactedFilterPreview from './impacted/impacted-filter-value-preview.vue';
54
+ import { searchMethod as priorityCaseSearchMethod } from './priority-case/config';
44
55
  import CasePriorityFilter from './priority-case/priority-case-filter-value-field.vue';
45
56
  import CasePriorityFilterPreview from './priority-case/priority-case-filter-value-preview.vue';
57
+ import { searchMethod as queueSearchMethod } from './queue/config';
46
58
  import QueueFilter from './queue/queue-filter-value-field.vue';
47
59
  import QueueFilterPreview from './queue/queue-filter-value-preview.vue';
60
+ import { searchMethod as ratedBySearchMethod } from './rated-by/config';
48
61
  import RatedByFilter from './rated-by/rated-by-filter-value-field.vue';
49
62
  import RatedByFilterPreview from './rated-by/rated-by-filter-value-preview.vue';
50
63
  import RatingFromToFilter from './rating/rating-from-to-filter-value-field.vue';
51
64
  import RatingFromToFilterPreview from './rating/rating-from-to-filter-value-preview.vue';
52
65
  import ReactionTimeFilter from './reaction-time/reaction-time-filter-value-field.vue';
53
66
  import ReactionTimeFilterPreview from './reaction-time/reaction-time-filter-value-preview.vue';
67
+ import { searchMethod as reporterSearchMethod } from './reporter/config';
54
68
  import ReporterFilter from './reporter/reporter-filter-value-field.vue';
55
69
  import ReporterFilterPreview from './reporter/reporter-filter-value-preview.vue';
56
70
  import ResolutionTimeFilter from './resolution-time/resolution-time-filter-value-field.vue';
57
71
  import ResolutionTimeFilterPreview from './resolution-time/resolution-time-filter-value-preview.vue';
58
72
  import ScoreFilter from './score/score-from-to-filter-value-field.vue';
59
73
  import ScoreFilterPreview from './score/score-from-to-filter-value-preview.vue';
74
+ import { searchMethod as serviceCaseSearchMethod } from './service-case/config';
60
75
  import CaseServiceFilter from './service-case/service-case-filter-value-field.vue';
61
76
  import CaseServiceFilterPreview from './service-case/service-case-filter-value-preview.vue';
77
+ import { searchMethod as slaSearchMethod } from './sla/config';
62
78
  import SlaFilter from './sla/sla-filter-value-field.vue';
63
79
  import SlaFilterPreview from './sla/sla-filter-value-preview.vue';
64
80
  import SlaConditionFilter from './sla-condition/sla-condition-filter-value-field.vue';
65
81
  import SlaConditionFilterPreview from './sla-condition/sla-condition-filter-value-preview.vue';
82
+ import { searchMethod as sourceCaseSearchMethod } from './source-case/config';
66
83
  import CaseSourceFilter from './source-case/source-case-filter-value-field.vue';
67
84
  import CaseSourceFilterPreview from './source-case/source-case-filter-value-preview.vue';
68
85
  import CaseStatusFilter from './status-case/status-case-filter-value-field.vue';
@@ -71,10 +88,12 @@ import TagFilter from './tag/tag-filter-value-field.vue';
71
88
  import TagFilterPreview from './tag/tag-filter-value-preview.vue';
72
89
  import TalkDurationFilter from './talk-duration/talk-duration-filter-value-field.vue';
73
90
  import TalkDurationFilterPreview from './talk-duration/talk-duration-filter-value-preview.vue';
91
+ import { searchMethod as teamSearchMethod } from './team/config';
74
92
  import TeamFilter from './team/team-filter-value-field.vue';
75
93
  import TeamFilterPreview from './team/team-filter-value-preview.vue';
76
94
  import TotalDurationFilter from './total-duration/total-duration-filter-value-field.vue';
77
95
  import TotalDurationFilterPreview from './total-duration/total-duration-filter-value-preview.vue';
96
+ import { searchMethod as userSearchMethod } from './user/config';
78
97
  import UserFilter from './user/user-filter-value-field.vue';
79
98
  import UserFilterPreview from './user/user-filter-value-preview.vue';
80
99
  import VariableFilter from './variable/variable-filter-value-field.vue';
@@ -109,6 +128,7 @@ export {
109
128
  ContactFilterPreview,
110
129
  ContactGroupFilter,
111
130
  ContactGroupFilterPreview,
131
+ CreatedAtFilterValueField,
112
132
  CreatedAtFromFilter,
113
133
  CreatedAtFromFilterPreview,
114
134
  CreatedAtToFilter,
@@ -182,6 +202,7 @@ export const FilterOptionToValueComponentMap: Record<FilterOption, Component> =
182
202
  [FilterOption.HasTranscription]: HasTranscriptionFilter,
183
203
  [FilterOption.User]: UserFilter,
184
204
  [FilterOption.Variable]: VariableFilter,
205
+ [FilterOption.CreatedAt]: CreatedAtFilterValueField,
185
206
  [FilterOption.CreatedAtFrom]: CreatedAtFromFilter,
186
207
  [FilterOption.CreatedAtTo]: CreatedAtToFilter,
187
208
  [FilterOption.Status]: CaseStatusFilter,
@@ -208,6 +229,7 @@ export const FilterOptionToPreviewComponentMap: Record<
208
229
  FilterOption,
209
230
  Component
210
231
  > = {
232
+ [FilterOption.CreatedAt]: CreatedAtFilterPreview,
211
233
  [FilterOption.Agent]: AgentFilterPreview,
212
234
  [FilterOption.AmdResult]: AmdResultFilterPreview,
213
235
  [FilterOption.Contact]: ContactFilterPreview,
@@ -248,3 +270,26 @@ export const FilterOptionToPreviewComponentMap: Record<
248
270
  [FilterOption.ActualResolutionTime]: ActualResolutionTimeFilterPreview,
249
271
  [FilterOption.HasAttachment]: HasAttachmentFilterPreview,
250
272
  };
273
+
274
+ export const FilterOptionToApiSearchMethodMap: Record<
275
+ FilterOption,
276
+ (unknown) => { items }
277
+ > = {
278
+ [FilterOption.Agent]: agentSearchMethod,
279
+ [FilterOption.Gateway]: gatewaySearchMethod,
280
+ [FilterOption.Grantee]: granteeSearchMethod,
281
+ [FilterOption.Queue]: queueSearchMethod,
282
+ [FilterOption.RatedBy]: ratedBySearchMethod,
283
+ [FilterOption.Reporter]: reporterSearchMethod,
284
+ [FilterOption.Sla]: slaSearchMethod,
285
+ [FilterOption.Service]: serviceCaseSearchMethod,
286
+ [FilterOption.Source]: sourceCaseSearchMethod,
287
+ [FilterOption.User]: userSearchMethod,
288
+ [FilterOption.ContactGroup]: contactGroupSearchMethod,
289
+ [FilterOption.Assignee]: assigneeSearchMethod,
290
+ [FilterOption.Author]: authorSearchMethod,
291
+ [FilterOption.Priority]: priorityCaseSearchMethod,
292
+ [FilterOption.Impacted]: impactedSearchMethod,
293
+ [FilterOption.Contact]: contactSearchMethod,
294
+ [FilterOption.Team]: teamSearchMethod,
295
+ };
@@ -1,31 +1,15 @@
1
1
  <template>
2
- <ul v-if="localValue">
3
- <li
4
- v-for="({ name }, index) of localValue"
5
- :key="index"
6
- >
7
- {{ name }}
8
- </li>
9
- </ul>
2
+ <lookup-filter-value-preview v-bind="props" />
10
3
  </template>
11
4
 
12
5
  <script lang="ts" setup>
13
- import { ref } from 'vue';
6
+ import type { EngineQueue } from 'webitel-sdk';
14
7
 
15
- import { searchMethod } from './config.js';
8
+ import LookupFilterValuePreview from '../_shared/lookup-filter-preview/lookup-filter-value-preview.vue';
16
9
 
17
10
  const props = defineProps<{
18
- value: number[];
11
+ value: EngineQueue[];
19
12
  }>();
20
-
21
- const localValue = ref([]);
22
-
23
- const getLocalValue = async () => {
24
- const { items } = await searchMethod({ id: props.value });
25
- localValue.value = items;
26
- };
27
-
28
- getLocalValue();
29
13
  </script>
30
14
 
31
15
  <style scoped></style>
@@ -1,31 +1,16 @@
1
1
  <template>
2
- <ul v-if="localValue">
3
- <li
4
- v-for="({ name }, index) of localValue"
5
- :key="index"
6
- >
7
- {{ name }}
8
- </li>
9
- </ul>
2
+ <lookup-filter-value-preview v-bind="props" />
10
3
  </template>
11
4
 
12
5
  <script lang="ts" setup>
13
- import { ref } from 'vue';
14
-
15
- import { searchMethod } from './config.js';
6
+ import LookupFilterValuePreview from '../_shared/lookup-filter-preview/lookup-filter-value-preview.vue';
16
7
 
17
8
  const props = defineProps<{
18
- value: number[];
9
+ /**
10
+ * would be great to use generated type for this
11
+ * */
12
+ value: Record<string, unknown>[];
19
13
  }>();
20
-
21
- const localValue = ref([]);
22
-
23
- const getLocalValue = async () => {
24
- const { items } = await searchMethod({ id: props.value });
25
- localValue.value = items;
26
- };
27
-
28
- getLocalValue();
29
14
  </script>
30
15
 
31
16
  <style scoped></style>
@@ -1,4 +1,4 @@
1
- import ServiceCatalogsAPI from '@webitel/ui-sdk/api/clients/caseServiceCatalogs/service-catalogs';
1
+ import ServiceCatalogsAPI from '@webitel/ui-sdk/api/clients/caseServiceCatalogs/serviceCatalogs';
2
2
  import ServicesAPI from '@webitel/ui-sdk/api/clients/caseServices/services';
3
3
 
4
4
  export const searchMethod = ServiceCatalogsAPI.getList;
@@ -1,31 +1,15 @@
1
1
  <template>
2
- <ul v-if="localValue">
3
- <li
4
- v-for="({ name }, index) of localValue"
5
- :key="index"
6
- >
7
- {{ name }}
8
- </li>
9
- </ul>
2
+ <lookup-filter-value-preview v-bind="props" />
10
3
  </template>
11
4
 
12
5
  <script lang="ts" setup>
13
- import { ref } from 'vue';
6
+ import type { EngineAgentTeam } from 'webitel-sdk';
14
7
 
15
- import { searchMethod } from './config.js';
8
+ import LookupFilterValuePreview from '../_shared/lookup-filter-preview/lookup-filter-value-preview.vue';
16
9
 
17
10
  const props = defineProps<{
18
- value: number[];
11
+ value: EngineAgentTeam[];
19
12
  }>();
20
-
21
- const localValue = ref([]);
22
-
23
- const getLocalValue = async () => {
24
- const { items } = await searchMethod({ id: props.value });
25
- localValue.value = items;
26
- };
27
-
28
- getLocalValue();
29
13
  </script>
30
14
 
31
15
  <style scoped></style>
@@ -1,31 +1,16 @@
1
1
  <template>
2
- <ul v-if="localValue">
3
- <li
4
- v-for="({ name }, index) of localValue"
5
- :key="index"
6
- >
7
- {{ name }}
8
- </li>
9
- </ul>
2
+ <lookup-filter-value-preview v-bind="props" />
10
3
  </template>
11
4
 
12
5
  <script lang="ts" setup>
13
- import { ref } from 'vue';
14
-
15
- import { searchMethod } from './config.js';
6
+ import LookupFilterValuePreview from '../_shared/lookup-filter-preview/lookup-filter-value-preview.vue';
16
7
 
17
8
  const props = defineProps<{
18
- value: number[];
9
+ /**
10
+ * would be great to use generated type for this
11
+ * */
12
+ value: Record<string, unknown>[];
19
13
  }>();
20
-
21
- const localValue = ref([]);
22
-
23
- const getLocalValue = async () => {
24
- const { items } = await searchMethod({ id: props.value });
25
- localValue.value = items;
26
- };
27
-
28
- getLocalValue();
29
14
  </script>
30
15
 
31
16
  <style scoped></style>
@@ -21,6 +21,7 @@
21
21
  display: flex;
22
22
  flex-direction: column;
23
23
  gap: var(--spacing-xs);
24
+ min-width: 60px;
24
25
 
25
26
  &__header {
26
27
  @extend %typo-subtitle-1;
@@ -1,12 +1,15 @@
1
1
  <template>
2
- <dynamic-filter-config-view :disabled="dummy">
2
+ <dynamic-filter-config-view :disabled="readonly">
3
3
  <template #activator="{ visible: configFormVisible }">
4
- <wt-tooltip :disabled="configFormVisible">
4
+ <wt-tooltip
5
+ :disabled="configFormVisible"
6
+ @update:visible="!localValue && fillLocalValue()"
7
+ >
5
8
  <template #activator>
6
9
  <wt-chip color="primary">
7
10
  {{ filter.label || t(`webitelUI.filters.${filter.name}`) }}
8
11
  <wt-icon-btn
9
- v-if="!dummy"
12
+ v-if="!filterConfig.notDeletable && !readonly"
10
13
  icon="close--filled"
11
14
  size="sm"
12
15
  color="on-primary"
@@ -23,9 +26,14 @@
23
26
 
24
27
  <template #default>
25
28
  <slot name="info">
29
+ <wt-loader
30
+ v-if="!localValue"
31
+ size="sm"
32
+ />
26
33
  <component
27
34
  :is="FilterOptionToPreviewComponentMap[filter.name]"
28
- :value="filter.value"
35
+ v-else
36
+ :value="localValue"
29
37
  />
30
38
  </slot>
31
39
  </template>
@@ -34,15 +42,18 @@
34
42
  </wt-tooltip>
35
43
  </template>
36
44
 
37
- <template #content="slotScope">
45
+ <template #content="{ tooltipSlotScope }">
38
46
  <slot
39
47
  name="form"
40
- v-bind="slotScope"
48
+ v-bind="{ tooltipSlotScope }"
41
49
  >
42
50
  <dynamic-filter-config-form
43
- :filter="filter"
44
- @cancel="() => slotScope.hide()"
45
- @submit="(payload) => submitFilterChange(payload) && slotScope.hide()"
51
+ :filter="props.filter"
52
+ :options="filterOptions"
53
+ @cancel="() => tooltipSlotScope.hide()"
54
+ @submit="
55
+ (payload) => submit(payload, { hide: tooltipSlotScope.hide })
56
+ "
46
57
  />
47
58
  </slot>
48
59
  </template>
@@ -50,20 +61,34 @@
50
61
  </template>
51
62
 
52
63
  <script lang="ts" setup>
53
- import { WtChip } from '@webitel/ui-sdk/components';
54
- import { WtIconBtn } from '@webitel/ui-sdk/components';
55
- import { WtTooltip } from '@webitel/ui-sdk/components';
64
+ import {
65
+ WtChip,
66
+ WtIconBtn,
67
+ WtLoader,
68
+ WtTooltip,
69
+ } from '@webitel/ui-sdk/components';
70
+ import { computed, ref } from 'vue';
56
71
  import { useI18n } from 'vue-i18n';
57
72
 
58
- import type { FilterInitParams, IFilter } from '../../types/Filter';
73
+ import type { FilterOption } from '../../enums/FilterOption';
74
+ import type { FilterData, IFilter } from '../../types/Filter';
59
75
  import DynamicFilterConfigForm from '../config/dynamic-filter-config-form.vue';
60
76
  import DynamicFilterConfigView from '../config/dynamic-filter-config-view.vue';
61
- import { FilterOptionToPreviewComponentMap } from '../filter-options';
77
+ import {
78
+ FilterOptionToApiSearchMethodMap,
79
+ FilterOptionToPreviewComponentMap,
80
+ } from '../filter-options';
62
81
  import DynamicFilterPreviewInfo from './dynamic-filter-preview-info.vue';
63
82
 
64
83
  interface Props {
65
84
  filter: IFilter;
66
- dummy?: boolean /* https://webitel.atlassian.net/browse/WTEL-6308?focusedCommentId=657415 */;
85
+ /**
86
+ * @description
87
+ * is needed for form component to localize selected filter name value and/or
88
+ * pass filter options configs
89
+ */
90
+ filterOptions: FilterOption[];
91
+ readonly?: boolean;
67
92
  }
68
93
 
69
94
  const { t } = useI18n();
@@ -71,16 +96,50 @@ const { t } = useI18n();
71
96
  const props = defineProps<Props>();
72
97
 
73
98
  const emit = defineEmits<{
74
- 'update:filter': [IFilter];
99
+ 'update:filter': [FilterData];
75
100
  'delete:filter': [IFilter];
76
101
  }>();
77
102
 
78
- const deleteFilter = () => {
79
- emit('delete:filter', props.filter);
103
+ const filterConfig = computed(() => {
104
+ const thisFilterOption =
105
+ props.filterOptions?.find((option) => {
106
+ return option.value === props.filter.name;
107
+ }) || {};
108
+
109
+ return thisFilterOption;
110
+ });
111
+
112
+ const localValue = ref();
113
+
114
+ /**
115
+ * @author @dlohvinov
116
+ *
117
+ * @description
118
+ * loading filters preview data -> main preview component
119
+ * instead of tooltip-components to avoid api requests spam
120
+ */
121
+ const fillLocalValue = async (filter = props.filter) => {
122
+ const filterName = props.filter.name;
123
+ const filterValue = filter.value;
124
+
125
+ const valueSearchMethod = FilterOptionToApiSearchMethodMap[filterName];
126
+
127
+ if (valueSearchMethod) {
128
+ const { items } = await valueSearchMethod({ id: filterValue });
129
+ localValue.value = items;
130
+ } else {
131
+ localValue.value = filterValue;
132
+ }
80
133
  };
81
134
 
82
- const submitFilterChange = (filter: FilterInitParams) => {
135
+ const submit = (filter: IFilter, { hide }) => {
83
136
  emit('update:filter', filter);
137
+ fillLocalValue(filter);
138
+ hide();
139
+ };
140
+
141
+ const deleteFilter = () => {
142
+ emit('delete:filter', props.filter);
84
143
  };
85
144
  </script>
86
145
 
@@ -91,4 +150,8 @@ const submitFilterChange = (filter: FilterInitParams) => {
91
150
  align-items: center;
92
151
  gap: var(--spacing-2xs);
93
152
  }
153
+
154
+ .wt-loader {
155
+ margin: auto;
156
+ }
94
157
  </style>
@@ -1,25 +1,11 @@
1
1
  <template>
2
2
  <dynamic-filter-panel-wrapper>
3
3
  <template #filters>
4
- <!-- WTF? - /* https://webitel.atlassian.net/browse/WTEL-6308?focusedCommentId=657415 */ -->
5
- <!--TODO-->
6
- <!-- <dynamic-filter-preview-->
7
- <!-- v-if="!hasCreatedAtFromFilter"-->
8
- <!-- :filter="defaultCreatedAtFromFilterDataPreview"-->
9
- <!-- dummy-->
10
- <!-- >-->
11
- <!-- <template #info>-->
12
- <!-- <component-->
13
- <!-- :is="FilterOptionToPreviewComponentMap[FilterOption.CreatedAtFrom]"-->
14
- <!-- :value="defaultCreatedAtFromFilterDataPreview.value"-->
15
- <!-- />-->
16
- <!-- </template>-->
17
- <!-- </dynamic-filter-preview>-->
18
-
19
4
  <dynamic-filter-preview
20
5
  v-for="filter of appliedFilters"
21
6
  :key="filter.name"
22
7
  :filter="filter"
8
+ :filter-options="localizedFilterOptions"
23
9
  disable-click-away
24
10
  @update:filter="emit('filter:update', $event)"
25
11
  @delete:filter="emit('filter:delete', filter)"
@@ -36,6 +22,7 @@
36
22
  <apply-preset-action
37
23
  :namespace="props.presetNamespace"
38
24
  :use-presets-store="props.usePresetsStore"
25
+ :filter-options="props.filterOptions"
39
26
  @apply="emit('preset:apply', $event)"
40
27
  />
41
28
 
@@ -43,6 +30,7 @@
43
30
  v-if="enablePresets"
44
31
  :namespace="props.presetNamespace"
45
32
  :filters-manager="props.filtersManager"
33
+ :filter-options="props.filterOptions"
46
34
  />
47
35
 
48
36
  <wt-icon-action
@@ -80,7 +68,8 @@ type Props = {
80
68
  filterOptions: FilterOption[];
81
69
  /**
82
70
  * @description
83
- * Looks like a anti-pattern, but save-preset.vue component needs filterManager
71
+ * create local filters manager from snapshot
72
+ * inside save-preset.vue
84
73
  */
85
74
  filtersManager: IFiltersManager;
86
75
  /**
@@ -130,7 +119,21 @@ const { t } = useI18n();
130
119
 
131
120
  const appliedFilters = computed(() => {
132
121
  return props.filtersManager.getFiltersList({
133
- include: props.filterOptions,
122
+ include: props.filterOptions.map((opt) => {
123
+ return opt.value || opt;
124
+ }),
125
+ });
126
+ });
127
+
128
+ const localizedFilterOptions = computed(() => {
129
+ return props.filterOptions.map((opt) => {
130
+ const isExtended = typeof opt !== 'string';
131
+
132
+ const name = isExtended
133
+ ? t(`webitelUI.filters.${opt.value}`)
134
+ : t(`webitelUI.filters.${opt}`);
135
+
136
+ return isExtended ? { name, ...opt } : { name, value: opt };
134
137
  });
135
138
  });
136
139
 
@@ -139,16 +142,11 @@ const appliedFilters = computed(() => {
139
142
  * available filters to add, with appliedFilters excluded
140
143
  */
141
144
  const availableFilterOptions = computed(() => {
142
- return props.filterOptions
143
- .filter((opt) => {
144
- return appliedFilters.value.every((filter) => {
145
- return filter.name !== opt;
146
- });
147
- })
148
- .map((opt) => ({
149
- name: t(`webitelUI.filters.${opt}`),
150
- value: opt,
151
- }));
145
+ return localizedFilterOptions.value.filter(({ value: opt }) => {
146
+ return appliedFilters.value.every((filter) => {
147
+ return filter.name !== opt;
148
+ });
149
+ });
152
150
  });
153
151
 
154
152
  const enablePresets = computed(() => !!props.presetNamespace);
@@ -1,3 +1,5 @@
1
+ import type { FilterName } from '../types/Filter';
2
+
1
3
  export const FilterOption = {
2
4
  Agent: 'agent',
3
5
  AmdResult: 'amdResult',
@@ -18,6 +20,7 @@ export const FilterOption = {
18
20
  HasTranscription: 'hasTranscription',
19
21
  User: 'user',
20
22
  Variable: 'variable',
23
+ CreatedAt: 'createdAt',
21
24
  CreatedAtFrom: 'createdAtFrom',
22
25
  CreatedAtTo: 'createdAtTo',
23
26
  Status: 'status',
@@ -38,6 +41,13 @@ export const FilterOption = {
38
41
  ActualReactionTime: 'actualReactionTime',
39
42
  ActualResolutionTime: 'actualResolutionTime',
40
43
  HasAttachment: 'hasAttachment',
41
- } as const;
44
+ } as FilterName;
45
+
46
+ type SimpleFilterOption = (typeof FilterOption)[keyof typeof FilterOption];
47
+
48
+ type ExtendedFilterOption = {
49
+ name: FilterOption;
50
+ notDeletable?: boolean;
51
+ };
42
52
 
43
- export type FilterOption = (typeof FilterOption)[keyof typeof FilterOption];
53
+ export type FilterOption = SimpleFilterOption | ExtendedFilterOption;