@webitel/api-services 26.8.17 → 26.8.18

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/api-services",
3
- "version": "26.8.17",
3
+ "version": "26.8.18",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,19 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * @description
5
+ * An optional number the user is allowed to clear.
6
+ *
7
+ * `wt-input-number` wraps PrimeVue's `InputNumber`, which emits `null` — not
8
+ * `undefined` — when its input is emptied. A plain `z.number().optional()`
9
+ * rejects that with "expected number, received null", which the form shows as
10
+ * a red field with a helper text indistinguishable from a required error, and
11
+ * which blocks saving. Worse, the failure is on the base object, so a
12
+ * `superRefine` carrying the real per-type rules never runs.
13
+ *
14
+ * Accepting `null` keeps an emptied optional field valid; whether the field may
15
+ * be empty at all stays with the required rules that run in `superRefine`.
16
+ *
17
+ * https://webitel.atlassian.net/browse/WTEL-10326
18
+ */
19
+ export const clearableNumberSchema = z.number().nullable().optional();
@@ -1,3 +1,4 @@
1
+ export * from './_shared/clearableNumber.validations';
1
2
  export * from './_shared/duration.validations';
2
3
  export * from './_shared/lookup.validations';
3
4
  export * from './_shared/variablePair.validations';
@@ -133,6 +133,47 @@ describe('queueSchema', () => {
133
133
  expect(issuePaths(queueSchema.safeParse(queue))).not.toContain('team');
134
134
  });
135
135
 
136
+ /**
137
+ * `wt-input-number` wraps PrimeVue's `InputNumber`, which emits `null` when
138
+ * its input is emptied. An optional number that rejects `null` fails on the
139
+ * base object, so `superRefine` never runs and the field reads as required.
140
+ * https://webitel.atlassian.net/browse/WTEL-10326
141
+ */
142
+ it.each(
143
+ allQueueTypes,
144
+ )('accepts cleared optional numbers of type %i', (type) => {
145
+ const queue = validQueueFor(type);
146
+ const required = new Set(requiredPathsFor(type));
147
+ const payload = (queue.payload ?? {}) as AnyRecord;
148
+
149
+ for (const [key, value] of Object.entries(payload)) {
150
+ if (typeof value === 'number' && !required.has(`payload.${key}`)) {
151
+ payload[key] = null;
152
+ }
153
+ }
154
+ if (!required.has('priority')) queue.priority = null;
155
+
156
+ expect(issuePaths(queueSchema.safeParse(queue))).toEqual([]);
157
+ });
158
+
159
+ /** A cleared *required* number still fails — with the rule's message, not zod's. */
160
+ it('reports a cleared required number as required', () => {
161
+ const queue = validQueueFor(QueueType.OUTBOUND_IVR_QUEUE);
162
+ set(queue, 'payload.maxAttempts', null);
163
+
164
+ const result = queueSchema.safeParse(queue);
165
+
166
+ expect(issuePaths(result)).toEqual([
167
+ 'payload.maxAttempts',
168
+ ]);
169
+ expect(result.error?.issues[0]).toMatchObject({
170
+ code: 'custom',
171
+ params: {
172
+ i18nKey: 'required',
173
+ },
174
+ });
175
+ });
176
+
136
177
  /**
137
178
  * Regle builds `$fields` from state keys, so anything the defaults seed must
138
179
  * survive the schema — a key the schema strips has no validation entry, and
@@ -1,6 +1,7 @@
1
1
  import { get } from 'lodash-es';
2
2
  import { z } from 'zod';
3
3
 
4
+ import { clearableNumberSchema } from '../_shared/clearableNumber.validations';
4
5
  import { i18nIssue } from '../_shared/i18nIssue';
5
6
  import { isFilled } from '../_shared/isFilled';
6
7
  import { flexibleLookupSchema } from '../_shared/lookup.validations';
@@ -30,7 +31,7 @@ export const queueSchemaBase = z.object({
30
31
  description: z.string().optional(),
31
32
  type: z.number(),
32
33
  enabled: z.boolean().optional(),
33
- priority: z.number().optional(),
34
+ priority: clearableNumberSchema,
34
35
  tags: z
35
36
  .array(
36
37
  z.object({
@@ -1,5 +1,7 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ import { clearableNumberSchema } from '../_shared/clearableNumber.validations';
4
+
3
5
  import { flexibleLookupSchema } from '../_shared/lookup.validations';
4
6
 
5
7
  /** Answering-machine detection, on the outbound IVR and the two auto-dialers. */
@@ -10,13 +12,13 @@ export const queueAmdSchema = z.object({
10
12
  playback: flexibleLookupSchema.optional(),
11
13
  allowNotSure: z.boolean().optional(),
12
14
  silenceNotSure: z.boolean().optional(),
13
- maxWordLength: z.number().optional(),
14
- maxNumberOfWords: z.number().optional(),
15
- betweenWordsSilence: z.number().optional(),
16
- minWordLength: z.number().optional(),
17
- totalAnalysisTime: z.number().optional(),
18
- silenceThreshold: z.number().optional(),
19
- afterGreetingSilence: z.number().optional(),
20
- greeting: z.number().optional(),
21
- initialSilence: z.number().optional(),
15
+ maxWordLength: clearableNumberSchema,
16
+ maxNumberOfWords: clearableNumberSchema,
17
+ betweenWordsSilence: clearableNumberSchema,
18
+ minWordLength: clearableNumberSchema,
19
+ totalAnalysisTime: clearableNumberSchema,
20
+ silenceThreshold: clearableNumberSchema,
21
+ afterGreetingSilence: clearableNumberSchema,
22
+ greeting: clearableNumberSchema,
23
+ initialSilence: clearableNumberSchema,
22
24
  });
