@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.
- package/package.json +3 -1
- package/src/api/clients/_shared/types.ts +25 -0
- package/src/api/clients/index.ts +8 -0
- package/src/api/clients/queues/__tests__/fieldsToSend.spec.ts +135 -0
- package/src/api/clients/queues/__tests__/queueLogs.spec.ts +132 -0
- package/src/api/clients/queues/__tests__/queueMembers.spec.ts +175 -0
- package/src/api/clients/queues/__tests__/queues.spec.ts +130 -0
- package/src/api/clients/queues/defaults/__tests__/queueTypeDefaults.spec.ts +143 -0
- package/src/api/clients/queues/defaults/amd.ts +17 -0
- package/src/api/clients/queues/defaults/defaultQueue.ts +9 -0
- package/src/api/clients/queues/defaults/processing.ts +8 -4
- package/src/api/clients/queues/defaults/queueTypeDefaults.ts +305 -0
- package/src/api/clients/queues/queueAgents.ts +48 -0
- package/src/api/clients/queues/queueBuckets.ts +187 -0
- package/src/api/clients/queues/queueHooks.ts +184 -0
- package/src/api/clients/queues/queueLogs.ts +80 -0
- package/src/api/clients/queues/queueMembers.ts +426 -0
- package/src/api/clients/queues/queueResGroups.ts +176 -0
- package/src/api/clients/queues/queueSkills.ts +183 -0
- package/src/api/clients/queues/queueState.ts +97 -0
- package/src/api/clients/queues/queues.ts +124 -94
- package/src/api/clients/queues/scripts/__tests__/mapResetMembersFilters.spec.ts +132 -0
- package/src/api/clients/queues/scripts/mapResetMembersFilters.ts +52 -0
- package/src/enums/CallReportingStatus/CallReportingStatus.ts +13 -0
- package/src/enums/MemberStopCause/MemberStopCause.ts +13 -0
- package/src/enums/QueueStrategy/QueueStrategy.ts +27 -0
- package/src/enums/QueueType/QueueType.ts +31 -0
- package/src/enums/TimeBaseScore/TimeBaseScore.ts +6 -0
- package/src/enums/TypesResourceStrategy/TypesResourceStrategy.ts +7 -0
- package/src/enums/index.ts +21 -1
- package/src/scripts/index.ts +19 -1
- package/src/scripts/normalizeDatetime/normalizeDatetime.ts +132 -0
- package/src/validations/_shared/isFilled.ts +15 -0
- package/src/validations/_shared/lookup.validations.ts +28 -0
- package/src/validations/_shared/variablePair.validations.ts +18 -0
- package/src/validations/index.ts +12 -0
- package/src/validations/queue/__tests__/queue.validations.spec.ts +159 -0
- package/src/validations/queue/queue.rules.ts +166 -0
- package/src/validations/queue/queue.validations.ts +105 -0
- package/src/validations/queue/queueAmd.validations.ts +22 -0
- package/src/validations/queue/queuePayload.validations.ts +71 -0
- package/src/validations/queue/taskProcessing.validations.ts +21 -0
- package/src/validations/queueBucket/queueBucket.validations.ts +10 -0
- package/src/validations/queueHook/queueHook.validations.ts +10 -0
- package/src/validations/queueMember/memberCommunication.validations.ts +25 -0
- package/src/validations/queueMember/queueMember.validations.ts +38 -0
- package/src/validations/queueResGroup/queueResGroup.validations.ts +15 -0
- package/src/validations/queueSkill/queueSkill.validations.ts +42 -0
- package/types/.tsbuildinfo +1 -1
- package/types/api/clients/_shared/types.d.ts +20 -0
- package/types/api/clients/index.d.ts +8 -0
- package/types/api/clients/queues/defaults/amd.d.ts +17 -0
- package/types/api/clients/queues/defaults/defaultQueue.d.ts +9 -0
- package/types/api/clients/queues/defaults/processing.d.ts +7 -2
- package/types/api/clients/queues/defaults/queueTypeDefaults.d.ts +31 -0
- package/types/api/clients/queues/queueAgents.d.ts +7 -0
- package/types/api/clients/queues/queueBuckets.d.ts +12 -0
- package/types/api/clients/queues/queueHooks.d.ts +12 -0
- package/types/api/clients/queues/queueLogs.d.ts +7 -0
- package/types/api/clients/queues/queueMembers.d.ts +30 -0
- package/types/api/clients/queues/queueResGroups.d.ts +15 -0
- package/types/api/clients/queues/queueSkills.d.ts +12 -0
- package/types/api/clients/queues/queueState.d.ts +8 -0
- package/types/api/clients/queues/queues.d.ts +9 -1
- package/types/api/clients/queues/scripts/mapResetMembersFilters.d.ts +30 -0
- package/types/enums/CallReportingStatus/CallReportingStatus.d.ts +11 -0
- package/types/enums/MemberStopCause/MemberStopCause.d.ts +11 -0
- package/types/enums/QueueStrategy/QueueStrategy.d.ts +21 -0
- package/types/enums/QueueType/QueueType.d.ts +28 -0
- package/types/enums/TimeBaseScore/TimeBaseScore.d.ts +5 -0
- package/types/enums/TypesResourceStrategy/TypesResourceStrategy.d.ts +5 -0
- package/types/enums/index.d.ts +7 -1
- package/types/scripts/index.d.ts +3 -1
- package/types/scripts/normalizeDatetime/normalizeDatetime.d.ts +14 -0
- package/types/validations/_shared/isFilled.d.ts +9 -0
- package/types/validations/_shared/lookup.validations.d.ts +18 -0
- package/types/validations/_shared/variablePair.validations.d.ts +16 -0
- package/types/validations/index.d.ts +12 -0
- package/types/validations/queue/queue.rules.d.ts +21 -0
- package/types/validations/queue/queue.validations.d.ts +285 -0
- package/types/validations/queue/queueAmd.validations.d.ts +22 -0
- package/types/validations/queue/queuePayload.validations.d.ts +75 -0
- package/types/validations/queue/taskProcessing.validations.d.ts +22 -0
- package/types/validations/queueBucket/queueBucket.validations.d.ts +9 -0
- package/types/validations/queueHook/queueHook.validations.d.ts +10 -0
- package/types/validations/queueMember/memberCommunication.validations.d.ts +16 -0
- package/types/validations/queueMember/queueMember.validations.d.ts +48 -0
- package/types/validations/queueResGroup/queueResGroup.validations.d.ts +11 -0
- package/types/validations/queueSkill/queueSkill.validations.d.ts +34 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
mapResetMembersFilters,
|
|
5
|
+
mapResetMembersQuantityFilters,
|
|
6
|
+
} from '../mapResetMembersFilters';
|
|
7
|
+
|
|
8
|
+
describe('mapResetMembersFilters', () => {
|
|
9
|
+
it('maps the datalist filter names onto the reset body', () => {
|
|
10
|
+
expect(
|
|
11
|
+
mapResetMembersFilters({
|
|
12
|
+
agent: [
|
|
13
|
+
1,
|
|
14
|
+
],
|
|
15
|
+
bucket: [
|
|
16
|
+
2,
|
|
17
|
+
],
|
|
18
|
+
createdAt: {
|
|
19
|
+
from: 100,
|
|
20
|
+
to: 200,
|
|
21
|
+
},
|
|
22
|
+
memberPriority: {
|
|
23
|
+
from: 1,
|
|
24
|
+
to: 9,
|
|
25
|
+
},
|
|
26
|
+
search: 'joe',
|
|
27
|
+
stopCause: [
|
|
28
|
+
'cancel',
|
|
29
|
+
],
|
|
30
|
+
}),
|
|
31
|
+
).toEqual({
|
|
32
|
+
agent_id: [
|
|
33
|
+
1,
|
|
34
|
+
],
|
|
35
|
+
bucket_id: [
|
|
36
|
+
2,
|
|
37
|
+
],
|
|
38
|
+
created_at: {
|
|
39
|
+
from: 100,
|
|
40
|
+
to: 200,
|
|
41
|
+
},
|
|
42
|
+
priority: {
|
|
43
|
+
from: 1,
|
|
44
|
+
to: 9,
|
|
45
|
+
},
|
|
46
|
+
q: 'joe',
|
|
47
|
+
stop_cause: [
|
|
48
|
+
'cancel',
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The members table shipped flat `from`/`to`/`cause`/`priority` names before
|
|
55
|
+
* the migration; both spellings are accepted so the two can coexist.
|
|
56
|
+
*/
|
|
57
|
+
it('still accepts the legacy flat filter names', () => {
|
|
58
|
+
const mapped = mapResetMembersFilters({
|
|
59
|
+
from: 100,
|
|
60
|
+
to: 200,
|
|
61
|
+
cause: [
|
|
62
|
+
'cancel',
|
|
63
|
+
],
|
|
64
|
+
priority: {
|
|
65
|
+
from: 1,
|
|
66
|
+
to: 9,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(mapped.created_at).toEqual({
|
|
71
|
+
from: 100,
|
|
72
|
+
to: 200,
|
|
73
|
+
});
|
|
74
|
+
expect(mapped.priority).toEqual({
|
|
75
|
+
from: 1,
|
|
76
|
+
to: 9,
|
|
77
|
+
});
|
|
78
|
+
expect(mapped.stop_cause).toEqual([
|
|
79
|
+
'cancel',
|
|
80
|
+
]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('leaves every bound undefined when nothing is filtered', () => {
|
|
84
|
+
expect(mapResetMembersFilters()).toEqual({
|
|
85
|
+
agent_id: undefined,
|
|
86
|
+
bucket_id: undefined,
|
|
87
|
+
created_at: {
|
|
88
|
+
from: undefined,
|
|
89
|
+
to: undefined,
|
|
90
|
+
},
|
|
91
|
+
priority: {
|
|
92
|
+
from: undefined,
|
|
93
|
+
to: undefined,
|
|
94
|
+
},
|
|
95
|
+
q: undefined,
|
|
96
|
+
stop_cause: undefined,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('mapResetMembersQuantityFilters', () => {
|
|
102
|
+
/**
|
|
103
|
+
* The count endpoint is a GET, so the nested range messages have to be
|
|
104
|
+
* spelled out as dotted query keys rather than nested objects.
|
|
105
|
+
*/
|
|
106
|
+
it('flattens the ranges into dotted query keys', () => {
|
|
107
|
+
const mapped = mapResetMembersQuantityFilters({
|
|
108
|
+
createdAt: {
|
|
109
|
+
from: 100,
|
|
110
|
+
to: 200,
|
|
111
|
+
},
|
|
112
|
+
memberPriority: {
|
|
113
|
+
from: 1,
|
|
114
|
+
to: 9,
|
|
115
|
+
},
|
|
116
|
+
search: 'joe',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(mapped).toEqual({
|
|
120
|
+
agent_id: undefined,
|
|
121
|
+
bucket_id: undefined,
|
|
122
|
+
'created_at.from': 100,
|
|
123
|
+
'created_at.to': 200,
|
|
124
|
+
'priority.from': 1,
|
|
125
|
+
'priority.to': 9,
|
|
126
|
+
q: 'joe',
|
|
127
|
+
stop_cause: undefined,
|
|
128
|
+
});
|
|
129
|
+
expect(mapped).not.toHaveProperty('created_at');
|
|
130
|
+
expect(mapped).not.toHaveProperty('priority');
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ApiParams } from '../../_shared/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The members table, the "reset members" action and the "how many will this
|
|
5
|
+
* affect?" counter all read the same filter set, but the three endpoints want
|
|
6
|
+
* it in three different shapes.
|
|
7
|
+
*
|
|
8
|
+
* Filter names accept both the datalist names (`createdAt`, `stopCause`,
|
|
9
|
+
* `memberPriority`) and the legacy flat ones (`from`/`to`, `cause`,
|
|
10
|
+
* `priority`). `createdAt` and `memberPriority` arrive already resolved to
|
|
11
|
+
* `{ from, to }` — relative values like "today" are resolved app-side, where
|
|
12
|
+
* the user's timezone lives.
|
|
13
|
+
*/
|
|
14
|
+
const range = (value: ApiParams | undefined, from?: number, to?: number) => ({
|
|
15
|
+
from: value?.from ?? from,
|
|
16
|
+
to: value?.to ?? to,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const mapResetMembersFilters = (filters: ApiParams = {}) => {
|
|
20
|
+
const createdAt = range(filters.createdAt, filters.from, filters.to);
|
|
21
|
+
const priority = range(
|
|
22
|
+
filters.memberPriority ?? filters.priority,
|
|
23
|
+
filters.priorityFrom,
|
|
24
|
+
filters.priorityTo,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
agent_id: filters.agent,
|
|
29
|
+
bucket_id: filters.bucket,
|
|
30
|
+
created_at: createdAt,
|
|
31
|
+
priority,
|
|
32
|
+
q: filters.search,
|
|
33
|
+
stop_cause: filters.stopCause ?? filters.cause,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The count endpoint is a GET, so the nested messages have to be spelled out
|
|
39
|
+
* as dotted query keys. `qs` emits these unencoded, which is what the backend
|
|
40
|
+
* expects — do NOT let `camelToSnake` near them.
|
|
41
|
+
*/
|
|
42
|
+
export const mapResetMembersQuantityFilters = (filters: ApiParams = {}) => {
|
|
43
|
+
const { created_at, priority, ...rest } = mapResetMembersFilters(filters);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
...rest,
|
|
47
|
+
'created_at.from': created_at.from,
|
|
48
|
+
'created_at.to': created_at.to,
|
|
49
|
+
'priority.from': priority.from,
|
|
50
|
+
'priority.to': priority.to,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const CallReportingStatus = {
|
|
2
|
+
ABANDONED: 'abandoned',
|
|
3
|
+
CANCEL: 'cancel',
|
|
4
|
+
SUCCESS: 'success',
|
|
5
|
+
FAILED: 'failed',
|
|
6
|
+
MISSED: 'missed',
|
|
7
|
+
TIMEOUT: 'timeout',
|
|
8
|
+
ENDLESS: 'endless',
|
|
9
|
+
TRANSFERRED: 'transferred',
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export type CallReportingStatus =
|
|
13
|
+
(typeof CallReportingStatus)[keyof typeof CallReportingStatus];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const MemberStopCause = {
|
|
2
|
+
MISSED: 'missed',
|
|
3
|
+
TIMEOUT: 'timeout',
|
|
4
|
+
FAILED: 'failed',
|
|
5
|
+
ABANDONED: 'abandoned',
|
|
6
|
+
SUCCESS: 'success',
|
|
7
|
+
CANCEL: 'cancel',
|
|
8
|
+
EXPIRED: 'expired',
|
|
9
|
+
CANCELED_BY_TIMEOUT: 'canceled_by_timeout',
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export type MemberStopCause =
|
|
13
|
+
(typeof MemberStopCause)[keyof typeof MemberStopCause];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const QueueStrategy = {
|
|
2
|
+
FIFO: 'fifo',
|
|
3
|
+
LIFO: 'lifo',
|
|
4
|
+
STRICT_FIFO: 'strict_fifo',
|
|
5
|
+
} as const;
|
|
6
|
+
|
|
7
|
+
export type QueueStrategy = (typeof QueueStrategy)[keyof typeof QueueStrategy];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @description
|
|
11
|
+
* Select options for the queue `strategy` field, in display order.
|
|
12
|
+
* `name` is the raw label the admin UI shows — not a locale key.
|
|
13
|
+
*/
|
|
14
|
+
export const QueueStrategyList = [
|
|
15
|
+
{
|
|
16
|
+
name: 'FIFO',
|
|
17
|
+
value: QueueStrategy.FIFO,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'LIFO',
|
|
21
|
+
value: QueueStrategy.LIFO,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'STRICT_FIFO',
|
|
25
|
+
value: QueueStrategy.STRICT_FIFO,
|
|
26
|
+
},
|
|
27
|
+
] as const;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const QueueType = {
|
|
2
|
+
OFFLINE_QUEUE: 0,
|
|
3
|
+
INBOUND_QUEUE: 1,
|
|
4
|
+
OUTBOUND_IVR_QUEUE: 2,
|
|
5
|
+
PREVIEW_DIALER: 3,
|
|
6
|
+
PROGRESSIVE_DIALER: 4,
|
|
7
|
+
PREDICTIVE_DIALER: 5,
|
|
8
|
+
CHAT_INBOUND_QUEUE: 6,
|
|
9
|
+
INBOUND_JOB_QUEUE: 7,
|
|
10
|
+
OUTBOUND_JOB_QUEUE: 8,
|
|
11
|
+
IM_CHAT_QUEUE: 9,
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
export type QueueType = (typeof QueueType)[keyof typeof QueueType];
|
|
15
|
+
|
|
16
|
+
export const QueueTypeName = {
|
|
17
|
+
OFFLINE_QUEUE: 'offline',
|
|
18
|
+
INBOUND_QUEUE: 'inbound',
|
|
19
|
+
OUTBOUND_IVR_QUEUE: 'ivr',
|
|
20
|
+
PREVIEW_DIALER: 'preview',
|
|
21
|
+
PROGRESSIVE_DIALER: 'progressive',
|
|
22
|
+
PREDICTIVE_DIALER: 'predictive',
|
|
23
|
+
CHAT_INBOUND_QUEUE: 'inbound chat',
|
|
24
|
+
INBOUND_JOB_QUEUE: 'task',
|
|
25
|
+
OUTBOUND_JOB_QUEUE: 'outbound_task',
|
|
26
|
+
OUTBOUND_CALL: 'outbound_call',
|
|
27
|
+
IM_CHAT_QUEUE: 'im_chat',
|
|
28
|
+
NOT_IMPLEMENT: 'NOT_IMPLEMENT',
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
export type QueueTypeName = (typeof QueueTypeName)[keyof typeof QueueTypeName];
|
package/src/enums/index.ts
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
|
+
import { CallReportingStatus } from './CallReportingStatus/CallReportingStatus';
|
|
1
2
|
import { ChatGatewayProvider } from './ChatGatewayProvider/ChatGatewayPrivider';
|
|
3
|
+
import { MemberStopCause } from './MemberStopCause/MemberStopCause';
|
|
4
|
+
import {
|
|
5
|
+
QueueStrategy,
|
|
6
|
+
QueueStrategyList,
|
|
7
|
+
} from './QueueStrategy/QueueStrategy';
|
|
8
|
+
import { QueueType, QueueTypeName } from './QueueType/QueueType';
|
|
2
9
|
import { RelativeDatetimeValue } from './RelativeDatetimeValue/RelativeDatetimeValue';
|
|
10
|
+
import { TimeBaseScore } from './TimeBaseScore/TimeBaseScore';
|
|
11
|
+
import { TypesResourceStrategy } from './TypesResourceStrategy/TypesResourceStrategy';
|
|
3
12
|
|
|
4
|
-
export {
|
|
13
|
+
export {
|
|
14
|
+
CallReportingStatus,
|
|
15
|
+
ChatGatewayProvider,
|
|
16
|
+
MemberStopCause,
|
|
17
|
+
QueueStrategy,
|
|
18
|
+
QueueStrategyList,
|
|
19
|
+
QueueType,
|
|
20
|
+
QueueTypeName,
|
|
21
|
+
RelativeDatetimeValue,
|
|
22
|
+
TimeBaseScore,
|
|
23
|
+
TypesResourceStrategy,
|
|
24
|
+
};
|
package/src/scripts/index.ts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import convertDuration from './convertDuration/convertDuration';
|
|
2
2
|
import downloadFile from './downloadFile/downloadFile';
|
|
3
3
|
import { FileFormat } from './downloadFile/types/fileFormat.types';
|
|
4
|
+
import {
|
|
5
|
+
isRelativeDatetimeValue,
|
|
6
|
+
normalizeDatetimeRange,
|
|
7
|
+
normalizeToTimestamp,
|
|
8
|
+
} from './normalizeDatetime/normalizeDatetime';
|
|
4
9
|
|
|
5
|
-
export {
|
|
10
|
+
export type {
|
|
11
|
+
DatetimeRangeValue,
|
|
12
|
+
NormalizeDatetimeOptions,
|
|
13
|
+
NormalizeDatetimeValueParam,
|
|
14
|
+
RelativeDatetimeRoundOption,
|
|
15
|
+
} from './normalizeDatetime/normalizeDatetime';
|
|
16
|
+
export {
|
|
17
|
+
convertDuration,
|
|
18
|
+
downloadFile,
|
|
19
|
+
FileFormat,
|
|
20
|
+
isRelativeDatetimeValue,
|
|
21
|
+
normalizeDatetimeRange,
|
|
22
|
+
normalizeToTimestamp,
|
|
23
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
endOfMonth,
|
|
3
|
+
endOfToday,
|
|
4
|
+
endOfWeek,
|
|
5
|
+
startOfMonth,
|
|
6
|
+
startOfToday,
|
|
7
|
+
startOfWeek,
|
|
8
|
+
} from 'date-fns';
|
|
9
|
+
|
|
10
|
+
import { RelativeDatetimeValue } from '../../enums';
|
|
11
|
+
|
|
12
|
+
export type RelativeDatetimeRoundOption =
|
|
13
|
+
| 'start'
|
|
14
|
+
| 'end'; /* for "from" or "to" */
|
|
15
|
+
|
|
16
|
+
export type NormalizeDatetimeValueParam =
|
|
17
|
+
| RelativeDatetimeValue
|
|
18
|
+
| number
|
|
19
|
+
| string
|
|
20
|
+
| null
|
|
21
|
+
| undefined;
|
|
22
|
+
|
|
23
|
+
export type NormalizeDatetimeOptions = {
|
|
24
|
+
round?: RelativeDatetimeRoundOption;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const isRelativeDatetimeValue = (
|
|
28
|
+
value: unknown,
|
|
29
|
+
): value is RelativeDatetimeValue => {
|
|
30
|
+
return (
|
|
31
|
+
typeof value === 'string' &&
|
|
32
|
+
(Object.values(RelativeDatetimeValue) as string[]).includes(value)
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const convertRelativeDatetimeToTimestamp = (
|
|
37
|
+
relativeValue: RelativeDatetimeValue,
|
|
38
|
+
options?: NormalizeDatetimeOptions,
|
|
39
|
+
): number => {
|
|
40
|
+
if (options?.round === 'end') {
|
|
41
|
+
switch (relativeValue) {
|
|
42
|
+
case RelativeDatetimeValue.Today:
|
|
43
|
+
return endOfToday().getTime();
|
|
44
|
+
case RelativeDatetimeValue.ThisWeek:
|
|
45
|
+
return endOfWeek(Date.now(), {
|
|
46
|
+
weekStartsOn: 1,
|
|
47
|
+
}).getTime();
|
|
48
|
+
case RelativeDatetimeValue.ThisMonth:
|
|
49
|
+
return endOfMonth(Date.now()).getTime();
|
|
50
|
+
default:
|
|
51
|
+
return Date.now();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
switch (relativeValue) {
|
|
55
|
+
case RelativeDatetimeValue.Today:
|
|
56
|
+
return startOfToday().getTime();
|
|
57
|
+
case RelativeDatetimeValue.ThisWeek:
|
|
58
|
+
return startOfWeek(Date.now(), {
|
|
59
|
+
weekStartsOn: 1,
|
|
60
|
+
}).getTime();
|
|
61
|
+
case RelativeDatetimeValue.ThisMonth:
|
|
62
|
+
return startOfMonth(Date.now()).getTime();
|
|
63
|
+
default:
|
|
64
|
+
return Date.now();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const normalizeToTimestamp = (
|
|
69
|
+
value?: NormalizeDatetimeValueParam,
|
|
70
|
+
options: NormalizeDatetimeOptions = {},
|
|
71
|
+
): number => {
|
|
72
|
+
if (value == null) {
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
if (typeof value === 'number') {
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
if (isRelativeDatetimeValue(value)) {
|
|
79
|
+
return convertRelativeDatetimeToTimestamp(value, options);
|
|
80
|
+
}
|
|
81
|
+
if (typeof value === 'string') {
|
|
82
|
+
if (+value) {
|
|
83
|
+
return +value;
|
|
84
|
+
}
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
return Date.now();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export type DatetimeRangeValue = {
|
|
91
|
+
from?: NormalizeDatetimeValueParam;
|
|
92
|
+
to?: NormalizeDatetimeValueParam;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const isDatetimeRangeValue = (
|
|
96
|
+
value: unknown,
|
|
97
|
+
): value is DatetimeRangeValue =>
|
|
98
|
+
typeof value === 'object' &&
|
|
99
|
+
value !== null &&
|
|
100
|
+
('from' in value || 'to' in value);
|
|
101
|
+
|
|
102
|
+
export const normalizeDatetimeRange = (
|
|
103
|
+
value?: NormalizeDatetimeValueParam | DatetimeRangeValue,
|
|
104
|
+
): DatetimeRangeValue | undefined => {
|
|
105
|
+
if (value == null || value === '') {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
if (isDatetimeRangeValue(value)) {
|
|
109
|
+
return {
|
|
110
|
+
from:
|
|
111
|
+
value.from == null || value.from === ''
|
|
112
|
+
? undefined
|
|
113
|
+
: normalizeToTimestamp(value.from, {
|
|
114
|
+
round: 'start',
|
|
115
|
+
}),
|
|
116
|
+
to:
|
|
117
|
+
value.to == null || value.to === ''
|
|
118
|
+
? undefined
|
|
119
|
+
: normalizeToTimestamp(value.to, {
|
|
120
|
+
round: 'end',
|
|
121
|
+
}),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
from: normalizeToTimestamp(value, {
|
|
126
|
+
round: 'start',
|
|
127
|
+
}),
|
|
128
|
+
to: normalizeToTimestamp(value, {
|
|
129
|
+
round: 'end',
|
|
130
|
+
}),
|
|
131
|
+
};
|
|
132
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description
|
|
3
|
+
* Vuelidate's `required` semantics, preserved so migrated forms keep behaving
|
|
4
|
+
* the same: `{}` and `[]` count as empty, but `0` and `false` are filled.
|
|
5
|
+
*
|
|
6
|
+
* Getting this wrong is silent — a queue's `priority: 0` or `enabled: false`
|
|
7
|
+
* would start failing a required check that never fired before.
|
|
8
|
+
*/
|
|
9
|
+
export const isFilled = (value: unknown): boolean => {
|
|
10
|
+
if (value === null || value === undefined) return false;
|
|
11
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
12
|
+
if (value instanceof Date) return !Number.isNaN(value.getTime());
|
|
13
|
+
if (typeof value === 'object') return Object.keys(value).length > 0;
|
|
14
|
+
return String(value).length > 0;
|
|
15
|
+
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
import { i18nIssue } from './i18nIssue';
|
|
4
|
+
import { isFilled } from './isFilled';
|
|
5
|
+
|
|
3
6
|
export const lookupSchema = z.object({
|
|
4
7
|
id: z.string().optional(),
|
|
5
8
|
name: z.string().optional(),
|
|
@@ -9,3 +12,28 @@ export const requiredLookupSchema = z.object({
|
|
|
9
12
|
id: z.string().min(1),
|
|
10
13
|
name: z.string().optional(),
|
|
11
14
|
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @description
|
|
18
|
+
* A lookup whose `id` may arrive as a number. Several call-center endpoints
|
|
19
|
+
* return numeric ids, and validating those against `lookupSchema`'s
|
|
20
|
+
* string-only `id` would fail a field the user filled in correctly.
|
|
21
|
+
*/
|
|
22
|
+
export const flexibleLookupSchema = z.object({
|
|
23
|
+
id: z
|
|
24
|
+
.union([
|
|
25
|
+
z.string(),
|
|
26
|
+
z.number(),
|
|
27
|
+
])
|
|
28
|
+
.optional(),
|
|
29
|
+
name: z.string().optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The same, but required — reproducing Vuelidate's `required` on a lookup,
|
|
34
|
+
* which treated an empty object as missing.
|
|
35
|
+
*/
|
|
36
|
+
export const filledLookupSchema = flexibleLookupSchema.refine(
|
|
37
|
+
(value) => isFilled(value),
|
|
38
|
+
i18nIssue('required'),
|
|
39
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @description
|
|
5
|
+
* Queues and queue members both hold user-defined variables as an editable
|
|
6
|
+
* list of pairs, while the wire format is a plain map. The api modules convert
|
|
7
|
+
* between the two; this is the form-side shape.
|
|
8
|
+
*/
|
|
9
|
+
export const variablePairSchema = z.object({
|
|
10
|
+
key: z.string(),
|
|
11
|
+
value: z.string(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The form-side pair, for the components that edit the list. Inferred from the
|
|
16
|
+
* schema so it cannot drift from what the api modules convert against.
|
|
17
|
+
*/
|
|
18
|
+
export type VariablePair = z.infer<typeof variablePairSchema>;
|
package/src/validations/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './_shared/duration.validations';
|
|
2
2
|
export * from './_shared/lookup.validations';
|
|
3
|
+
export * from './_shared/variablePair.validations';
|
|
3
4
|
export * from './auditForm/auditForm.validations';
|
|
4
5
|
export * from './bucket/bucket.validations';
|
|
5
6
|
export * from './calendar/calendar.validations';
|
|
@@ -15,6 +16,17 @@ export * from './contactGroup/contactGroup.validations';
|
|
|
15
16
|
export * from './contactGroupCondition/contactGroupCondition.validations';
|
|
16
17
|
export * from './OAuth/OAuth.validations';
|
|
17
18
|
export * from './onlineSkill/onlineSkill.validations';
|
|
19
|
+
export * from './queue/queue.rules';
|
|
20
|
+
export * from './queue/queue.validations';
|
|
21
|
+
export * from './queue/queueAmd.validations';
|
|
22
|
+
export * from './queue/queuePayload.validations';
|
|
23
|
+
export * from './queue/taskProcessing.validations';
|
|
24
|
+
export * from './queueBucket/queueBucket.validations';
|
|
25
|
+
export * from './queueHook/queueHook.validations';
|
|
26
|
+
export * from './queueMember/memberCommunication.validations';
|
|
27
|
+
export * from './queueMember/queueMember.validations';
|
|
28
|
+
export * from './queueResGroup/queueResGroup.validations';
|
|
29
|
+
export * from './queueSkill/queueSkill.validations';
|
|
18
30
|
export * from './sla/sla.validations';
|
|
19
31
|
export * from './slaCondition/slaCondition.validations';
|
|
20
32
|
export * from './types';
|