@webitel/api-services 0.1.69 → 0.1.70

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 (89) hide show
  1. package/package.json +3 -1
  2. package/src/api/clients/_shared/types.ts +25 -0
  3. package/src/api/clients/index.ts +8 -0
  4. package/src/api/clients/queues/__tests__/fieldsToSend.spec.ts +135 -0
  5. package/src/api/clients/queues/__tests__/queueLogs.spec.ts +132 -0
  6. package/src/api/clients/queues/__tests__/queueMembers.spec.ts +175 -0
  7. package/src/api/clients/queues/__tests__/queues.spec.ts +130 -0
  8. package/src/api/clients/queues/defaults/__tests__/queueTypeDefaults.spec.ts +143 -0
  9. package/src/api/clients/queues/defaults/amd.ts +17 -0
  10. package/src/api/clients/queues/defaults/defaultQueue.ts +9 -0
  11. package/src/api/clients/queues/defaults/processing.ts +8 -4
  12. package/src/api/clients/queues/defaults/queueTypeDefaults.ts +305 -0
  13. package/src/api/clients/queues/queueAgents.ts +48 -0
  14. package/src/api/clients/queues/queueBuckets.ts +187 -0
  15. package/src/api/clients/queues/queueHooks.ts +184 -0
  16. package/src/api/clients/queues/queueLogs.ts +80 -0
  17. package/src/api/clients/queues/queueMembers.ts +426 -0
  18. package/src/api/clients/queues/queueResGroups.ts +176 -0
  19. package/src/api/clients/queues/queueSkills.ts +183 -0
  20. package/src/api/clients/queues/queueState.ts +97 -0
  21. package/src/api/clients/queues/queues.ts +124 -94
  22. package/src/api/clients/queues/scripts/__tests__/mapResetMembersFilters.spec.ts +132 -0
  23. package/src/api/clients/queues/scripts/mapResetMembersFilters.ts +52 -0
  24. package/src/enums/CallReportingStatus/CallReportingStatus.ts +13 -0
  25. package/src/enums/MemberStopCause/MemberStopCause.ts +13 -0
  26. package/src/enums/QueueStrategy/QueueStrategy.ts +27 -0
  27. package/src/enums/QueueType/QueueType.ts +31 -0
  28. package/src/enums/TimeBaseScore/TimeBaseScore.ts +6 -0
  29. package/src/enums/TypesResourceStrategy/TypesResourceStrategy.ts +7 -0
  30. package/src/enums/index.ts +21 -1
  31. package/src/scripts/index.ts +19 -1
  32. package/src/scripts/normalizeDatetime/normalizeDatetime.ts +132 -0
  33. package/src/validations/_shared/isFilled.ts +15 -0
  34. package/src/validations/_shared/lookup.validations.ts +28 -0
  35. package/src/validations/_shared/variablePair.validations.ts +18 -0
  36. package/src/validations/index.ts +12 -0
  37. package/src/validations/queue/__tests__/queue.validations.spec.ts +159 -0
  38. package/src/validations/queue/queue.rules.ts +166 -0
  39. package/src/validations/queue/queue.validations.ts +105 -0
  40. package/src/validations/queue/queueAmd.validations.ts +22 -0
  41. package/src/validations/queue/queuePayload.validations.ts +71 -0
  42. package/src/validations/queue/taskProcessing.validations.ts +21 -0
  43. package/src/validations/queueBucket/queueBucket.validations.ts +10 -0
  44. package/src/validations/queueHook/queueHook.validations.ts +10 -0
  45. package/src/validations/queueMember/memberCommunication.validations.ts +25 -0
  46. package/src/validations/queueMember/queueMember.validations.ts +38 -0
  47. package/src/validations/queueResGroup/queueResGroup.validations.ts +15 -0
  48. package/src/validations/queueSkill/queueSkill.validations.ts +42 -0
  49. package/types/.tsbuildinfo +1 -1
  50. package/types/api/clients/_shared/types.d.ts +20 -0
  51. package/types/api/clients/index.d.ts +8 -0
  52. package/types/api/clients/queues/defaults/amd.d.ts +17 -0
  53. package/types/api/clients/queues/defaults/defaultQueue.d.ts +9 -0
  54. package/types/api/clients/queues/defaults/processing.d.ts +7 -2
  55. package/types/api/clients/queues/defaults/queueTypeDefaults.d.ts +31 -0
  56. package/types/api/clients/queues/queueAgents.d.ts +7 -0
  57. package/types/api/clients/queues/queueBuckets.d.ts +12 -0
  58. package/types/api/clients/queues/queueHooks.d.ts +12 -0
  59. package/types/api/clients/queues/queueLogs.d.ts +7 -0
  60. package/types/api/clients/queues/queueMembers.d.ts +30 -0
  61. package/types/api/clients/queues/queueResGroups.d.ts +15 -0
  62. package/types/api/clients/queues/queueSkills.d.ts +12 -0
  63. package/types/api/clients/queues/queueState.d.ts +8 -0
  64. package/types/api/clients/queues/queues.d.ts +9 -1
  65. package/types/api/clients/queues/scripts/mapResetMembersFilters.d.ts +30 -0
  66. package/types/enums/CallReportingStatus/CallReportingStatus.d.ts +11 -0
  67. package/types/enums/MemberStopCause/MemberStopCause.d.ts +11 -0
  68. package/types/enums/QueueStrategy/QueueStrategy.d.ts +21 -0
  69. package/types/enums/QueueType/QueueType.d.ts +28 -0
  70. package/types/enums/TimeBaseScore/TimeBaseScore.d.ts +5 -0
  71. package/types/enums/TypesResourceStrategy/TypesResourceStrategy.d.ts +5 -0
  72. package/types/enums/index.d.ts +7 -1
  73. package/types/scripts/index.d.ts +3 -1
  74. package/types/scripts/normalizeDatetime/normalizeDatetime.d.ts +14 -0
  75. package/types/validations/_shared/isFilled.d.ts +9 -0
  76. package/types/validations/_shared/lookup.validations.d.ts +18 -0
  77. package/types/validations/_shared/variablePair.validations.d.ts +16 -0
  78. package/types/validations/index.d.ts +12 -0
  79. package/types/validations/queue/queue.rules.d.ts +21 -0
  80. package/types/validations/queue/queue.validations.d.ts +285 -0
  81. package/types/validations/queue/queueAmd.validations.d.ts +22 -0
  82. package/types/validations/queue/queuePayload.validations.d.ts +75 -0
  83. package/types/validations/queue/taskProcessing.validations.d.ts +22 -0
  84. package/types/validations/queueBucket/queueBucket.validations.d.ts +9 -0
  85. package/types/validations/queueHook/queueHook.validations.d.ts +10 -0
  86. package/types/validations/queueMember/memberCommunication.validations.d.ts +16 -0
  87. package/types/validations/queueMember/queueMember.validations.d.ts +48 -0
  88. package/types/validations/queueResGroup/queueResGroup.validations.d.ts +11 -0
  89. package/types/validations/queueSkill/queueSkill.validations.d.ts +34 -0
