@webitel/ui-datalist 26.8.20 → 26.8.22

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 (21) hide show
  1. package/package.json +1 -1
  2. package/src/modules/card/composables/__tests__/useCardValidation.spec.ts +28 -0
  3. package/src/modules/filters/components/config/static-view/__tests__/static-filter-field.spec.ts +29 -4
  4. package/src/modules/filters/components/config/static-view/static-filter-field.vue +2 -0
  5. package/src/modules/filters/modules/filterConfig/components/action/action-filter-value-field.vue +75 -0
  6. package/src/modules/filters/modules/filterConfig/components/action/action-filter-value-preview.vue +35 -0
  7. package/src/modules/filters/modules/filterConfig/components/index.ts +14 -0
  8. package/src/modules/filters/modules/filterConfig/components/object/index.ts +29 -0
  9. package/src/modules/filters/modules/filterConfig/components/object/object-filter-value-field.vue +82 -0
  10. package/src/modules/filters/modules/filterConfig/components/object/object-filter-value-preview.vue +13 -0
  11. package/src/modules/filters/modules/filterConfig/composables/useActionOptions.ts +24 -0
  12. package/src/modules/filters/modules/filterConfig/enums/FilterOption.ts +3 -0
  13. package/types/.tsbuildinfo +1 -1
  14. package/types/modules/filters/modules/filterConfig/components/action/action-filter-value-field.vue.d.ts +18 -0
  15. package/types/modules/filters/modules/filterConfig/components/action/action-filter-value-preview.vue.d.ts +6 -0
  16. package/types/modules/filters/modules/filterConfig/components/index.d.ts +35 -1
  17. package/types/modules/filters/modules/filterConfig/components/object/index.d.ts +29 -0
  18. package/types/modules/filters/modules/filterConfig/components/object/object-filter-value-field.vue.d.ts +19 -0
  19. package/types/modules/filters/modules/filterConfig/components/object/object-filter-value-preview.vue.d.ts +6 -0
  20. package/types/modules/filters/modules/filterConfig/composables/useActionOptions.d.ts +6 -0
  21. package/types/modules/filters/modules/filterConfig/enums/FilterOption.d.ts +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-datalist",
3
- "version": "26.8.20",
3
+ "version": "26.8.22",
4
4
  "description": "Toolkit for building data lists in webitel ui system",
