@webitel/api-services 26.8.75 → 26.8.77
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 +1 -1
- package/src/api/clients/pauseTemplates/__tests__/pauseTemplates.spec.ts +144 -0
- package/src/api/clients/pauseTemplates/pauseTemplates.ts +33 -71
- package/src/api/clients/shiftTemplates/__tests__/shiftTemplates.spec.ts +145 -0
- package/src/api/clients/shiftTemplates/shiftTemplates.ts +34 -45
- package/src/validations/index.ts +2 -0
- package/src/validations/pauseTemplate/__tests__/pauseTemplate.validations.spec.ts +85 -0
- package/src/validations/pauseTemplate/pauseTemplate.validations.ts +51 -0
- package/src/validations/shiftTemplate/__tests__/shiftTemplate.validations.spec.ts +146 -0
- package/src/validations/shiftTemplate/shiftTemplate.validations.ts +38 -0
- package/types/.tsbuildinfo +1 -1
- package/types/api/clients/pauseTemplates/pauseTemplates.d.ts +1 -6
- package/types/api/clients/shiftTemplates/shiftTemplates.d.ts +1 -6
- package/types/validations/index.d.ts +2 -0
- package/types/validations/pauseTemplate/pauseTemplate.validations.d.ts +30 -0
- package/types/validations/shiftTemplate/shiftTemplate.validations.d.ts +23 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WfmPauseTemplate,
|
|
3
|
+
WfmPauseTemplateCause,
|
|
4
|
+
} from '@webitel/api-services/gen/models';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
import { flexibleLookupSchema } from '../_shared/lookup.validations';
|
|
8
|
+
import type { ZodShape } from '../types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* [Claude] the API returns the duration (minutes) as an int64 string. An emptied
|
|
12
|
+
* field becomes `undefined` (not `NaN` as with `z.coerce`), so the form shows
|
|
13
|
+
* "required" instead of zod's raw "expected number, received NaN".
|
|
14
|
+
*/
|
|
15
|
+
const durationMinutesSchema = z.preprocess(
|
|
16
|
+
(value) =>
|
|
17
|
+
value === '' || value === null || value === undefined
|
|
18
|
+
? undefined
|
|
19
|
+
: Number(value),
|
|
20
|
+
z.number().int().min(1),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* [Claude] the pause cause is optional ("Not selected"), the duration is not.
|
|
25
|
+
*/
|
|
26
|
+
export const pauseTemplateCauseSchema = z.object({
|
|
27
|
+
cause: flexibleLookupSchema.nullish(),
|
|
28
|
+
duration: durationMinutesSchema,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* [Claude] a new template row: no pause cause ("Not selected"), 30 minutes.
|
|
33
|
+
* Shared by the schema default and the form's "add row" action.
|
|
34
|
+
*/
|
|
35
|
+
export const getDefaultPauseTemplateCause = (): WfmPauseTemplateCause & {
|
|
36
|
+
duration: string;
|
|
37
|
+
} => ({
|
|
38
|
+
duration: '30',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export const pauseTemplateSchema = z.object<ZodShape<WfmPauseTemplate>>({
|
|
42
|
+
name: z.string().min(1).max(250).default(''),
|
|
43
|
+
description: z.string().optional().default(''),
|
|
44
|
+
causes: z
|
|
45
|
+
.array(pauseTemplateCauseSchema)
|
|
46
|
+
.min(1)
|
|
47
|
+
// [Claude] `.prefault`, not `.default`: the API-shaped row (duration as a string) goes through the schema
|
|
48
|
+
.prefault(() => [
|
|
49
|
+
getDefaultPauseTemplateCause(),
|
|
50
|
+
]),
|
|
51
|
+
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { getDefaultsFromZodSchema } from '@webitel/api-services/gen/utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
getDefaultShiftTemplateTime,
|
|
6
|
+
getShiftTemplateTimeRangeErrors,
|
|
7
|
+
shiftTemplateSchema,
|
|
8
|
+
} from '../shiftTemplate.validations';
|
|
9
|
+
|
|
10
|
+
const valid = {
|
|
11
|
+
name: 'Day',
|
|
12
|
+
times: [
|
|
13
|
+
{
|
|
14
|
+
start: 540,
|
|
15
|
+
end: 1200,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
describe('shiftTemplateSchema', () => {
|
|
21
|
+
it('accepts a template with a name and a time range', () => {
|
|
22
|
+
expect(shiftTemplateSchema.safeParse(valid).success).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('fills a new template with one 9:00–18:00 row', () => {
|
|
26
|
+
expect(getDefaultShiftTemplateTime()).toEqual({
|
|
27
|
+
start: 540,
|
|
28
|
+
end: 1080,
|
|
29
|
+
});
|
|
30
|
+
expect(getDefaultsFromZodSchema(shiftTemplateSchema, {})).toEqual(
|
|
31
|
+
expect.objectContaining({
|
|
32
|
+
times: [
|
|
33
|
+
getDefaultShiftTemplateTime(),
|
|
34
|
+
],
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('accepts a new template once the name is filled', () => {
|
|
40
|
+
const defaults = getDefaultsFromZodSchema(
|
|
41
|
+
shiftTemplateSchema,
|
|
42
|
+
{},
|
|
43
|
+
) as object;
|
|
44
|
+
|
|
45
|
+
expect(
|
|
46
|
+
shiftTemplateSchema.safeParse({
|
|
47
|
+
...defaults,
|
|
48
|
+
name: 'Day',
|
|
49
|
+
}).success,
|
|
50
|
+
).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('requires a name', () => {
|
|
54
|
+
expect(
|
|
55
|
+
shiftTemplateSchema.safeParse({
|
|
56
|
+
...valid,
|
|
57
|
+
name: '',
|
|
58
|
+
}).success,
|
|
59
|
+
).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('rejects a name longer than 250 symbols', () => {
|
|
63
|
+
expect(
|
|
64
|
+
shiftTemplateSchema.safeParse({
|
|
65
|
+
...valid,
|
|
66
|
+
name: 'a'.repeat(251),
|
|
67
|
+
}).success,
|
|
68
|
+
).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('requires at least one time range', () => {
|
|
72
|
+
expect(
|
|
73
|
+
shiftTemplateSchema.safeParse({
|
|
74
|
+
...valid,
|
|
75
|
+
times: [],
|
|
76
|
+
}).success,
|
|
77
|
+
).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it.each([
|
|
81
|
+
[
|
|
82
|
+
'end before start',
|
|
83
|
+
{
|
|
84
|
+
start: 600,
|
|
85
|
+
end: 540,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
'zero duration',
|
|
90
|
+
{
|
|
91
|
+
start: 600,
|
|
92
|
+
end: 600,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
'end out of the day',
|
|
97
|
+
{
|
|
98
|
+
start: 600,
|
|
99
|
+
end: 1440,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
[
|
|
103
|
+
'empty start',
|
|
104
|
+
{
|
|
105
|
+
end: 600,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
])('rejects a row with %s', (_, time) => {
|
|
109
|
+
expect(
|
|
110
|
+
shiftTemplateSchema.safeParse({
|
|
111
|
+
...valid,
|
|
112
|
+
times: [
|
|
113
|
+
time,
|
|
114
|
+
],
|
|
115
|
+
}).success,
|
|
116
|
+
).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('getShiftTemplateTimeRangeErrors', () => {
|
|
121
|
+
it('marks both ends of a row whose start is not before its end', () => {
|
|
122
|
+
expect(
|
|
123
|
+
getShiftTemplateTimeRangeErrors([
|
|
124
|
+
{
|
|
125
|
+
start: 540,
|
|
126
|
+
end: 1200,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
start: 600,
|
|
130
|
+
end: 540,
|
|
131
|
+
},
|
|
132
|
+
]),
|
|
133
|
+
).toEqual([
|
|
134
|
+
{
|
|
135
|
+
index: 1,
|
|
136
|
+
prop: 'start',
|
|
137
|
+
key: 'timerangeStartLessThanEnd',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
index: 1,
|
|
141
|
+
prop: 'end',
|
|
142
|
+
key: 'timerangeStartLessThanEnd',
|
|
143
|
+
},
|
|
144
|
+
]);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { WfmShiftTemplate } from '@webitel/api-services/gen/models';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
getTimeRangeErrors,
|
|
6
|
+
type TimeRange,
|
|
7
|
+
timeRangeSchema,
|
|
8
|
+
} from '../_shared/timeRange.validations';
|
|
9
|
+
import type { ZodShape } from '../types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* [Claude] each row is a part of the shift within a day: start < end, both in minutes
|
|
13
|
+
*/
|
|
14
|
+
const shiftTemplateTimesSchema = z.array(timeRangeSchema).min(1);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* [Claude] row errors for `useTimeRangesValidation`: regle marks a row as a plain field,
|
|
18
|
+
* so the custom range issues are read from the schema directly
|
|
19
|
+
*/
|
|
20
|
+
export const getShiftTemplateTimeRangeErrors = (items: unknown) =>
|
|
21
|
+
getTimeRangeErrors(shiftTemplateTimesSchema, items);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* [Claude] a new template row: 9:00–18:00, 9 hours (minutes of the day).
|
|
25
|
+
* Shared by the schema default and the form's "add row" action.
|
|
26
|
+
*/
|
|
27
|
+
export const getDefaultShiftTemplateTime = (): TimeRange => ({
|
|
28
|
+
start: 9 * 60,
|
|
29
|
+
end: 18 * 60,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const shiftTemplateSchema = z.object<ZodShape<WfmShiftTemplate>>({
|
|
33
|
+
name: z.string().min(1).max(250).default(''),
|
|
34
|
+
description: z.string().optional().default(''),
|
|
35
|
+
times: shiftTemplateTimesSchema.default(() => [
|
|
36
|
+
getDefaultShiftTemplateTime(),
|
|
37
|
+
]),
|
|
38
|
+
});
|