@webitel/ui-datalist 26.8.25 → 26.8.27

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-datalist",
3
- "version": "26.8.25",
3
+ "version": "26.8.27",
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",
@@ -1,9 +1,14 @@
1
1
  import { useRegleSchema } from '@regle/schemas';
2
2
  import { getQueueDefaults } from '@webitel/api-services/api';
3
3
  import { QueueType } from '@webitel/api-services/enums';
4
- import { queueSchema } from '@webitel/api-services/validations';
4
+ import { getDefaultsFromZodSchema } from '@webitel/api-services/utils';
5
+ import {
6
+ caseCloseFieldsChecks,
7
+ caseSchema,
8
+ queueSchema,
9
+ } from '@webitel/api-services/validations';
5
10
  import { describe, expect, it } from 'vitest';
6
- import { effectScope, ref } from 'vue';
11
+ import { effectScope, nextTick, ref } from 'vue';
7
12
 
8
13
  /**
9
14
  * A card whose required-ness comes from a root `superRefine` — the queue, whose
@@ -93,3 +98,39 @@ describe('card validation of a root-level required rule', () => {
93
98
  expect(rootError).toBe(false);
94
99
  });
95
100
  });
101
+
102
+ describe('card validation of the close reason on a new final case', () => {
103
+ const caseCardSchema = caseSchema
104
+ .passthrough()
105
+ .check(...caseCloseFieldsChecks);
106
+
107
+ it('reports the missing close reason on the field', async () => {
108
+ const state = ref(getDefaultsFromZodSchema(caseCardSchema, {}) as never);
109
+ const scope = effectScope(true);
110
+ // biome-ignore lint/suspicious/noExplicitAny: regle's inferred state type
111
+ let r$: any;
112
+
113
+ scope.run(() => {
114
+ ({ r$ } = useRegleSchema(state, caseCardSchema as never, {
115
+ autoDirty: true,
116
+ syncState: {
117
+ onValidate: true,
118
+ },
119
+ }));
120
+ });
121
+ await nextTick();
122
+
123
+ (state.value as Record<string, unknown>).statusCondition = {
124
+ id: '1',
125
+ final: true,
126
+ };
127
+ await nextTick();
128
+
129
+ const result = await r$.$validate();
130
+
131
+ expect(result.valid).toBe(false);
132
+ expect(r$.$fields.closeReason.$error).toBe(true);
133
+ expect(r$.$fields.closeReason.$errors).toHaveLength(1);
134
+ expect(r$.$fields.closeResult.$error).toBe(true);
135
+ });
136
+ });
@@ -1,24 +1,26 @@
1
1
  import type { DataField } from '@webitel/api-services/gen/models';
2
2
  import type { WtTableHeader } from '@webitel/ui-sdk/components/wt-table/types/WtTable';
3
- import { WtTypeExtensionFieldKind } from '@webitel/ui-sdk/enums';
4
3
  import { isVariableHeader } from '@webitel/ui-sdk/modules/TableVariableColumnSelect';
5
4
  import { computed, type MaybeRefOrGetter, toValue } from 'vue';
6
5
  import { useI18n } from 'vue-i18n';
7
6
 
8
7
  import type { FilterInitParams, FilterName, IFilter } from '../classes/Filter';
9
8
  import type { IFiltersManager } from '../classes/FiltersManager';
9
+ import {
10
+ isVariableKeyFilterValue,
11
+ VariableKeyFilterConfig,
12
+ } from '../modules/filterConfig/components/variable/variableKeyFilterConfig';
10
13
  import { FilterOption } from '../modules/filterConfig/enums/FilterOption';
11
14
  import {
12
15
  isVariableFilterName,
13
16
  toVariableFilterFields,
14
17
  VARIABLE_FIELD_PREFIX,
15
- variableKeyFromFilterName,
16
18
  } from '../scripts/variableFilters';
17
19
 
18
20
  const parseVariableFilterValue = (raw: string) =>
19
21
  raw.split('&').reduce<Record<string, string>>((vars, pair) => {
20
- const [key, value] = pair.split('=');
21
- if (key) vars[key] = value ?? '';
22
+ const [key, ...valueParts] = pair.split('=');
23
+ if (key) vars[key] = valueParts.join('=');
22
24
  return vars;
23
25
  }, {});
24
26
 
@@ -37,49 +39,39 @@ export const useVariableColumnFilters = ({
37
39
  }) => {
38
40
  const { t } = useI18n();
39
41
 
40
- const withVariableFilterLabel = (
41
- params: FilterInitParams,
42
- ): FilterInitParams =>
43
- isVariableFilterName(params.name)
44
- ? {
45
- ...params,
46
- label: t('webitelUI.filters.variable'),
47
- }
48
- : params;
49
-
50
42
  const variableFilterFields = computed<DataField[]>(() => {
51
43
  const headers = toValue(shownHeaders) || [];
52
44
  const columnHeaders = headers.filter(
53
45
  (header) => isVariableHeader(header) && header.filtered,
54
46
  );
55
- const columnFields = toVariableFilterFields(columnHeaders);
56
47
 
57
- const knownNames = new Set(columnFields.map((field) => field.id));
58
- const appliedOnlyFields = toValue(filtersManager)
59
- .getAllKeys()
60
- .filter((name) => isVariableFilterName(name) && !knownNames.has(name))
61
- .map((name) => ({
62
- id: name,
63
- name: variableKeyFromFilterName(name),
64
- kind: WtTypeExtensionFieldKind.Text,
65
- }));
66
-
67
- return [
68
- ...columnFields,
69
- ...appliedOnlyFields,
70
- ];
48
+ return toVariableFilterFields(columnHeaders).filter(
49
+ (field) => !field.id || !toValue(filtersManager).hasFilter(field.id),
50
+ );
71
51
  });
72
52
 
53
+ const variableFilterConfigs = computed(() =>
54
+ toValue(filtersManager)
55
+ .getAllKeys()
56
+ .filter(isVariableFilterName)
57
+ .map(
58
+ (name) =>
59
+ new VariableKeyFilterConfig({
60
+ name,
61
+ label: t('webitelUI.filters.variable'),
62
+ }),
63
+ ),
64
+ );
65
+
73
66
  const splitVariableFilter = (params: FilterInitParams) => {
74
67
  const variables = parseVariableFilterValue(String(params.value ?? ''));
75
68
 
76
69
  Object.entries(variables).forEach(([key, value]) => {
77
- addFilter(
78
- withVariableFilterLabel({
79
- name: `${VARIABLE_FIELD_PREFIX}${key}`,
80
- value,
81
- }),
82
- );
70
+ addFilter({
71
+ name: `${VARIABLE_FIELD_PREFIX}${key}`,
72
+ value,
73
+ label: params.label,
74
+ });
83
75
  });
84
76
 
85
77
  if (toValue(filtersManager).hasFilter(FilterOption.Variable)) {
@@ -95,7 +87,7 @@ export const useVariableColumnFilters = ({
95
87
  return;
96
88
  }
97
89
 
98
- addFilter(withVariableFilterLabel(params));
90
+ addFilter(params);
99
91
  };
100
92
 
101
93
  const handleUpdateFilter = (params: FilterInitParams) => {
@@ -104,12 +96,52 @@ export const useVariableColumnFilters = ({
104
96
  return;
105
97
  }
106
98
 
107
- updateFilter(withVariableFilterLabel(params));
99
+ updateFilter(params);
100
+ };
101
+
102
+ const updateVariableKeyFilter = ({
103
+ name,
104
+ value,
105
+ label,
106
+ }: FilterInitParams) => {
107
+ if (!isVariableKeyFilterValue(value)) {
108
+ updateFilter({
109
+ name,
110
+ value,
111
+ label,
112
+ });
113
+ return;
114
+ }
115
+
116
+ const newName = `${VARIABLE_FIELD_PREFIX}${value.key}`;
117
+
118
+ if (newName !== name) {
119
+ deleteFilter({
120
+ name,
121
+ });
122
+ }
123
+
124
+ addFilter({
125
+ name: newName,
126
+ value: value.value,
127
+ label,
128
+ });
129
+ };
130
+
131
+ const handlePanelUpdateFilter = (params: FilterInitParams) => {
132
+ if (isVariableFilterName(params.name)) {
133
+ updateVariableKeyFilter(params);
134
+ return;
135
+ }
136
+
137
+ handleUpdateFilter(params);
108
138
  };
109
139
 
110
140
  return {
111
141
  variableFilterFields,
142
+ variableFilterConfigs,
112
143
  handleAddFilter,
113
144
  handleUpdateFilter,
145
+ handlePanelUpdateFilter,
114
146
  };
115
147
  };
@@ -25,7 +25,11 @@ const props = defineProps<{
25
25
 
26
26
  const { t } = useI18n();
27
27
 
28
- const variable = props.value.split('=');
28
+ const [key, ...valueParts] = props.value.split('=');
29
+ const variable = [
30
+ key,
31
+ valueParts.join('='),
32
+ ];
29
33
  </script>
30
34
 
31
35
  <style lang="scss" scoped>
@@ -0,0 +1,99 @@
1
+ <template>
2
+ <div class="variable-key-filter-value-field">
3
+ <wt-input-text
4
+ :model-value="variable.key"
5
+ :label="t('vocabulary.keys')"
6
+ :v="!disableValidation && v$.variable.key"
7
+ @update:model-value="updateVariable({ key: $event })"
8
+ />
9
+ <wt-input-text
10
+ :model-value="variable.value"
11
+ :label="t('vocabulary.values')"
12
+ :v="!disableValidation && v$.variable.value"
13
+ @update:model-value="updateVariable({ value: $event })"
14
+ />
15
+ </div>
16
+ </template>
17
+
18
+ <script lang="ts" setup>
19
+ import { useVuelidate } from '@vuelidate/core';
20
+ import { required } from '@vuelidate/validators';
21
+ import { computed, watch } from 'vue';
22
+ import { useI18n } from 'vue-i18n';
23
+
24
+ import {
25
+ type IVariableKeyFilterConfig,
26
+ isVariableKeyFilterValue,
27
+ type VariableKeyFilterValue,
28
+ } from './variableKeyFilterConfig';
29
+
30
+ const props = defineProps<{
31
+ filterConfig: IVariableKeyFilterConfig;
32
+ disableValidation?: boolean;
33
+ }>();
34
+
35
+ const emit = defineEmits<{
36
+ 'update:invalid': [
37
+ boolean,
38
+ ];
39
+ }>();
40
+
41
+ const model = defineModel<string | VariableKeyFilterValue>();
42
+
43
+ const { t } = useI18n();
44
+
45
+ const variable = computed<VariableKeyFilterValue>(() =>
46
+ isVariableKeyFilterValue(model.value)
47
+ ? model.value
48
+ : {
49
+ key: props.filterConfig.variableKey,
50
+ value: model.value ?? '',
51
+ },
52
+ );
53
+
54
+ const updateVariable = (patch: Partial<VariableKeyFilterValue>) => {
55
+ model.value = {
56
+ ...variable.value,
57
+ ...patch,
58
+ };
59
+ };
60
+
61
+ const v$ = useVuelidate(
62
+ computed(() => ({
63
+ variable: {
64
+ key: {
65
+ required,
66
+ },
67
+ value: {
68
+ required,
69
+ },
70
+ },
71
+ })),
72
+ {
73
+ variable,
74
+ },
75
+ {
76
+ $autoDirty: true,
77
+ },
78
+ );
79
+
80
+ if (!props.disableValidation) v$.value.$touch();
81
+
82
+ watch(
83
+ () => v$.value.$invalid,
84
+ (invalid) => {
85
+ emit('update:invalid', invalid);
86
+ },
87
+ {
88
+ immediate: true,
89
+ },
90
+ );
91
+ </script>
92
+
93
+ <style lang="scss" scoped>
94
+ .variable-key-filter-value-field {
95
+ display: flex;
96
+ flex-direction: column;
97
+ gap: var(--spacing-xs);
98
+ }
99
+ </style>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <variable-filter-value-preview
3
+ :key="variableValue"
4
+ :value="variableValue"
5
+ />
6
+ </template>
7
+
8
+ <script lang="ts" setup>
9
+ import { computed } from 'vue';
10
+
11
+ import VariableFilterValuePreview from './variable-filter-value-preview.vue';
12
+ import type { IVariableKeyFilterConfig } from './variableKeyFilterConfig';
13
+
14
+ const props = defineProps<{
15
+ filterConfig: IVariableKeyFilterConfig;
16
+ value?: unknown;
17
+ }>();
18
+
19
+ const variableValue = computed(
20
+ () => `${props.filterConfig.variableKey}=${props.value ?? ''}`,
21
+ );
22
+ </script>
23
+
24
+ <style scoped></style>
@@ -0,0 +1,48 @@
1
+ import { z } from 'zod/v4';
2
+
3
+ import type { FilterName, FilterValue } from '../../../../classes/Filter';
4
+ import { variableKeyFromFilterName } from '../../../../scripts/variableFilters';
5
+ import type { BaseFilterConfig } from '../../classes/FilterConfig';
6
+ import { FilterConfig } from '../../classes/FilterConfig';
7
+ import VariableKeyFilterValueField from './variable-key-filter-value-field.vue';
8
+ import VariableKeyFilterValuePreview from './variable-key-filter-value-preview.vue';
9
+
10
+ export interface IVariableKeyFilterConfig extends BaseFilterConfig {
11
+ readonly variableKey: string;
12
+ }
13
+
14
+ const variableKeyValueSchema = z.object({
15
+ key: z.string(),
16
+ value: z.string(),
17
+ });
18
+
19
+ export type VariableKeyFilterValue = z.infer<typeof variableKeyValueSchema>;
20
+
21
+ export const isVariableKeyFilterValue = (
22
+ value: FilterValue,
23
+ ): value is VariableKeyFilterValue =>
24
+ variableKeyValueSchema.safeParse(value).success;
25
+
26
+ export class VariableKeyFilterConfig
27
+ extends FilterConfig
28
+ implements IVariableKeyFilterConfig
29
+ {
30
+ readonly variableKey: string;
31
+
32
+ constructor({
33
+ name,
34
+ label,
35
+ }: {
36
+ name: FilterName;
37
+ label: string;
38
+ }) {
39
+ super({
40
+ name,
41
+ valueInputComponent: VariableKeyFilterValueField,
42
+ valuePreviewComponent: VariableKeyFilterValuePreview,
43
+ });
44
+
45
+ this.label = label;
46
+ this.variableKey = variableKeyFromFilterName(name);
47
+ }
48
+ }