@@ -1,5 +1,7 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ import { clearableNumberSchema } from '../_shared/clearableNumber.validations';
4
+
3
5
  import { queueAmdSchema } from './queueAmd.validations';
4
6
 
5
7
  /**
@@ -16,15 +18,15 @@ import { queueAmdSchema } from './queueAmd.validations';
16
18
  */
17
19
  export const queuePayloadSchema = z.object({
18
20
  // dialing and retries
19
- maxAttempts: z.number().optional(),
20
- minAttempts: z.number().optional(),
21
- originateTimeout: z.number().optional(),
22
- waitBetweenRetries: z.number().optional(),
21
+ maxAttempts: clearableNumberSchema,
22
+ minAttempts: clearableNumberSchema,
23
+ originateTimeout: clearableNumberSchema,
24
+ waitBetweenRetries: clearableNumberSchema,
23
25
  waitBetweenRetriesDesc: z.boolean().optional(),
24
- minDuration: z.number().optional(),
25
- maxCalls: z.number().optional(),
26
- progressiveCount: z.number().optional(),
27
- dialingRate: z.number().optional(),
26
+ minDuration: clearableNumberSchema,
27
+ maxCalls: clearableNumberSchema,
28
+ progressiveCount: clearableNumberSchema,
29
+ dialingRate: clearableNumberSchema,
28
30
  resourceStrategy: z.string().optional(),
29
31
  strictCircuit: z.boolean().optional(),
30
32
  perNumbers: z.boolean().optional(),
@@ -32,33 +34,33 @@ export const queuePayloadSchema = z.object({
32
34
  retryAbandoned: z.boolean().optional(),
33
35
 
34
36
  // predictive dialer only
35
- maxAgentLine: z.number().optional(),
36
- maxAgentLose: z.number().optional(),
37
- maxAbandonedRate: z.number().optional(),
38
- targetAbandonedRate: z.number().optional(),
39
- abandonRateAdjustment: z.number().optional(),
40
- loadFactor: z.number().optional(),
41
- playbackSilence: z.number().optional(),
42
- statisticTime: z.number().optional(),
37
+ maxAgentLine: clearableNumberSchema,
38
+ maxAgentLose: clearableNumberSchema,
39
+ maxAbandonedRate: clearableNumberSchema,
40
+ targetAbandonedRate: clearableNumberSchema,
41
+ abandonRateAdjustment: clearableNumberSchema,
42
+ loadFactor: clearableNumberSchema,
43
+ playbackSilence: clearableNumberSchema,
44
+ statisticTime: clearableNumberSchema,
43
45
 
44
46
  // waiting and distribution
45
47
  timeBaseScore: z.string().optional(),
46
- maxWaitTime: z.number().optional(),
47
- maxWaitingSize: z.number().optional(),
48
- discardAbandonedAfter: z.number().optional(),
48
+ maxWaitTime: clearableNumberSchema,
49
+ maxWaitingSize: clearableNumberSchema,
50
+ discardAbandonedAfter: clearableNumberSchema,
49
51
  manualDistribution: z.boolean().optional(),
50
- minOnlineAgents: z.number().optional(),
51
- maxMemberLimit: z.number().optional(),
52
+ minOnlineAgents: clearableNumberSchema,
53
+ maxMemberLimit: clearableNumberSchema,
52
54
  ignoreCalendar: z.boolean().optional(),
53
55
 
54
56
  // sticky agent
55
- stickyAgentSec: z.number().optional(),
57
+ stickyAgentSec: clearableNumberSchema,
56
58
  stickyIgnoreStatus: z.boolean().optional(),
57
59
 
58
60
  // chat queues only
59
- maxIdleAgent: z.number().optional(),
60
- maxIdleClient: z.number().optional(),
61
- maxIdleDialog: z.number().optional(),
61
+ maxIdleAgent: clearableNumberSchema,
62
+ maxIdleClient: clearableNumberSchema,
63
+ maxIdleDialog: clearableNumberSchema,
62
64
  lastMessageTimeout: z.boolean().optional(),
63
65
 
64
66
  // media
@@ -1,19 +1,21 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ import { clearableNumberSchema } from '../_shared/clearableNumber.validations';
4
+
3
5
  import { flexibleLookupSchema } from '../_shared/lookup.validations';
4
6
 
5
7
  export const prolongationOptionsSchema = z.object({
6
8
  enabled: z.boolean().optional(),
7
9
  isTimeoutRetry: z.boolean().optional(),
8
- prolongationTimeSec: z.number().optional(),
9
- repeatsNumber: z.number().optional(),
10
+ prolongationTimeSec: clearableNumberSchema,
11
+ repeatsNumber: clearableNumberSchema,
10
12
  });
11
13
 
12
14
  export const taskProcessingSchema = z.object({
13
15
  enabled: z.boolean().optional(),
14
16
  formSchema: flexibleLookupSchema.optional(),
15
- sec: z.number().optional(),
16
- renewalSec: z.number().optional(),
17
+ sec: clearableNumberSchema,
18
+ renewalSec: clearableNumberSchema,
17
19
  // `.prefault`, not `.default`: zod 4's `.default({})` does not recurse, so
18
20
  // the nested keys would be missing and Regle would build no `$fields` for
19
21
  // them — the prolongation block would lose its validation silently.