@webitel/api-services 26.8.77 → 26.8.78
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/workingConditions/__tests__/workingConditions.spec.ts +162 -0
- package/src/api/clients/workingConditions/workingConditions.ts +31 -46
- package/src/validations/index.ts +1 -0
- package/src/validations/workingCondition/__tests__/workingCondition.validations.spec.ts +81 -0
- package/src/validations/workingCondition/workingCondition.validations.ts +25 -0
- package/types/.tsbuildinfo +1 -1
- package/types/api/clients/workingConditions/workingConditions.d.ts +1 -6
- package/types/validations/index.d.ts +1 -0
- package/types/validations/workingCondition/workingCondition.validations.d.ts +19 -0
package/package.json
CHANGED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const wireItem = {
|
|
4
|
+
id: '1',
|
|
5
|
+
name: 'FTE 1',
|
|
6
|
+
description: 'desc',
|
|
7
|
+
workday_hours: 9,
|
|
8
|
+
workdays_per_month: 20,
|
|
9
|
+
vacation: 24,
|
|
10
|
+
sick_leaves: 10,
|
|
11
|
+
days_off: 5,
|
|
12
|
+
pause_duration: 60,
|
|
13
|
+
pause_template: {
|
|
14
|
+
id: '7',
|
|
15
|
+
name: '30-15-15',
|
|
16
|
+
},
|
|
17
|
+
shift_template: {
|
|
18
|
+
id: '3',
|
|
19
|
+
name: 'Full time',
|
|
20
|
+
},
|
|
21
|
+
domain_id: '1',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const service = {
|
|
25
|
+
workingConditionServiceSearchWorkingCondition: vi.fn(() =>
|
|
26
|
+
Promise.resolve({
|
|
27
|
+
data: {
|
|
28
|
+
items: [
|
|
29
|
+
wireItem,
|
|
30
|
+
],
|
|
31
|
+
next: true,
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
),
|
|
35
|
+
workingConditionServiceReadWorkingCondition: vi.fn(() =>
|
|
36
|
+
Promise.resolve({
|
|
37
|
+
data: {
|
|
38
|
+
item: wireItem,
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
),
|
|
42
|
+
workingConditionServiceCreateWorkingCondition: vi.fn(() =>
|
|
43
|
+
Promise.resolve({
|
|
44
|
+
data: {
|
|
45
|
+
item: wireItem,
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
),
|
|
49
|
+
workingConditionServiceUpdateWorkingCondition: vi.fn(() =>
|
|
50
|
+
Promise.resolve({
|
|
51
|
+
data: {
|
|
52
|
+
item: wireItem,
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
vi.mock('../../../../gen-wire', async (importOriginal) => ({
|
|
59
|
+
...(await importOriginal<Record<string, unknown>>()),
|
|
60
|
+
getWorkingConditionService: () => service,
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
const { WorkingConditionsAPI } = await import('../workingConditions');
|
|
64
|
+
|
|
65
|
+
const formItem = {
|
|
66
|
+
id: '1',
|
|
67
|
+
name: 'FTE 1',
|
|
68
|
+
description: 'desc',
|
|
69
|
+
workdayHours: 9,
|
|
70
|
+
workdaysPerMonth: 20,
|
|
71
|
+
vacation: 24,
|
|
72
|
+
sickLeaves: 10,
|
|
73
|
+
daysOff: 5,
|
|
74
|
+
pauseDuration: 60,
|
|
75
|
+
pauseTemplate: {
|
|
76
|
+
id: '7',
|
|
77
|
+
name: '30-15-15',
|
|
78
|
+
},
|
|
79
|
+
shiftTemplate: null,
|
|
80
|
+
domainId: '1',
|
|
81
|
+
createdAt: '1',
|
|
82
|
+
_dirty: true,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
describe('WorkingConditionsAPI', () => {
|
|
86
|
+
beforeEach(() => {
|
|
87
|
+
vi.clearAllMocks();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('getList sends the search as `q`', async () => {
|
|
91
|
+
const { items, next } = await WorkingConditionsAPI.getList({
|
|
92
|
+
search: 'fte',
|
|
93
|
+
page: 2,
|
|
94
|
+
size: 10,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(
|
|
98
|
+
service.workingConditionServiceSearchWorkingCondition,
|
|
99
|
+
).toHaveBeenCalledWith(
|
|
100
|
+
expect.objectContaining({
|
|
101
|
+
q: 'fte*',
|
|
102
|
+
page: 2,
|
|
103
|
+
size: 10,
|
|
104
|
+
}),
|
|
105
|
+
);
|
|
106
|
+
expect(items[0].workdaysPerMonth).toBe(20);
|
|
107
|
+
expect(next).toBe(true);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('get and update unwrap the `item` envelope', async () => {
|
|
111
|
+
const item = await WorkingConditionsAPI.get({
|
|
112
|
+
itemId: '1',
|
|
113
|
+
});
|
|
114
|
+
expect(item.pauseTemplate).toEqual(wireItem.pause_template);
|
|
115
|
+
|
|
116
|
+
const updated = await WorkingConditionsAPI.update({
|
|
117
|
+
itemId: '1',
|
|
118
|
+
itemInstance: formItem,
|
|
119
|
+
});
|
|
120
|
+
expect(updated.name).toBe('FTE 1');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('add wraps the item and sends only the form fields', async () => {
|
|
124
|
+
await WorkingConditionsAPI.add({
|
|
125
|
+
itemInstance: formItem,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const [body] =
|
|
129
|
+
service.workingConditionServiceCreateWorkingCondition.mock.calls[0];
|
|
130
|
+
expect(body).toEqual({
|
|
131
|
+
item: {
|
|
132
|
+
name: 'FTE 1',
|
|
133
|
+
description: 'desc',
|
|
134
|
+
workday_hours: 9,
|
|
135
|
+
workdays_per_month: 20,
|
|
136
|
+
vacation: 24,
|
|
137
|
+
sick_leaves: 10,
|
|
138
|
+
days_off: 5,
|
|
139
|
+
pause_duration: 60,
|
|
140
|
+
pause_template: {
|
|
141
|
+
id: '7',
|
|
142
|
+
name: '30-15-15',
|
|
143
|
+
},
|
|
144
|
+
shift_template: null,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('update sends the id separately and drops it from the item', async () => {
|
|
150
|
+
await WorkingConditionsAPI.update({
|
|
151
|
+
itemId: '1',
|
|
152
|
+
itemInstance: formItem,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const [id, body] =
|
|
156
|
+
service.workingConditionServiceUpdateWorkingCondition.mock.calls[0];
|
|
157
|
+
expect(id).toBe('1');
|
|
158
|
+
expect(body.item).not.toHaveProperty('id');
|
|
159
|
+
expect(body.item).not.toHaveProperty('domain_id');
|
|
160
|
+
expect(body.item).not.toHaveProperty('_dirty');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getShallowFieldsToSendFromZodSchema } from '@webitel/api-services/gen/utils';
|
|
2
|
+
import { workingConditionSchema } from '@webitel/api-services/validations';
|
|
3
|
+
import {
|
|
4
|
+
getWorkingConditionService,
|
|
5
|
+
WorkingConditionServiceSearchWorkingConditionQueryParams,
|
|
6
|
+
} from '../../../gen-wire';
|
|
2
7
|
import { getDefaultGetListResponse, getDefaultGetParams } from '../../defaults';
|
|
3
8
|
import {
|
|
4
9
|
applyTransform,
|
|
5
10
|
camelToSnake,
|
|
6
11
|
merge,
|
|
7
12
|
notify,
|
|
8
|
-
|
|
13
|
+
sanitizeToWire,
|
|
9
14
|
snakeToCamel,
|
|
15
|
+
starToSearch,
|
|
10
16
|
} from '../../transformers';
|
|
11
17
|
import type {
|
|
12
18
|
AddItemParams,
|
|
@@ -16,53 +22,37 @@ import type {
|
|
|
16
22
|
UpdateItemParams,
|
|
17
23
|
} from '../_shared/types';
|
|
18
24
|
|
|
19
|
-
const fieldsToSend =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'workdayHours',
|
|
23
|
-
'workdayPerMonth',
|
|
24
|
-
'pauseDuration',
|
|
25
|
-
'vacation',
|
|
26
|
-
'pauseTemplate',
|
|
27
|
-
'sickLeaves',
|
|
28
|
-
'shiftTemplate',
|
|
29
|
-
'daysOff',
|
|
30
|
-
'createdAt',
|
|
31
|
-
'createdBy',
|
|
32
|
-
'domainId',
|
|
33
|
-
'id',
|
|
34
|
-
'updatedAt',
|
|
35
|
-
'updatedBy',
|
|
36
|
-
];
|
|
25
|
+
const fieldsToSend = getShallowFieldsToSendFromZodSchema(
|
|
26
|
+
workingConditionSchema,
|
|
27
|
+
);
|
|
37
28
|
|
|
38
29
|
/**
|
|
39
|
-
* WFM services wrap both request and response payloads in an `item` envelope.
|
|
30
|
+
* [Claude] WFM services wrap both request and response payloads in an `item` envelope.
|
|
40
31
|
*/
|
|
41
32
|
const itemResponseHandler = (response: ApiParams) => ({
|
|
42
33
|
...response.item,
|
|
43
34
|
});
|
|
44
35
|
|
|
45
36
|
const getWorkingConditionsList = async (params: ApiParams) => {
|
|
46
|
-
const
|
|
47
|
-
search: q,
|
|
48
|
-
page,
|
|
49
|
-
size,
|
|
50
|
-
sort,
|
|
51
|
-
fields,
|
|
52
|
-
} = applyTransform(params, [
|
|
37
|
+
const requestParams = applyTransform(params, [
|
|
53
38
|
merge(getDefaultGetParams()),
|
|
39
|
+
starToSearch('search'),
|
|
40
|
+
(params: ApiParams) => ({
|
|
41
|
+
...params,
|
|
42
|
+
q: params.q ?? params.search,
|
|
43
|
+
}),
|
|
44
|
+
sanitizeToWire(
|
|
45
|
+
getShallowFieldsToSendFromZodSchema(
|
|
46
|
+
WorkingConditionServiceSearchWorkingConditionQueryParams,
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
camelToSnake(),
|
|
54
50
|
]);
|
|
55
51
|
|
|
56
52
|
try {
|
|
57
53
|
const response =
|
|
58
54
|
await getWorkingConditionService().workingConditionServiceSearchWorkingCondition(
|
|
59
|
-
|
|
60
|
-
page,
|
|
61
|
-
size,
|
|
62
|
-
q,
|
|
63
|
-
sort,
|
|
64
|
-
fields,
|
|
65
|
-
},
|
|
55
|
+
requestParams,
|
|
66
56
|
);
|
|
67
57
|
const { items, next } = applyTransform(response.data, [
|
|
68
58
|
snakeToCamel(),
|
|
@@ -98,16 +88,14 @@ const getWorkingCondition = async ({ itemId: id }: GetItemParams) => {
|
|
|
98
88
|
|
|
99
89
|
const addWorkingCondition = async ({ itemInstance }: AddItemParams) => {
|
|
100
90
|
const item = applyTransform(itemInstance, [
|
|
101
|
-
|
|
91
|
+
sanitizeToWire(fieldsToSend),
|
|
102
92
|
camelToSnake(),
|
|
103
93
|
]);
|
|
104
94
|
try {
|
|
105
95
|
const response =
|
|
106
96
|
await getWorkingConditionService().workingConditionServiceCreateWorkingCondition(
|
|
107
97
|
{
|
|
108
|
-
item
|
|
109
|
-
...item,
|
|
110
|
-
},
|
|
98
|
+
item,
|
|
111
99
|
},
|
|
112
100
|
);
|
|
113
101
|
return applyTransform(response.data, [
|
|
@@ -126,7 +114,7 @@ const updateWorkingCondition = async ({
|
|
|
126
114
|
itemId: id,
|
|
127
115
|
}: UpdateItemParams) => {
|
|
128
116
|
const item = applyTransform(itemInstance, [
|
|
129
|
-
|
|
117
|
+
sanitizeToWire(fieldsToSend),
|
|
130
118
|
camelToSnake(),
|
|
131
119
|
]);
|
|
132
120
|
try {
|
|
@@ -134,13 +122,12 @@ const updateWorkingCondition = async ({
|
|
|
134
122
|
await getWorkingConditionService().workingConditionServiceUpdateWorkingCondition(
|
|
135
123
|
String(id),
|
|
136
124
|
{
|
|
137
|
-
item
|
|
138
|
-
...item,
|
|
139
|
-
},
|
|
125
|
+
item,
|
|
140
126
|
},
|
|
141
127
|
);
|
|
142
128
|
return applyTransform(response.data, [
|
|
143
129
|
snakeToCamel(),
|
|
130
|
+
itemResponseHandler,
|
|
144
131
|
]);
|
|
145
132
|
} catch (err) {
|
|
146
133
|
throw applyTransform(err, [
|
|
@@ -163,9 +150,7 @@ const deleteWorkingCondition = async ({ id }: DeleteItemParams) => {
|
|
|
163
150
|
}
|
|
164
151
|
};
|
|
165
152
|
|
|
166
|
-
const getWorkingConditionsLookup = (
|
|
167
|
-
params: Parameters<typeof getWorkingConditionsList>[0],
|
|
168
|
-
) =>
|
|
153
|
+
const getWorkingConditionsLookup = (params: ApiParams) =>
|
|
169
154
|
getWorkingConditionsList({
|
|
170
155
|
...params,
|
|
171
156
|
fields: params.fields || [
|
package/src/validations/index.ts
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { workingConditionSchema } from '../workingCondition.validations';
|
|
4
|
+
|
|
5
|
+
const valid = {
|
|
6
|
+
name: 'FTE 1',
|
|
7
|
+
pauseTemplate: {
|
|
8
|
+
id: '1',
|
|
9
|
+
name: '30-15-15',
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe('workingConditionSchema', () => {
|
|
14
|
+
it('accepts a condition with only a name and a pause template', () => {
|
|
15
|
+
expect(workingConditionSchema.safeParse(valid).success).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('requires a pause template', () => {
|
|
19
|
+
expect(
|
|
20
|
+
workingConditionSchema.safeParse({
|
|
21
|
+
name: 'FTE 1',
|
|
22
|
+
}).success,
|
|
23
|
+
).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('keeps the workday duration optional and clearable', () => {
|
|
27
|
+
expect(
|
|
28
|
+
workingConditionSchema.safeParse({
|
|
29
|
+
...valid,
|
|
30
|
+
workdayHours: null,
|
|
31
|
+
}).success,
|
|
32
|
+
).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('rejects a name longer than 250 symbols', () => {
|
|
36
|
+
expect(
|
|
37
|
+
workingConditionSchema.safeParse({
|
|
38
|
+
...valid,
|
|
39
|
+
name: 'a'.repeat(251),
|
|
40
|
+
}).success,
|
|
41
|
+
).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it.each([
|
|
45
|
+
[
|
|
46
|
+
'workdayHours',
|
|
47
|
+
25,
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
'workdaysPerMonth',
|
|
51
|
+
32,
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
'vacation',
|
|
55
|
+
366,
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
'sickLeaves',
|
|
59
|
+
366,
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
'daysOff',
|
|
63
|
+
366,
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
'pauseDuration',
|
|
67
|
+
1441,
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
'workdayHours',
|
|
71
|
+
-1,
|
|
72
|
+
],
|
|
73
|
+
])('rejects %s = %j', (field, value) => {
|
|
74
|
+
expect(
|
|
75
|
+
workingConditionSchema.safeParse({
|
|
76
|
+
...valid,
|
|
77
|
+
[field]: value,
|
|
78
|
+
}).success,
|
|
79
|
+
).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { WfmWorkingCondition } from '@webitel/api-services/gen/models';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
filledLookupSchema,
|
|
6
|
+
flexibleLookupSchema,
|
|
7
|
+
} from '../_shared/lookup.validations';
|
|
8
|
+
import type { ZodShape } from '../types';
|
|
9
|
+
|
|
10
|
+
/** [Claude] an optional whole number the user may clear (`wt-input-number` emits `null`) */
|
|
11
|
+
const optionalCountSchema = (max: number) =>
|
|
12
|
+
z.number().int().min(0).max(max).nullish();
|
|
13
|
+
|
|
14
|
+
export const workingConditionSchema = z.object<ZodShape<WfmWorkingCondition>>({
|
|
15
|
+
name: z.string().min(1).max(250),
|
|
16
|
+
description: z.string().optional().default(''),
|
|
17
|
+
workdayHours: optionalCountSchema(24),
|
|
18
|
+
workdaysPerMonth: optionalCountSchema(31),
|
|
19
|
+
vacation: optionalCountSchema(365),
|
|
20
|
+
sickLeaves: optionalCountSchema(365),
|
|
21
|
+
daysOff: optionalCountSchema(365),
|
|
22
|
+
pauseDuration: optionalCountSchema(1440),
|
|
23
|
+
pauseTemplate: filledLookupSchema,
|
|
24
|
+
shiftTemplate: flexibleLookupSchema.nullish(),
|
|
25
|
+
});
|