@webitel/api-services 26.8.70 → 26.8.72

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 (55) hide show
  1. package/package.json +1 -1
  2. package/src/api/clients/outboundResources/outboundResources.ts +44 -81
  3. package/src/api/clients/outboundResources/resourceDisplays.ts +106 -32
  4. package/src/enums/ResourceCidType/ResourceCidType.ts +8 -0
  5. package/src/enums/ResourceIgnoreEarlyMedia/ResourceIgnoreEarlyMedia.ts +9 -0
  6. package/src/enums/index.ts +4 -0
  7. package/src/locale/en/en.ts +4 -0
  8. package/src/locale/es/es.ts +4 -0
  9. package/src/locale/kz/kz.ts +4 -0
  10. package/src/locale/pl/pl.ts +4 -0
  11. package/src/locale/ro/ro.ts +4 -0
  12. package/src/locale/ru/ru.ts +4 -0
  13. package/src/locale/uk/uk.ts +4 -0
  14. package/src/locale/uz/uz.ts +4 -0
  15. package/src/locale/vi/vi.ts +4 -0
  16. package/src/scripts/convertTimeUnits/__tests__/convertTimeUnits.spec.ts +19 -0
  17. package/src/scripts/convertTimeUnits/convertTimeUnits.ts +3 -0
  18. package/src/scripts/index.ts +3 -0
  19. package/src/validations/_shared/timeRange.validations.ts +103 -0
  20. package/src/validations/calendar/__tests__/calendar.validations.spec.ts +9 -9
  21. package/src/validations/calendar/calendar.validations.ts +10 -131
  22. package/src/validations/index.ts +5 -0
  23. package/src/validations/resource/__tests__/resource.validations.spec.ts +102 -0
  24. package/src/validations/resource/resource.validations.ts +28 -0
  25. package/src/validations/resourceDisplay/__tests__/resourceDisplay.validations.spec.ts +45 -0
  26. package/src/validations/resourceDisplay/resourceDisplay.validations.ts +9 -0
  27. package/src/validations/resourceGroup/__tests__/resourceGroup.validations.spec.ts +191 -0
  28. package/src/validations/resourceGroup/resourceGroup.validations.ts +36 -0
  29. package/src/validations/resourceInGroup/__tests__/resourceInGroup.validations.spec.ts +38 -0
  30. package/src/validations/resourceInGroup/resourceInGroup.validations.ts +16 -0
  31. package/types/.tsbuildinfo +1 -1
  32. package/types/api/clients/outboundResources/outboundResources.d.ts +2 -7
  33. package/types/api/clients/outboundResources/resourceDisplays.d.ts +7 -1
  34. package/types/enums/ResourceCidType/ResourceCidType.d.ts +6 -0
  35. package/types/enums/ResourceIgnoreEarlyMedia/ResourceIgnoreEarlyMedia.d.ts +7 -0
  36. package/types/enums/index.d.ts +3 -1
  37. package/types/locale/en/en.d.ts +3 -0
  38. package/types/locale/es/es.d.ts +3 -0
  39. package/types/locale/index.d.ts +27 -0
  40. package/types/locale/kz/kz.d.ts +3 -0
  41. package/types/locale/pl/pl.d.ts +3 -0
  42. package/types/locale/ro/ro.d.ts +3 -0
  43. package/types/locale/ru/ru.d.ts +3 -0
  44. package/types/locale/uk/uk.d.ts +3 -0
  45. package/types/locale/uz/uz.d.ts +3 -0
  46. package/types/locale/vi/vi.d.ts +3 -0
  47. package/types/scripts/convertTimeUnits/convertTimeUnits.d.ts +2 -0
  48. package/types/scripts/index.d.ts +2 -1
  49. package/types/validations/_shared/timeRange.validations.d.ts +18 -0
  50. package/types/validations/calendar/calendar.validations.d.ts +1 -7
  51. package/types/validations/index.d.ts +5 -0
  52. package/types/validations/resource/resource.validations.d.ts +26 -0
  53. package/types/validations/resourceDisplay/resourceDisplay.validations.d.ts +5 -0
  54. package/types/validations/resourceGroup/resourceGroup.validations.d.ts +17 -0
  55. package/types/validations/resourceInGroup/resourceInGroup.validations.d.ts +8 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/api-services",