@@ -0,0 +1,159 @@
1
+ import { get, set } from 'lodash-es';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { z } from 'zod';
4
+
5
+ import {
6
+ getQueueDefaults,
7
+ QueueTypeDefaults,
8
+ } from '../../../api/clients/queues/defaults/queueTypeDefaults';
9
+ import { QueueType } from '../../../enums';
10
+ import { queueTypeRules, sharedQueueRules } from '../queue.rules';
11
+ import { queueSchema, queueSchemaBase } from '../queue.validations';
12
+
13
+ type AnyRecord = Record<string, unknown>;
14
+
15
+ const allQueueTypes = Object.values(QueueType);
16
+
17
+ /** Lookup fields are `{ id, name }`; everything else required here is scalar. */
18
+ const lookupPaths = new Set([
19
+ 'calendar',
20
+ 'schema',
21
+ 'team',
22
+ 'dncList',
23
+ 'grantee',
24
+ 'ringtone',
25
+ 'doSchema',
26
+ 'afterSchema',
27
+ 'formSchema',
28
+ ]);
29
+
30
+ const filledValueFor = (path: string, current: unknown) => {
31
+ if (lookupPaths.has(path))
32
+ return {
33
+ id: '1',
34
+ name: 'x',
35
+ };
36
+ if (typeof current === 'number') return Math.max(current, 1);
37
+ if (typeof current === 'string' && current) return current;
38
+ return 'x';
39
+ };
40
+
41
+ const requiredPathsFor = (type: number) => [
42
+ ...(sharedQueueRules.required ?? []),
43
+ ...(queueTypeRules[type]?.required ?? []),
44
+ ];
45
+
46
+ /** A queue of `type` with every required field filled in. */
47
+ const validQueueFor = (type: number): AnyRecord => {
48
+ const queue = getQueueDefaults(type) as AnyRecord;
49
+ queue.name = 'Support';
50
+ for (const path of requiredPathsFor(type)) {
51
+ set(queue, path, filledValueFor(path, get(queue, path)));
52
+ }
53
+ return queue;
54
+ };
55
+
56
+ const issuePaths = (result: z.ZodSafeParseResult<unknown>) =>
57
+ result.success
58
+ ? []
59
+ : result.error.issues.map((issue) => issue.path.join('.'));
60
+
61
+ describe('queueSchema', () => {
62
+ /**
63
+ * `createCardStore` takes the schema straight, and
64
+ * `getShallowFieldsToSendFromZodSchema` calls `.keyof()` on it. Since zod 4
65
+ * keeps refinements inside the schema, `.superRefine` must leave a
66
+ * `ZodObject` behind — if that ever regresses, both break at once.
67
+ */
68
+ it('stays a ZodObject after superRefine', () => {
69
+ expect(queueSchema).toBeInstanceOf(z.ZodObject);
70
+ expect(queueSchema.keyof().options).toEqual(
71
+ queueSchemaBase.keyof().options,
72
+ );
73
+ expect(queueSchema.keyof().options).toContain('payload');
74
+ });
75
+
76
+ it('has a rule entry for every queue type', () => {
77
+ for (const type of allQueueTypes) {
78
+ expect(queueTypeRules[type], `missing rules for ${type}`).toBeDefined();
79
+ }
80
+ });
81
+
82
+ it.each(allQueueTypes)('accepts a fully filled queue of type %i', (type) => {
83
+ const result = queueSchema.safeParse(validQueueFor(type));
84
+
85
+ expect(issuePaths(result)).toEqual([]);
86
+ });
87
+
88
+ /**
89
+ * The behaviour that actually matters: each type's own required list is
90
+ * enforced, and only for that type.
91
+ */
92
+ it.each(allQueueTypes)('enforces every required field of type %i', (type) => {
93
+ for (const path of requiredPathsFor(type)) {
94
+ const queue = validQueueFor(type);
95
+ set(queue, path, lookupPaths.has(path) ? {} : '');
96
+
97
+ expect(
98
+ issuePaths(queueSchema.safeParse(queue)),
99
+ `${path} should be required for type ${type}`,
100
+ ).toContain(path);
101
+ }
102
+ });
103
+
104
+ it.each(allQueueTypes)('enforces the minimums of type %i', (type) => {
105
+ const minimums = {
106
+ ...sharedQueueRules.minValue,
107
+ ...queueTypeRules[type]?.minValue,
108
+ };
109
+
110
+ for (const [path, min] of Object.entries(minimums)) {
111
+ const queue = validQueueFor(type);
112
+ set(queue, path, min - 1);
113
+
114
+ expect(
115
+ issuePaths(queueSchema.safeParse(queue)),
116
+ `${path} should be >= ${min} for type ${type}`,
117
+ ).toContain(path);
118
+ }
119
+ });
120
+
121
+ /** Vuelidate's `required` treats 0 and false as filled; ours must too. */
122
+ it('treats a zero priority as filled rather than missing', () => {
123
+ const queue = validQueueFor(QueueType.INBOUND_QUEUE);
124
+ queue.priority = 0;
125
+
126
+ expect(issuePaths(queueSchema.safeParse(queue))).not.toContain('priority');
127
+ });
128
+
129
+ it('does not require team, which the legacy rules never enforced', () => {
130
+ const queue = validQueueFor(QueueType.OFFLINE_QUEUE);
131
+ queue.team = {};
132
+
133
+ expect(issuePaths(queueSchema.safeParse(queue))).not.toContain('team');
134
+ });
135
+
136
+ /**
137
+ * Regle builds `$fields` from state keys, so anything the defaults seed must
138
+ * survive the schema — a key the schema strips has no validation entry, and
139
+ * the field silently loses its required marker and error text.
140
+ */
141
+ it.each(allQueueTypes)('keeps every seeded key of type %i', (type) => {
142
+ const defaults = QueueTypeDefaults[type]() as AnyRecord;
143
+ const parsed = queueSchemaBase.parse(validQueueFor(type)) as AnyRecord;
144
+
145
+ for (const key of Object.keys(defaults)) {
146
+ expect(
147
+ parsed,
148
+ `top-level ${key} stripped for type ${type}`,
149
+ ).toHaveProperty(key);
150
+ }
151
+
152
+ for (const key of Object.keys(defaults.payload as AnyRecord)) {
153
+ expect(
154
+ parsed.payload,
155
+ `payload.${key} stripped for type ${type}`,
156
+ ).toHaveProperty(key);
157
+ }
158
+ });
159
+ });
@@ -0,0 +1,166 @@
1
+ import { QueueType } from '../../enums';
2
+
3
+ export interface QueueTypeRule {
4
+ /** dot-paths, relative to the queue object */
5
+ required?: string[];
6
+ /** dot-path → inclusive minimum */
7
+ minValue?: Record<string, number>;
8
+ }
9
+
10
+ /**
11
+ * @description
12
+ * Per-queue-type validation, transcribed from the admin app's
13
+ * `opened-queue.vue` `validations()` switch.
14
+ *
15
+ * Two things in here look like mistakes and are not:
16
+ *
17
+ * - `team` is commented "required" in every legacy queue schema but was never
18
+ * actually validated. That is preserved — adding it would start rejecting
19
+ * queues that save fine today.
20
+ * - `priority` and `payload.minOnlineAgents` carry `minValue(0)` for *all* ten
21
+ * types, including the ones whose form never shows `minOnlineAgents`.
22
+ */
23
+ export const sharedQueueRules: QueueTypeRule = {
24
+ required: [
25
+ 'name',
26
+ ],
27
+ minValue: {
28
+ priority: 0,
29
+ 'payload.minOnlineAgents': 0,
30
+ },
31
+ };
32
+
33
+ export const queueTypeRules: Record<number, QueueTypeRule> = {
34
+ [QueueType.OFFLINE_QUEUE]: {
35
+ required: [
36
+ 'strategy',
37
+ 'calendar',
38
+ 'payload.originateTimeout',
39
+ ],
40
+ minValue: {
41
+ 'payload.originateTimeout': 0,
42
+ },
43
+ },
44
+ [QueueType.INBOUND_QUEUE]: {
45
+ required: [
46
+ 'payload.timeBaseScore',
47
+ 'payload.maxWaitTime',
48
+ ],
49
+ minValue: {
50
+ 'payload.maxWaitTime': 0,
51
+ 'payload.discardAbandonedAfter': 0,
52
+ },
53
+ },
54
+ [QueueType.OUTBOUND_IVR_QUEUE]: {
55
+ required: [
56
+ 'strategy',
57
+ 'calendar',
58
+ 'schema',
59
+ 'payload.maxAttempts',
60
+ 'payload.originateTimeout',
61
+ 'payload.waitBetweenRetries',
62
+ 'payload.resourceStrategy',
63
+ ],
64
+ minValue: {
65
+ 'payload.originateTimeout': 0,
66
+ 'payload.waitBetweenRetries': 0,
67
+ 'payload.minDuration': 0,
68
+ },
69
+ },
70
+ [QueueType.PREVIEW_DIALER]: {
71
+ required: [
72
+ 'strategy',
73
+ 'calendar',
74
+ 'payload.maxAttempts',
75
+ 'payload.originateTimeout',
76
+ 'payload.waitBetweenRetries',
77
+ 'payload.resourceStrategy',
78
+ ],
79
+ minValue: {
80
+ 'payload.originateTimeout': 0,
81
+ 'payload.waitBetweenRetries': 0,
82
+ },
83
+ },
84
+ [QueueType.PROGRESSIVE_DIALER]: {
85
+ required: [
86
+ 'strategy',
87
+ 'calendar',
88
+ 'payload.maxAttempts',
89
+ 'payload.originateTimeout',
90
+ 'payload.waitBetweenRetries',
91
+ 'payload.resourceStrategy',
92
+ 'payload.progressiveCount',
93
+ ],
94
+ minValue: {
95
+ 'payload.originateTimeout': 0,
96
+ 'payload.waitBetweenRetries': 0,
97
+ 'payload.progressiveCount': 1,
98
+ },
99
+ },
100
+ [QueueType.PREDICTIVE_DIALER]: {
101
+ required: [
102
+ 'strategy',
103
+ 'calendar',
104
+ 'payload.maxAttempts',
105
+ 'payload.originateTimeout',
106
+ 'payload.waitBetweenRetries',
107
+ 'payload.resourceStrategy',
108
+ 'payload.progressiveCount',
109
+ ],
110
+ minValue: {
111
+ 'payload.originateTimeout': 0,
112
+ 'payload.waitBetweenRetries': 0,
113
+ 'payload.maxWaitTime': 0,
114
+ 'payload.progressiveCount': 1,
115
+ },
116
+ },
117
+ [QueueType.CHAT_INBOUND_QUEUE]: {
118
+ required: [
119
+ 'strategy',
120
+ 'payload.timeBaseScore',
121
+ 'payload.maxWaitTime',
122
+ ],
123
+ minValue: {
124
+ 'payload.maxWaitTime': 0,
125
+ 'payload.discardAbandonedAfter': 0,
126
+ },
127
+ },
128
+ /** shares a `case` label with CHAT_INBOUND_QUEUE in the legacy switch */
129
+ [QueueType.IM_CHAT_QUEUE]: {
130
+ required: [
131
+ 'strategy',
132
+ 'payload.timeBaseScore',
133
+ 'payload.maxWaitTime',
134
+ ],
135
+ minValue: {
136
+ 'payload.maxWaitTime': 0,
137
+ 'payload.discardAbandonedAfter': 0,
138
+ },
139
+ },
140
+ [QueueType.INBOUND_JOB_QUEUE]: {
141
+ required: [
142
+ 'strategy',
143
+ 'calendar',
144
+ 'payload.maxAttempts',
145
+ 'payload.waitBetweenRetries',
146
+ ],
147
+ minValue: {
148
+ 'payload.waitBetweenRetries': 0,
149
+ },
150
+ },
151
+ [QueueType.OUTBOUND_JOB_QUEUE]: {
152
+ required: [
153
+ 'strategy',
154
+ 'calendar',
155
+ 'schema',
156
+ 'payload.maxAttempts',
157
+ 'payload.originateTimeout',
158
+ 'payload.waitBetweenRetries',
159
+ ],
160
+ minValue: {
161
+ 'payload.originateTimeout': 0,
162
+ 'payload.waitBetweenRetries': 0,
163
+ 'payload.minDuration': 0,
164
+ },
165
+ },
166
+ };
@@ -0,0 +1,105 @@
1
+ import { get } from 'lodash-es';
2
+ import { z } from 'zod';
3
+
4
+ import { i18nIssue } from '../_shared/i18nIssue';
5
+ import { isFilled } from '../_shared/isFilled';
6
+ import { flexibleLookupSchema } from '../_shared/lookup.validations';
7
+ import { variablePairSchema } from '../_shared/variablePair.validations';
8
+ import { queueTypeRules, sharedQueueRules } from './queue.rules';
9
+ import { queuePayloadSchema } from './queuePayload.validations';
10
+ import { taskProcessingSchema } from './taskProcessing.validations';
11
+
12
+ /**
13
+ * @description
14
+ * One flat schema covering all ten queue types, rather than a discriminated
15
+ * union on `type`.
16
+ *
17
+ * The union would be more precise, but Regle would then infer
18
+ * `validationFields` as a union too, and every form tab would need narrowing
19
+ * before it could read a field. Flat keeps `validationFields.<field>` reachable
20
+ * from every tab regardless of type, and the per-type rules move into the
21
+ * `superRefine` below.
22
+ *
23
+ * `.superRefine` is safe to build on here: since zod 4 it lives inside the
24
+ * schema and returns a `ZodObject`, so `schema.keyof()` still works and
25
+ * `createCardStore` still accepts it.
26
+ */
27
+ export const queueSchemaBase = z.object({
28
+ // identity
29
+ name: z.string().min(1),
30
+ description: z.string().optional(),
31
+ type: z.number(),
32
+ enabled: z.boolean().optional(),
33
+ priority: z.number().optional(),
34
+ tags: z
35
+ .array(
36
+ z.object({
37
+ name: z.string(),
38
+ }),
39
+ )
40
+ .optional(),
41
+
42
+ // lookups
43
+ calendar: flexibleLookupSchema.optional(),
44
+ team: flexibleLookupSchema.optional(),
45
+ dncList: flexibleLookupSchema.optional(),
46
+ grantee: flexibleLookupSchema.optional(),
47
+ ringtone: flexibleLookupSchema.optional(),
48
+ schema: flexibleLookupSchema.optional(),
49
+ doSchema: flexibleLookupSchema.optional(),
50
+ afterSchema: flexibleLookupSchema.optional(),
51
+ formSchema: flexibleLookupSchema.optional(),
52
+
53
+ // behaviour
54
+ strategy: z.string().optional(),
55
+ stickyAgent: z.boolean().optional(),
56
+
57
+ taskProcessing: taskProcessingSchema.optional(),
58
+ payload: queuePayloadSchema.optional(),
59
+
60
+ variables: z.array(variablePairSchema).optional(),
61
+ });
62
+
63
+ /**
64
+ * Applies the rules the legacy `validations()` switch used to build per type.
65
+ * Paths are dotted, so an issue raised for `payload.maxWaitTime` lands on
66
+ * `validationFields.payload.$fields.maxWaitTime`.
67
+ */
68
+ export const queueSchema = queueSchemaBase.superRefine((queue, ctx) => {
69
+ const rules = [
70
+ sharedQueueRules,
71
+ queueTypeRules[Number(queue.type)] ?? {},
72
+ ];
73
+
74
+ for (const rule of rules) {
75
+ for (const path of rule.required ?? []) {
76
+ if (!isFilled(get(queue, path))) {
77
+ ctx.addIssue({
78
+ code: 'custom',
79
+ path: path.split('.'),
80
+ ...i18nIssue('required'),
81
+ });
82
+ }
83
+ }
84
+
85
+ for (const [path, min] of Object.entries(rule.minValue ?? {})) {
86
+ const value = get(queue, path);
87
+ // an absent optional field is the `required` rules' business, not this one
88
+ if (value === undefined || value === null || value === '') continue;
89
+ if (Number(value) < min) {
90
+ ctx.addIssue({
91
+ code: 'custom',
92
+ path: path.split('.'),
93
+ message: `Value must be ${min} or greater`,
94
+ });
95
+ }
96
+ }
97
+ }
98
+ });
99
+
100
+ /**
101
+ * The queue as the *form* holds it. Not an entity type — it has no `id`, and
102
+ * `variables` is the editable pair list rather than the wire-format map. Apps
103
+ * pair this with `EngineQueue` for the parts the form does not own.
104
+ */
105
+ export type QueueFormShape = z.infer<typeof queueSchemaBase>;
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod';
2
+
3
+ import { flexibleLookupSchema } from '../_shared/lookup.validations';
4
+
5
+ /** Answering-machine detection, on the outbound IVR and the two auto-dialers. */
6
+ export const queueAmdSchema = z.object({
7
+ enabled: z.boolean().optional(),
8
+ ai: z.boolean().optional(),
9
+ positive: z.array(z.string()).optional(),
10
+ playback: flexibleLookupSchema.optional(),
11
+ allowNotSure: z.boolean().optional(),
12
+ 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(),
22
+ });
@@ -0,0 +1,71 @@
1
+ import { z } from 'zod';
2
+
3
+ import { queueAmdSchema } from './queueAmd.validations';
4
+
5
+ /**
6
+ * @description
7
+ * The union of every queue type's `payload`. Which of these a given queue
8
+ * actually uses is decided by its type, both for display (the admin app's
9
+ * `QueueTypeProperties` lookup) and for validation (`queueTypeRules`).
10
+ *
11
+ * Deliberately no `.default()` on any leaf. Defaults come from
12
+ * `getQueueDefaults(type)`, which holds exactly the per-type key set; a default
13
+ * here would seed every queue with every field, and since
14
+ * `sanitize(fieldsToSend)` only filters top-level keys, `payload` is sent
15
+ * whole — an inbound queue would ship `progressiveCount` to the backend.
16
+ */
17
+ export const queuePayloadSchema = z.object({
18
+ // dialing and retries
19
+ maxAttempts: z.number().optional(),
20
+ minAttempts: z.number().optional(),
21
+ originateTimeout: z.number().optional(),
22
+ waitBetweenRetries: z.number().optional(),
23
+ waitBetweenRetriesDesc: z.boolean().optional(),
24
+ minDuration: z.number().optional(),
25
+ maxCalls: z.number().optional(),
26
+ progressiveCount: z.number().optional(),
27
+ dialingRate: z.number().optional(),
28
+ resourceStrategy: z.string().optional(),
29
+ strictCircuit: z.boolean().optional(),
30
+ perNumbers: z.boolean().optional(),
31
+ endless: z.boolean().optional(),
32
+ retryAbandoned: z.boolean().optional(),
33
+
34
+ // 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(),
43
+
44
+ // waiting and distribution
45
+ timeBaseScore: z.string().optional(),
46
+ maxWaitTime: z.number().optional(),
47
+ maxWaitingSize: z.number().optional(),
48
+ discardAbandonedAfter: z.number().optional(),
49
+ manualDistribution: z.boolean().optional(),
50
+ minOnlineAgents: z.number().optional(),
51
+ maxMemberLimit: z.number().optional(),
52
+ ignoreCalendar: z.boolean().optional(),
53
+
54
+ // sticky agent
55
+ stickyAgentSec: z.number().optional(),
56
+ stickyIgnoreStatus: z.boolean().optional(),
57
+
58
+ // chat queues only
59
+ maxIdleAgent: z.number().optional(),
60
+ maxIdleClient: z.number().optional(),
61
+ maxIdleDialog: z.number().optional(),
62
+ lastMessageTimeout: z.boolean().optional(),
63
+
64
+ // media
65
+ recordings: z.boolean().optional(),
66
+ recordAll: z.boolean().optional(),
67
+ allowGreetingAgent: z.boolean().optional(),
68
+ autoAnswerTone: z.string().nullable().optional(),
69
+
70
+ amd: queueAmdSchema.optional(),
71
+ });
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+
3
+ import { flexibleLookupSchema } from '../_shared/lookup.validations';
4
+
5
+ export const prolongationOptionsSchema = z.object({
6
+ enabled: z.boolean().optional(),
7
+ isTimeoutRetry: z.boolean().optional(),
8
+ prolongationTimeSec: z.number().optional(),
9
+ repeatsNumber: z.number().optional(),
10
+ });
11
+
12
+ export const taskProcessingSchema = z.object({
13
+ enabled: z.boolean().optional(),
14
+ formSchema: flexibleLookupSchema.optional(),
15
+ sec: z.number().optional(),
16
+ renewalSec: z.number().optional(),
17
+ // `.prefault`, not `.default`: zod 4's `.default({})` does not recurse, so
18
+ // the nested keys would be missing and Regle would build no `$fields` for
19
+ // them — the prolongation block would lose its validation silently.
20
+ prolongationOptions: prolongationOptionsSchema.prefault({}),
21
+ });
@@ -0,0 +1,10 @@
1
+ import { z } from 'zod';
2
+
3
+ import { filledLookupSchema } from '../_shared/lookup.validations';
4
+
5
+ export const queueBucketSchema = z.object({
6
+ bucket: filledLookupSchema,
7
+ /** legacy: required + numeric + minValue(0) */
8
+ priority: z.number().min(0),
9
+ disabled: z.boolean().optional(),
10
+ });
@@ -0,0 +1,10 @@
1
+ import { z } from 'zod';
2
+
3
+ import { filledLookupSchema } from '../_shared/lookup.validations';
4
+
5
+ export const queueHookSchema = z.object({
6
+ event: z.string().min(1),
7
+ schema: filledLookupSchema,
8
+ properties: z.array(z.string()).optional(),
9
+ enabled: z.boolean().optional(),
10
+ });
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+
3
+ import {
4
+ filledLookupSchema,
5
+ flexibleLookupSchema,
6
+ } from '../_shared/lookup.validations';
7
+
8
+ /**
9
+ * Carried over verbatim from the admin app's `digitsDtmfOnly` validator.
10
+ *
11
+ * NB the character class allows a literal `|` as well as digits and `w`/`W` —
12
+ * `[\d|w|W]` was almost certainly meant to be `[\dwW]`. Preserved as-is:
13
+ * tightening it would start rejecting values that save today.
14
+ */
15
+ const dtmfPattern = /^[\d|w|W]*$/;
16
+
17
+ export const memberCommunicationSchema = z.object({
18
+ destination: z.string().min(1),
19
+ type: filledLookupSchema,
20
+ dtmf: z.string().regex(dtmfPattern).optional(),
21
+ display: z.string().optional(),
22
+ priority: z.number().optional(),
23
+ resource: flexibleLookupSchema.optional(),
24
+ description: z.string().optional(),
25
+ });
@@ -0,0 +1,38 @@
1
+ import { z } from 'zod';
2
+
3
+ import { flexibleLookupSchema } from '../_shared/lookup.validations';
4
+ import { variablePairSchema } from '../_shared/variablePair.validations';
5
+ import { memberCommunicationSchema } from './memberCommunication.validations';
6
+
7
+ /**
8
+ * @description
9
+ * A queue member.
10
+ *
11
+ * `communications` and `variables` must stay in the schema even though neither
12
+ * is validated beyond "at least one communication": the card store saves the
13
+ * schema's parsed output, so a key that is absent here is a key that never
14
+ * reaches the backend.
15
+ */
16
+ export const queueMemberSchema = z.object({
17
+ name: z.string().min(1),
18
+ /** legacy `requiredArrayValue` — a member with no way to reach it is useless */
19
+ communications: z.array(memberCommunicationSchema).min(1),
20
+ priority: z.number().optional(),
21
+ bucket: flexibleLookupSchema.optional(),
22
+ timezone: flexibleLookupSchema.optional(),
23
+ agent: flexibleLookupSchema.optional(),
24
+ expireAt: z
25
+ .union([
26
+ z.string(),
27
+ z.number(),
28
+ ])
29
+ .optional(),
30
+ minOfferingAt: z
31
+ .union([
32
+ z.string(),
33
+ z.number(),
34
+ ])
35
+ .optional(),
36
+ stopCause: z.string().optional(),
37
+ variables: z.array(variablePairSchema).optional(),
38
+ });
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod';
2
+
3
+ import {
4
+ filledLookupSchema,
5
+ flexibleLookupSchema,
6
+ } from '../_shared/lookup.validations';
7
+
8
+ export const queueResGroupSchema = z.object({
9
+ resourceGroup: filledLookupSchema,
10
+ /**
11
+ * Sent, but never edited — the popup has no control for it. Kept so it
12
+ * survives a round-trip rather than being stripped on save.
13
+ */
14
+ communication: flexibleLookupSchema.optional(),
15
+ });
@@ -0,0 +1,42 @@
1
+ import { z } from 'zod';
2
+
3
+ import {
4
+ filledLookupSchema,
5
+ flexibleLookupSchema,
6
+ } from '../_shared/lookup.validations';
7
+
8
+ export const queueSkillSchemaBase = z.object({
9
+ skill: filledLookupSchema,
10
+ lvl: z.number().min(0).max(1000),
11
+ minCapacity: z.number().min(0).max(100).optional(),
12
+ maxCapacity: z.number().min(0).max(100).optional(),
13
+ buckets: z.array(flexibleLookupSchema).optional(),
14
+ enabled: z.boolean().optional(),
15
+ });
16
+
17
+ /**
18
+ * `minCapacity` and `maxCapacity` bound each other, which Vuelidate expressed
19
+ * as a pair of cross-field validators. Both sides are reported, so whichever
20
+ * field the user is editing shows the error.
21
+ */
22
+ export const queueSkillSchema = queueSkillSchemaBase.superRefine(
23
+ (skill, ctx) => {
24
+ if (skill.minCapacity == null || skill.maxCapacity == null) return;
25
+ if (skill.minCapacity <= skill.maxCapacity) return;
26
+
27
+ ctx.addIssue({
28
+ code: 'custom',
29
+ path: [
30
+ 'minCapacity',
31
+ ],
32
+ message: 'Value must be less than or equal to the maximum capacity',
33
+ });
34
+ ctx.addIssue({
35
+ code: 'custom',
36
+ path: [
37
+ 'maxCapacity',
38
+ ],
39
+ message: 'Value must be greater than or equal to the minimum capacity',
40
+ });
41
+ },
42
+ );