5
5
  "scripts": {
6
6
  "build:types": "vue-tsc -b tsconfig.build.json",
@@ -51,6 +51,34 @@ describe('card validation of a root-level required rule', () => {
51
51
  expect(calendarErrors).toHaveLength(1);
52
52
  });
53
53
 
54
+ it.each([
55
+ QueueType.OUTBOUND_IVR_QUEUE,
56
+ QueueType.OUTBOUND_JOB_QUEUE,
57
+ ])('reports a cleared calendar and schema of queue type %s on the fields', async (type) => {
58
+ const state = ref({
59
+ ...getQueueDefaults(type),
60
+ name: 'a queue',
61
+ calendar: {},
62
+ schema: {},
63
+ });
64
+ const scope = effectScope(true);
65
+ // biome-ignore lint/suspicious/noExplicitAny: regle's inferred state type
66
+ let r$: any;
67
+
68
+ scope.run(() => {
69
+ ({ r$ } = useRegleSchema(state, queueSchema as never));
70
+ });
71
+
72
+ const result = await r$.$validate();
73
+
74
+ expect(result.valid).toBe(false);
75
+ expect(r$.$error).toBe(true);
76
+ expect(r$.$fields.calendar.$error).toBe(true);
77
+ expect(r$.$fields.calendar.$errors.id).toHaveLength(1);
78
+ expect(r$.$fields.schema.$error).toBe(true);
79
+ expect(r$.$fields.schema.$errors.id).toHaveLength(1);
80
+ });
81
+
54
82
  it('accepts the same draft once the calendar is filled', async () => {
55
83
  const { valid, rootError } = await validateDraft({
56
84
  ...getQueueDefaults(QueueType.PREVIEW_DIALER),
@@ -168,10 +168,35 @@ describe('StaticFilterField', () => {
168
168
 
169
169
  /* static mode used to force hideLabel on every value field, which left
170
170
  select-based filters (has-option-filter-value-field) with no visible
171
- label at all — the value field itself now decides whether to show one */
172
- it('does not force hideLabel on its value field', () => {
173
- const wrapper = mountField(deletableConfig, filtersManager);
171
+ label at all — with showFilterName the value field itself decides whether to show one */
172
+ it('does not force hideLabel on its value field when showFilterName is set', () => {
173
+ const filterConfig = createFilterConfig({
174
+ name: FilterOption.Tag,
175
+ showFilterName: true,
176
+ valueInputComponent: StubInput,
177
+ });
178
+ filterConfig.label = 'Tags';
179
+
180
+ const wrapper = mountField(filterConfig, filtersManager);
181
+
182
+ expect(wrapper.findComponent(StubInput).props('hideLabel')).toBe(false);
183
+ expect(wrapper.findComponent(StubInput).attributes('placeholder')).not.toBe(
184
+ 'Tags',
185
+ );
186
+ });
187
+
188
+ it('hides the label and shows the filter name as placeholder without showFilterName', () => {
189
+ const filterConfig = createFilterConfig({
190
+ name: FilterOption.Tag,
191
+ valueInputComponent: StubInput,
192
+ });
193
+ filterConfig.label = 'Tags';
194
+
195
+ const wrapper = mountField(filterConfig, filtersManager);
174
196
 
175
- expect(wrapper.findComponent(StubInput).props('hideLabel')).toBeUndefined();
197
+ expect(wrapper.findComponent(StubInput).props('hideLabel')).toBe(true);
198
+ expect(wrapper.findComponent(StubInput).attributes('placeholder')).toBe(
199
+ 'Tags',
200
+ );
176
201
  });
177
202
  });
@@ -10,6 +10,8 @@
10
10
  :disable-default-value="true"
11
11
  :disable-validation="true /*for static filters validation is not needed (different presentation with dynamic filters)*/"
12
12
  :filter-config="filterConfig"
13
+ :hide-label="!filterConfig.showFilterName"
14
+ :placeholder="!filterConfig.showFilterName && filterConfig.label"
13
15
  :static-view="true"
14
16
  :model-value="filterValue"
15
17
  @update:model-value="onValueChange"
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <wt-multi-select
3
+ :label="labelValue"
4
+ :options="options"
5
+ v-model:model-value="model"
6
+ :v="!disableValidation && v$.model"
7
+ chips-view
8
+ data-key="value"
9
+ option-label="label"
10
+ option-value="value"
11
+ />
12
+ </template>
13
+
14
+ <script lang="ts" setup>
15
+ import { useVuelidate } from '@vuelidate/core';
16
+ import { required } from '@vuelidate/validators';
17
+ import { WtMultiSelect } from '@webitel/ui-sdk/components';
18
+ import { computed, onMounted, watch } from 'vue';
19
+ import { useI18n } from 'vue-i18n';
20
+
21
+ import { WtSysTypeFilterConfig } from '../../classes/FilterConfig';
22
+ import { useActionOptions } from '../../composables/useActionOptions';
23
+
24
+ const model = defineModel<string[]>();
25
+ const { t } = useI18n();
26
+
27
+ const props = defineProps<{
28
+ filterConfig?: WtSysTypeFilterConfig;
29
+ disableValidation?: boolean;
30
+ }>();
31
+
32
+ const v$ = useVuelidate(
33
+ computed(() => ({
34
+ model: {
35
+ required,
36
+ },
37
+ })),
38
+ {
39
+ model,
40
+ },
41
+ {
42
+ $autoDirty: true,
43
+ },
44
+ );
45
+
46
+ const emit = defineEmits<{
47
+ 'update:invalid': [
48
+ boolean,
49
+ ];
50
+ }>();
51
+
52
+ const labelKey = computed(() =>
53
+ props?.filterConfig?.showFilterName ? props.filterConfig.name : 'filterValue',
54
+ );
55
+
56
+ const labelValue = computed(() => t(`webitelUI.filters.${labelKey.value}`));
57
+
58
+ const { options } = useActionOptions();
59
+
60
+ onMounted(() => {
61
+ if (!props?.disableValidation) v$.value.$touch();
62
+ });
63
+
64
+ watch(
65
+ () => v$.value.$invalid,
66
+ (invalid) => {
67
+ emit('update:invalid', invalid);
68
+ },
69
+ {
70
+ immediate: true,
71
+ },
72
+ );
73
+ </script>
74
+
75
+ <style scoped></style>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <ul>
3
+ <li
4
+ v-for="(label, index) of arrayValues"
5
+ :key="index"
6
+ >
7
+ {{ label }}
8
+ </li>
9
+ </ul>
10
+ </template>
11
+
12
+ <script lang="ts" setup>
13
+ import { computed } from 'vue';
14
+
15
+ import { useActionOptions } from '../../composables/useActionOptions';
16
+
17
+ const props = defineProps<{
18
+ value?: string[];
19
+ }>();
20
+
21
+ const { options } = useActionOptions();
22
+
23
+ const arrayLabels = computed(() =>
24
+ options.value.reduce<Record<string, string>>((acc, { value, label }) => {
25
+ acc[value] = label;
26
+ return acc;
27
+ }, {}),
28
+ );
29
+
30
+ const arrayValues = computed(() =>
31
+ (props.value ?? []).map((v) => arrayLabels.value[v] || v),
32
+ );
33
+ </script>
34
+
35
+ <style scoped></style>
@@ -5,6 +5,8 @@ import DateTimeOptionsFilterValueField from './_shared/date-time-filter/date-tim
5
5
  import DateTimeOptionsFilterValuePreview from './_shared/date-time-filter/date-time-options/date-time-options-filter-value-preview.vue';
6
6
  import StringFilterValueField from './_shared/string-filter/string-filter-value-field.vue';
7
7
  import StringFilterValuePreview from './_shared/string-filter/string-filter-value-preview.vue';
8
+ import ActionFilter from './action/action-filter-value-field.vue';
9
+ import ActionFilterPreview from './action/action-filter-value-preview.vue';
8
10
  import { createAgentFilterConfig } from './agent';
9
11
  import AgentFilter from './agent/agent-filter-value-field.vue';
10
12
  import AgentFilterPreview from './agent/agent-filter-value-preview.vue';
@@ -91,6 +93,9 @@ import HasTranscriptionFilter from './has-transcription/has-transcription-filter
91
93
  import HasTranscriptionFilterPreview from './has-transcription/has-transcription-filter-value-preview.vue';
92
94
  import HasUserFilter from './has-user/has-user-filter-value-field.vue';
93
95
  import HasUserFilterPreview from './has-user/has-user-filter-value-preview.vue';
96
+ import { createObjectFilterConfig } from './object';
97
+ import ObjectFilter from './object/object-filter-value-field.vue';
98
+ import ObjectFilterPreview from './object/object-filter-value-preview.vue';
94
99
  import { createQueueFilterConfig } from './queue';
95
100
  import QueueFilter from './queue/queue-filter-value-field.vue';
96
101
  import QueueFilterPreview from './queue/queue-filter-value-preview.vue';
@@ -137,6 +142,8 @@ import VariableFilter from './variable/variable-filter-value-field.vue';
137
142
  import VariableFilterPreview from './variable/variable-filter-value-preview.vue';
138
143
 
139
144
  export {
145
+ ActionFilter,
146
+ ActionFilterPreview,
140
147
  AgentFilter,
141
148
  AgentFilterPreview,
142
149
  AgentStatusFilter,
@@ -204,6 +211,8 @@ export {
204
211
  HasTranscriptionFilterPreview,
205
212
  HasUserFilter,
206
213
  HasUserFilterPreview,
214
+ ObjectFilter,
215
+ ObjectFilterPreview,
207
216
  QueueFilter,
208
217
  QueueFilterPreview,
209
218
  QueuePeriodFilter,
@@ -285,6 +294,8 @@ export const FilterOptionToValueComponentMap: Record<FilterOption, Component> =
285
294
  [FilterOption.MemberName]: StringFilterValueField,
286
295
  [FilterOption.MemberDestination]: StringFilterValueField,
287
296
  [FilterOption.CallReportingResult]: CallReportingResultFilter,
297
+ [FilterOption.Action]: ActionFilter,
298
+ [FilterOption.Object]: ObjectFilter,
288
299
  [FilterOption.CaseStatus]: CaseStatusFilterValueField,
289
300
  [FilterOption.CaseSource]: CaseSourceFilterValueField,
290
301
  [FilterOption.CaseService]: CaseServiceFilterValueField,
@@ -321,6 +332,8 @@ export const FilterOptionToPreviewComponentMap: Record<
321
332
  [FilterOption.MemberName]: StringFilterValuePreview,
322
333
  [FilterOption.MemberDestination]: StringFilterValuePreview,
323
334
  [FilterOption.CallReportingResult]: CallReportingResultFilterPreview,
335
+ [FilterOption.Action]: ActionFilterPreview,
336
+ [FilterOption.Object]: ObjectFilterPreview,
324
337
  [FilterOption.Agent]: AgentFilterPreview,
325
338
  [FilterOption.Region]: RegionFilterPreview,
326
339
  [FilterOption.UtilizationProgress]: UtilizationProgressFilterPreview,
@@ -385,6 +398,7 @@ export const FilterOptionToFilterConfigCreatorMap = {
385
398
  [FilterOption.CaseSlaCondition]: createCaseSlaConditionFilterConfig,
386
399
  [FilterOption.CaseSource]: createCaseSourceFilterConfig,
387
400
  [FilterOption.CaseStatus]: createCaseStatusFilterConfig,
401
+ [FilterOption.Object]: createObjectFilterConfig,
388
402
  [FilterOption.Contact]: createContactFilterConfig,
389
403
  [FilterOption.ContactGroup]: createContactGroupFilterConfig,
390
404
  [FilterOption.ContactLabel]: createContactLabelFilterConfig,
@@ -0,0 +1,29 @@
1
+ import { ChangelogsAPI } from '@webitel/api-services/api';
2
+ import { WtObject } from '@webitel/ui-sdk/enums';
3
+
4
+ import {
5
+ type FilterConfigBaseParams,
6
+ WtSysTypeFilterConfig,
7
+ } from '../../classes/FilterConfig';
8
+ import { gateFilterSearch } from '../../composables/useFilterReadAccess';
9
+ import { FilterOption } from '../../enums/FilterOption';
10
+ import ObjectFilterValueField from './object-filter-value-field.vue';
11
+ import ObjectFilterValuePreview from './object-filter-value-preview.vue';
12
+
13
+ class ObjectFilterConfig extends WtSysTypeFilterConfig {
14
+ readonly name = FilterOption.Object;
15
+ valueInputComponent = ObjectFilterValueField;
16
+ valuePreviewComponent = ObjectFilterValuePreview;
17
+
18
+ searchRecords = gateFilterSearch(
19
+ WtObject.ChangeLog,
20
+ (params: Record<string, unknown>) =>
21
+ ChangelogsAPI.getObjectsList({
22
+ ...params,
23
+ includeExisting: true,
24
+ }),
25
+ );
26
+ }
27
+
28
+ export const createObjectFilterConfig = (params?: FilterConfigBaseParams) =>
29
+ new ObjectFilterConfig(params);
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <wt-multi-select
3
+ :label="labelValue"
4
+ :disabled="!hasReadAccess"
5
+ :search-method="searchMethod"
6
+ :model-value="model"
7
+ :v="!disableValidation && v$.model"
8
+ option-value="id"
9
+ @update:model-value="handleInput"
10
+ />
11
+ </template>
12
+
13
+ <script lang="ts" setup>
14
+ import { useVuelidate } from '@vuelidate/core';
15
+ import { required } from '@vuelidate/validators';
16
+ import { WtMultiSelect } from '@webitel/ui-sdk/components';
17
+ import { WtObject } from '@webitel/ui-sdk/enums';
18
+ import { computed, onMounted, watch } from 'vue';
19
+ import { useI18n } from 'vue-i18n';
20
+
21
+ import { WtSysTypeFilterConfig } from '../../classes/FilterConfig';
22
+ import { useFilterReadAccess } from '../../composables/useFilterReadAccess';
23
+
24
+ type ModelValue = number[];
25
+
26
+ const props = defineProps<{
27
+ filterConfig: WtSysTypeFilterConfig;
28
+ disableValidation?: boolean;
29
+ }>();
30
+
31
+ const model = defineModel<ModelValue>();
32
+
33
+ const emit = defineEmits<{
34
+ 'update:invalid': [
35
+ boolean,
36
+ ];
37
+ }>();
38
+ const { t } = useI18n();
39
+
40
+ const { hasReadAccess, gateSearch } = useFilterReadAccess(WtObject.ChangeLog);
41
+ const searchMethod = gateSearch(props.filterConfig.searchRecords);
42
+
43
+ const labelKey = computed(() =>
44
+ props?.filterConfig?.showFilterName ? props.filterConfig.name : 'filterValue',
45
+ );
46
+
47
+ const labelValue = computed(() => t(`webitelUI.filters.${labelKey.value}`));
48
+
49
+ const v$ = useVuelidate(
50
+ computed(() => ({
51
+ model: {
52
+ required,
53
+ },
54
+ })),
55
+ {
56
+ model,
57
+ },
58
+ {
59
+ $autoDirty: true,
60
+ },
61
+ );
62
+
63
+ onMounted(() => {
64
+ if (!props?.disableValidation) v$.value.$touch();
65
+ });
66
+
67
+ watch(
68
+ () => v$.value.$invalid,
69
+ (invalid) => {
70
+ emit('update:invalid', invalid);
71
+ },
72
+ {
73
+ immediate: true,
74
+ },
75
+ );
76
+
77
+ const handleInput = (value: ModelValue) => {
78
+ model.value = value;
79
+ };
80
+ </script>
81
+
82
+ <style scoped></style>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <lookup-filter-value-preview v-bind="props" />
3
+ </template>
4
+
5
+ <script lang="ts" setup>
6
+ import LookupFilterValuePreview from '../_shared/lookup-filter-preview/lookup-filter-value-preview.vue';
7
+
8
+ const props = defineProps<{
9
+ value: unknown[];
10
+ }>();
11
+ </script>
12
+
13
+ <style scoped></style>
@@ -0,0 +1,24 @@
1
+ import { LoggerAction } from '@webitel/api-services/gen/models';
2
+ import { computed } from 'vue';
3
+ import { useI18n } from 'vue-i18n';
4
+
5
+ const ActionLocale = {
6
+ [LoggerAction.Create]: 'reusable.create',
7
+ [LoggerAction.Update]: 'reusable.edit',
8
+ [LoggerAction.Delete]: 'reusable.delete',
9
+ } as const;
10
+
11
+ export function useActionOptions() {
12
+ const { t } = useI18n();
13
+
14
+ const options = computed(() =>
15
+ Object.entries(ActionLocale).map(([value, locale]) => ({
16
+ value,
17
+ label: t(locale),
18
+ })),
19
+ );
20
+
21
+ return {
22
+ options,
23
+ };
24
+ }
@@ -60,6 +60,9 @@ export const FilterOption = {
60
60
  MemberName: 'name',
61
61
  MemberDestination: 'destination',
62
62
  CallReportingResult: 'result' /** queue attempt CallReportingStatus */,
63
+ Action: 'action',
64
+ Object: 'object',
65
+ Date: 'date',
63
66
  } as const;
64
67
 
65
68
  /**