3
- "version": "26.8.70",
3
+ "version": "26.8.72",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,5 +1,11 @@
1
- import deepCopy from 'deep-copy';
2
- import { getOutboundResourceService } from '../../../gen-wire';
1
+ import { getShallowFieldsToSendFromZodSchema } from '@webitel/api-services/gen/utils';
2
+ import {
3
+ CreateOutboundResourceBody,
4
+ getOutboundResourceService,
5
+ PatchOutboundResourceBody,
6
+ SearchOutboundResourceQueryParams,
7
+ UpdateOutboundResourceBody,
8
+ } from '../../../gen-wire';
3
9
  import { getDefaultGetListResponse, getDefaultGetParams } from '../../defaults';
4
10
  import {
5
11
  applyTransform,
@@ -7,7 +13,7 @@ import {
7
13
  merge,
8
14
  mergeEach,
9
15
  notify,
10
- sanitize,
16
+ sanitizeToWire,
11
17
  snakeToCamel,
12
18
  starToSearch,
13
19
  } from '../../transformers';
@@ -23,54 +29,28 @@ import type {
23
29
 
24
30
  const baseUrl = '/call_center/resources';
25
31
 
26
- const fieldsToSend = [
27
- 'limit',
28
- 'enabled',
29
- 'rps',
30
- 'maxSuccessivelyErrors',
31
- 'name',
32
- 'errorIds',
33
- 'display',
34
- 'description',
35
- 'resourceId',
36
- 'gateway',
37
- 'patterns',
38
- 'failureDialDelay',
39
- 'parameters',
40
- ];
41
-
42
- /*
43
- * The form binds `maxErrors` / `cps`, the API expects
44
- * `maxSuccessivelyErrors` / `rps`. `getResource` maps them back.
45
- */
46
- const preRequestHandler = (item: ApiParams) => {
47
- const copy = deepCopy(item);
48
- copy.maxSuccessivelyErrors = copy.maxErrors;
49
- copy.rps = copy.cps;
50
- return copy;
51
- };
52
-
53
- const getResourcesList = async (params: ApiParams) => {
32
+ const getOutboundResourcesList = async (params: ApiParams) => {
54
33
  const defaultObject = {
55
34
  gateway: null,
56
35
  enabled: false,
57
36
  };
58
37
 
59
- const { page, size, search, sort, fields, id } = applyTransform(params, [
38
+ const requestParams = applyTransform(params, [
60
39
  merge(getDefaultGetParams()),
61
40
  starToSearch('search'),
41
+ (params: ApiParams) => ({
42
+ ...params,
43
+ q: params.q ?? params.search,
44
+ }),
45
+ sanitizeToWire(
46
+ getShallowFieldsToSendFromZodSchema(SearchOutboundResourceQueryParams),
47
+ ),
48
+ camelToSnake(),
62
49
  ]);
63
50
 
64
51
  try {
65
- const response = await getOutboundResourceService().searchOutboundResource({
66
- page,
67
- size,
68
- // the generated param is `q`; `search` is what the datalist store sends
69
- q: search,
70
- sort,
71
- fields,
72
- id,
73
- });
52
+ const response =
53
+ await getOutboundResourceService().searchOutboundResource(requestParams);
74
54
  const { items, next } = applyTransform(response.data, [
75
55
  snakeToCamel(),
76
56
  merge(getDefaultGetListResponse()),
@@ -88,34 +68,14 @@ const getResourcesList = async (params: ApiParams) => {
88
68
  }
89
69
  };
90
70
 
91
- const getResource = async ({ itemId: id }: GetItemParams) => {
71
+ const getOutboundResource = async ({ itemId: id }: GetItemParams) => {
92
72
  const defaultObject = {
93
- name: '',
94
- gateway: {},
95
- rps: 0,
96
- limit: 0,
97
- description: '',
98
- maxSuccessivelyErrors: 0,
99
- errorIds: [],
100
- patterns: [],
101
- failureDialDelay: 0,
102
73
  parameters: {
103
74
  cidType: '',
104
75
  ignoreEarlyMedia: '',
105
76
  },
106
77
  };
107
78
 
108
- const responseHandler = (response: ApiParams) => {
109
- const copy = deepCopy(response);
110
- copy.maxErrors = copy.maxSuccessivelyErrors;
111
- copy.cps = copy.rps;
112
- copy.parameters = {
113
- ...defaultObject.parameters,
114
- ...copy.parameters,
115
- };
116
- return copy;
117
- };
118
-
119
79
  try {
120
80
  const response = await getOutboundResourceService().readOutboundResource(
121
81
  String(id),
@@ -123,7 +83,6 @@ const getResource = async ({ itemId: id }: GetItemParams) => {
123
83
  return applyTransform(response.data, [
124
84
  snakeToCamel(),
125
85
  merge(defaultObject),
126
- responseHandler,
127
86
  ]);
128
87
  } catch (err) {
129
88
  throw applyTransform(err, [
@@ -132,10 +91,11 @@ const getResource = async ({ itemId: id }: GetItemParams) => {
132
91
  }
133
92
  };
134
93
 
135
- const addResource = async ({ itemInstance }: AddItemParams) => {
94
+ const addOutboundResource = async ({ itemInstance }: AddItemParams) => {
136
95
  const item = applyTransform(itemInstance, [
137
- preRequestHandler,
138
- sanitize(fieldsToSend),
96
+ sanitizeToWire(
97
+ getShallowFieldsToSendFromZodSchema(CreateOutboundResourceBody),
98
+ ),
139
99
  camelToSnake(),
140
100
  ]);
141
101
  try {
@@ -151,13 +111,14 @@ const addResource = async ({ itemInstance }: AddItemParams) => {
151
111
  }
152
112
  };
153
113
 
154
- const updateResource = async ({
114
+ const updateOutboundResource = async ({
155
115
  itemInstance,
156
116
  itemId: id,
157
117
  }: UpdateItemParams) => {
158
118
  const item = applyTransform(itemInstance, [
159
- preRequestHandler,
160
- sanitize(fieldsToSend),
119
+ sanitizeToWire(
120
+ getShallowFieldsToSendFromZodSchema(UpdateOutboundResourceBody),
121
+ ),
161
122
  camelToSnake(),
162
123
  ]);
163
124
  try {
@@ -175,9 +136,11 @@ const updateResource = async ({
175
136
  }
176
137
  };
177
138
 
178
- const patchResource = async ({ changes, id }: PatchItemParams) => {
139
+ const patchOutboundResource = async ({ changes, id }: PatchItemParams) => {
179
140
  const body = applyTransform(changes, [
180
- sanitize(fieldsToSend),
141
+ sanitizeToWire(
142
+ getShallowFieldsToSendFromZodSchema(PatchOutboundResourceBody),
143
+ ),
181
144
  camelToSnake(),
182
145
  ]);
183
146
  try {
@@ -195,7 +158,7 @@ const patchResource = async ({ changes, id }: PatchItemParams) => {
195
158
  }
196
159
  };
197
160
 
198
- const deleteResource = async ({ id }: DeleteItemParams) => {
161
+ const deleteOutboundResource = async ({ id }: DeleteItemParams) => {
199
162
  try {
200
163
  const response = await getOutboundResourceService().deleteOutboundResource(
201
164
  String(id),
@@ -208,8 +171,8 @@ const deleteResource = async ({ id }: DeleteItemParams) => {
208
171
  }
209
172
  };
210
173
 
211
- const getResourcesLookup = (params: Parameters<typeof getResourcesList>[0]) =>
212
- getResourcesList({
174
+ const getOutboundResourcesLookup = (params: ApiParams) =>
175
+ getOutboundResourcesList({
213
176
  ...params,
214
177
  fields: params.fields || [
215
178
  'id',
@@ -218,13 +181,13 @@ const getResourcesLookup = (params: Parameters<typeof getResourcesList>[0]) =>
218
181
  });
219
182
 
220
183
  export const OutboundResourcesAPI = {
221
- getList: getResourcesList,
222
- get: getResource,
223
- add: addResource,
224
- patch: patchResource,
225
- update: updateResource,
226
- delete: deleteResource,
227
- getLookup: getResourcesLookup,
184
+ getList: getOutboundResourcesList,
185
+ get: getOutboundResource,
186
+ add: addOutboundResource,
187
+ update: updateOutboundResource,
188
+ patch: patchOutboundResource,
189
+ delete: deleteOutboundResource,
190
+ getLookup: getOutboundResourcesLookup,
228
191
 
229
192
  ...generatePermissionsApi(baseUrl),
230
193
  };
@@ -1,11 +1,24 @@
1
- import { getOutboundResourceService } from '../../../gen-wire';
2
- import { getDefaultGetListResponse, getDefaultGetParams } from '../../defaults';
1
+ import { getShallowFieldsToSendFromZodSchema } from '@webitel/api-services/gen/utils';
2
+ import type { AxiosError } from 'axios';
3
+ import type { Composer } from 'vue-i18n';
4
+ import { config } from '../../../config/config';
5
+ import {
6
+ CreateOutboundResourceDisplayBody,
7
+ getOutboundResourceService,
8
+ SearchOutboundResourceDisplayQueryParams,
9
+ UpdateOutboundResourceDisplayBody,
10
+ } from '../../../gen-wire';
11
+ import {
12
+ getDefaultGetListResponse,
13
+ getDefaultGetParams,
14
+ getDefaultInstance,
15
+ } from '../../defaults';
3
16
  import {
4
17
  applyTransform,
5
18
  camelToSnake,
6
19
  merge,
7
20
  notify,
8
- sanitize,
21
+ sanitizeToWire,
9
22
  snakeToCamel,
10
23
  starToSearch,
11
24
  } from '../../transformers';
@@ -18,38 +31,30 @@ import type {
18
31
  NestedUpdateItemParams,
19
32
  } from '../_shared/types';
20
33
 
21
- const fieldsToSend = [
22
- 'display',
23
- 'resourceId',
24
- ];
25
-
26
- const preRequestHandler = (parentId: ApiId) => (item: ApiParams) => ({
27
- ...item,
28
- resourceId: parentId,
29
- });
34
+ const instance = getDefaultInstance();
30
35
 
31
36
  const getResourceDisplaysList = async (params: ApiParams) => {
32
- const { page, size, search, sort, fields, id, parentId } = applyTransform(
33
- params,
34
- [
35
- merge(getDefaultGetParams()),
36
- starToSearch('search'),
37
- ],
38
- );
37
+ const { parentId, ...rest } = params;
38
+ const requestParams = applyTransform(rest, [
39
+ merge(getDefaultGetParams()),
40
+ starToSearch('search'),
41
+ (params: ApiParams) => ({
42
+ ...params,
43
+ q: params.q ?? params.search,
44
+ }),
45
+ sanitizeToWire(
46
+ getShallowFieldsToSendFromZodSchema(
47
+ SearchOutboundResourceDisplayQueryParams,
48
+ ),
49
+ ),
50
+ camelToSnake(),
51
+ ]);
39
52
 
40
53
  try {
41
54
  const response =
42
55
  await getOutboundResourceService().searchOutboundResourceDisplay(
43
56
  String(parentId),
44
- {
45
- page,
46
- size,
47
- // the generated param is `q`; `search` is what the datalist store sends
48
- q: search,
49
- sort,
50
- fields,
51
- id,
52
- },
57
+ requestParams,
53
58
  );
54
59
  const { items, next } = applyTransform(response.data, [
55
60
  snakeToCamel(),
@@ -91,8 +96,9 @@ const addResourceDisplay = async ({
91
96
  itemInstance,
92
97
  }: NestedAddItemParams) => {
93
98
  const item = applyTransform(itemInstance, [
94
- preRequestHandler(parentId),
95
- sanitize(fieldsToSend),
99
+ sanitizeToWire(
100
+ getShallowFieldsToSendFromZodSchema(CreateOutboundResourceDisplayBody),
101
+ ),
96
102
  camelToSnake(),
97
103
  ]);
98
104
  try {
@@ -117,8 +123,9 @@ const updateResourceDisplay = async ({
117
123
  itemId: id,
118
124
  }: NestedUpdateItemParams) => {
119
125
  const item = applyTransform(itemInstance, [
120
- preRequestHandler(parentId),
121
- sanitize(fieldsToSend),
126
+ sanitizeToWire(
127
+ getShallowFieldsToSendFromZodSchema(UpdateOutboundResourceDisplayBody),
128
+ ),
122
129
  camelToSnake(),
123
130
  ]);
124
131
  try {
@@ -156,10 +163,77 @@ const deleteResourceDisplay = async ({
156
163
  }
157
164
  };
158
165
 
166
+ /**
167
+ * [Claude] Invalid numbers in an uploaded file come back with an id that starts
168
+ * with `cc_outbound_resource.validatePhoneNumber`. Only the prefix is known, so
169
+ * `translateError`, which looks the translation up by the exact id, does not fit.
170
+ *
171
+ * [WTEL-10579](https://webitel.atlassian.net/browse/WTEL-10579)
172
+ */
173
+ const translatePhoneNumberError = (
174
+ err: AxiosError<{
175
+ id?: string;
176
+ translation?: string;
177
+ }>,
178
+ ) => {
179
+ const data = err.response?.data;
180
+ if (
181
+ data?.id?.startsWith('cc_outbound_resource.validatePhoneNumber') &&
182
+ config.i18n?.global
183
+ ) {
184
+ data.translation = (config.i18n.global as Composer).t(
185
+ 'backendErrors.ccOutboundResource.validatePhoneNumber',
186
+ );
187
+ }
188
+ return err;
189
+ };
190
+
191
+ /**
192
+ * [Claude] Bulk number import from a csv file: the file itself is sent, and the
193
+ * backend parses it and validates the numbers. `POST /displays/:id` is not in
194
+ * the OpenAPI spec, so there is no generated method for it — the request goes
195
+ * through the default instance instead.
196
+ *
197
+ * [WTEL-10579](https://webitel.atlassian.net/browse/WTEL-10579)
198
+ */
199
+ const uploadResourceDisplays = async ({
200
+ parentId,
201
+ file,
202
+ delimiter,
203
+ map,
204
+ }: {
205
+ parentId: ApiId;
206
+ file: File;
207
+ delimiter: string;
208
+ map: string;
209
+ }) => {
210
+ const formData = new FormData();
211
+ formData.append('file', file);
212
+ formData.append('delimiter', delimiter);
213
+ formData.append('map', map);
214
+
215
+ try {
216
+ const response = await instance.post(`/displays/${parentId}`, formData, {
217
+ headers: {
218
+ 'Content-Type': 'multipart/form-data',
219
+ },
220
+ });
221
+ return applyTransform(response.data, [
222
+ snakeToCamel(),
223
+ ]);
224
+ } catch (err) {
225
+ throw applyTransform(err, [
226
+ translatePhoneNumberError,
227
+ notify,
228
+ ]);
229
+ }
230
+ };
231
+
159
232
  export const ResourceDisplaysAPI = {
160
233
  getList: getResourceDisplaysList,
161
234
  get: getResourceDisplay,
162
235
  add: addResourceDisplay,
163
236
  update: updateResourceDisplay,
164
237
  delete: deleteResourceDisplay,
238
+ upload: uploadResourceDisplays,
165
239
  };
@@ -0,0 +1,8 @@
1
+ export const ResourceCidType = {
2
+ None: 'none',
3
+ Rpid: 'rpid',
4
+ Pid: 'pid',
5
+ } as const;
6
+
7
+ export type ResourceCidType =
8
+ (typeof ResourceCidType)[keyof typeof ResourceCidType];
@@ -0,0 +1,9 @@
1
+ export const ResourceIgnoreEarlyMedia = {
2
+ True: 'true',
3
+ False: 'false',
4
+ Consume: 'consume',
5
+ RingReady: 'ring_ready',
6
+ } as const;
7
+
8
+ export type ResourceIgnoreEarlyMedia =
9
+ (typeof ResourceIgnoreEarlyMedia)[keyof typeof ResourceIgnoreEarlyMedia];
@@ -12,6 +12,8 @@ import {
12
12
  } from './QueueStrategy/QueueStrategy';
13
13
  import { QueueType, QueueTypeName } from './QueueType/QueueType';
14
14
  import { RelativeDatetimeValue } from './RelativeDatetimeValue/RelativeDatetimeValue';
15
+ import { ResourceCidType } from './ResourceCidType/ResourceCidType';
16
+ import { ResourceIgnoreEarlyMedia } from './ResourceIgnoreEarlyMedia/ResourceIgnoreEarlyMedia';
15
17
  import { TimeBaseScore } from './TimeBaseScore/TimeBaseScore';
16
18
  import { TypesResourceStrategy } from './TypesResourceStrategy/TypesResourceStrategy';
17
19
  import { UrlProtocol } from './UrlProtocol/UrlProtocol';
@@ -31,6 +33,8 @@ export {
31
33
  QueueType,
32
34
  QueueTypeName,
33
35
  RelativeDatetimeValue,
36
+ ResourceCidType,
37
+ ResourceIgnoreEarlyMedia,
34
38
  TimeBaseScore,
35
39
  TypesResourceStrategy,
36
40
  UrlProtocol,
@@ -2,6 +2,10 @@ import type { MessageContext } from 'vue-i18n';
2
2
 
3
3
  export default {
4
4
  backendErrors: {
5
+ ccOutboundResource: {
6
+ validatePhoneNumber: ({ linked }: MessageContext) =>
7
+ `File contains invalid numbers. ${linked('validation.phoneNumberSymbolsValidator')}`,
8
+ },
5
9
  contacts: {
6
10
  search: {
7
11
  filters: {
@@ -2,6 +2,10 @@ import type { MessageContext } from 'vue-i18n';
2
2
 
3
3
  export default {
4
4
  backendErrors: {
5
+ ccOutboundResource: {
6
+ validatePhoneNumber: ({ linked }: MessageContext) =>
7
+ `El archivo contiene números incorrectos. ${linked('validation.phoneNumberSymbolsValidator')}`,
8
+ },
5
9
  contacts: {
6
10
  search: {
7
11
  filters: {
@@ -2,6 +2,10 @@ import type { MessageContext } from 'vue-i18n';
2
2
 
3
3
  export default {
4
4
  backendErrors: {
5
+ ccOutboundResource: {
6
+ validatePhoneNumber: ({ linked }: MessageContext) =>
7
+ `Файлда қате нөмірлер бар. ${linked('validation.phoneNumberSymbolsValidator')}`,
8
+ },
5
9
  contacts: {
6
10
  search: {
7
11
  filters: {
@@ -2,6 +2,10 @@ import type { MessageContext } from 'vue-i18n';
2
2
 
3
3
  export default {
4
4
  backendErrors: {
5
+ ccOutboundResource: {
6
+ validatePhoneNumber: ({ linked }: MessageContext) =>
7
+ `Plik zawiera nieprawidłowe numery. ${linked('validation.phoneNumberSymbolsValidator')}`,
8
+ },
5
9
  contacts: {
6
10
  search: {
7
11
  filters: {
@@ -2,6 +2,10 @@ import type { MessageContext } from 'vue-i18n';
2
2
 
3
3
  export default {
4
4
  backendErrors: {
5
+ ccOutboundResource: {
6
+ validatePhoneNumber: ({ linked }: MessageContext) =>
7
+ `Fișierul conține numere incorecte. ${linked('validation.phoneNumberSymbolsValidator')}`,
8
+ },
5
9
  contacts: {
6
10
  search: {
7
11
  filters: {
@@ -2,6 +2,10 @@ import type { MessageContext } from 'vue-i18n';
2
2
 
3
3
  export default {
4
4
  backendErrors: {
5
+ ccOutboundResource: {
6
+ validatePhoneNumber: ({ linked }: MessageContext) =>
7
+ `Файл содержит некорректные номера. ${linked('validation.phoneNumberSymbolsValidator')}`,
8
+ },
5
9
  contacts: {
6
10
  search: {
7
11
  filters: {
@@ -2,6 +2,10 @@ import type { MessageContext } from 'vue-i18n';
2
2
 
3
3
  export default {
4
4
  backendErrors: {
5
+ ccOutboundResource: {
6
+ validatePhoneNumber: ({ linked }: MessageContext) =>
7
+ `Файл містить некоректні номери. ${linked('validation.phoneNumberSymbolsValidator')}`,
8
+ },
5
9
  contacts: {
6
10
  search: {
7
11
  filters: {
@@ -2,6 +2,10 @@ import type { MessageContext } from 'vue-i18n';
2
2
 
3
3
  export default {
4
4
  backendErrors: {
5
+ ccOutboundResource: {
6
+ validatePhoneNumber: ({ linked }: MessageContext) =>
7
+ `Faylda noto'g'ri raqamlar mavjud. ${linked('validation.phoneNumberSymbolsValidator')}`,
8
+ },
5
9
  contacts: {
6
10
  search: {
7
11
  filters: {
@@ -2,6 +2,10 @@ import type { MessageContext } from 'vue-i18n';
2
2
 
3
3
  export default {
4
4
  backendErrors: {
5
+ ccOutboundResource: {
6
+ validatePhoneNumber: ({ linked }: MessageContext) =>
7
+ `Tệp chứa số không hợp lệ. ${linked('validation.phoneNumberSymbolsValidator')}`,
8
+ },
5
9
  contacts: {
6
10
  search: {
7
11
  filters: {
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { minToSec, secToMin } from '../convertTimeUnits';
4
+
5
+ describe('convertTimeUnits', () => {
6
+ it('converts minutes to seconds', () => {
7
+ expect(minToSec(0)).toBe(0);
8
+ expect(minToSec(540)).toBe(32400);
9
+ });
10
+
11
+ it('converts seconds to minutes', () => {
12
+ expect(secToMin(0)).toBe(0);
13
+ expect(secToMin(32400)).toBe(540);
14
+ });
15
+
16
+ it('round-trips a time of day', () => {
17
+ expect(secToMin(minToSec(1439))).toBe(1439);
18
+ });
19
+ });
@@ -0,0 +1,3 @@
1
+ export const minToSec = (min: number) => min * 60;
2
+
3
+ export const secToMin = (sec: number) => sec / 60;
@@ -1,4 +1,5 @@
1
1
  import convertDuration from './convertDuration/convertDuration';
2
+ import { minToSec, secToMin } from './convertTimeUnits/convertTimeUnits';
2
3
  import downloadFile from './downloadFile/downloadFile';
3
4
  import { FileFormat } from './downloadFile/types/fileFormat.types';
4
5
  import {
@@ -18,6 +19,8 @@ export {
18
19
  downloadFile,
19
20
  FileFormat,
20
21
  isRelativeDatetimeValue,
22
+ minToSec,
21
23
  normalizeDatetimeRange,
22
24
  normalizeToTimestamp,
25
+ secToMin,
23
26
  };