@controleonline/ui-customers 1.0.81 → 1.0.83
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/.github/workflows/integration-source-gate.yml +68 -0
- package/.github/workflows/publish-npm.yml +91 -0
- package/AGENTS.md +12 -6
- package/docs/technical/Client-Details-Criar-Usuario.md +113 -0
- package/package.json +5 -4
- package/src/react/components/tabs/AddressesTab.js +240 -335
- package/src/react/components/tabs/DocumentAttachments.js +270 -0
- package/src/react/components/tabs/DocumentsTab.js +355 -463
- package/src/react/components/tabs/DocumentsTab.styles.js +123 -0
- package/src/react/components/tabs/EmployeeCreateFormFields.js +203 -0
- package/src/react/components/tabs/EmployeesTab.js +17 -171
- package/src/react/components/tabs/EmployeesTab.styles.js +220 -172
- package/src/react/components/tabs/GeneralTab.js +84 -46
- package/src/react/components/tabs/GeneralTab.styles.js +81 -77
- package/src/react/components/tabs/MediaTab.js +50 -5
- package/src/react/components/tabs/PeopleCategoriesPanel.js +161 -78
- package/src/react/components/tabs/SalesmanTab.js +1 -1
- package/src/react/components/tabs/UserFormModal.js +26 -2
- package/src/react/components/tabs/UsersTab.js +30 -19
- package/src/react/components/tabs/UsersTab.styles.js +257 -257
- package/src/react/components/tabs/UsersTabApiKeyModal.js +115 -0
- package/src/react/components/tabs/UsersTabUserModal.js +222 -0
- package/src/react/components/tabs/addressesTabHelpers.js +190 -0
- package/src/react/components/tabs/documentsTabHelpers.js +142 -0
- package/src/react/components/tabs/employeeContacts.js +3 -23
- package/src/react/components/tabs/employeeUnlink.js +7 -4
- package/src/react/components/tabs/employeesTabHelpers.js +33 -9
- package/src/react/components/tabs/generalTabHelpers.js +30 -8
- package/src/react/components/tabs/useGeneralTabPeopleLink.js +68 -0
- package/src/react/components/tabs/usersTabHelpers.js +24 -23
- package/src/react/pages/details.page.styles.js +114 -100
- package/src/react/pages/employees.js +4 -0
- package/src/react/utils/humanCompanyLinkTypes.js +28 -0
- package/src/tests/browser/manager/client-details-add-user-modal.spec.js +281 -0
- package/src/tests/browser/manager/client-details-address-create-coords.spec.js +414 -0
- package/src/tests/browser/manager/client-details-address-edit.spec.js +331 -0
- package/src/tests/browser/manager/client-details-contact-edit.spec.js +198 -0
- package/src/tests/browser/manager/client-details-create-contact.spec.js +306 -0
- package/src/tests/browser/manager/client-details-documents.spec.js +346 -0
- package/src/tests/browser/manager/client-details-identity-case.spec.js +227 -0
- package/src/tests/browser/manager/client-details-people-type.manifest.md +18 -0
- package/src/tests/browser/manager/client-details-people-type.spec.js +345 -0
- package/src/tests/browser/manager/client-details-supplier-product-create.spec.js +334 -0
- package/src/tests/browser/manager/client-details-users-tab-load.spec.js +194 -0
- package/src/tests/browser/manager/employees-salesman-after-sales.spec.js +441 -0
- package/src/tests/react/components/SalesmanTab.company.test.js +40 -0
- package/src/tests/react/components/tabs/UsersTab.styles.test.js +39 -0
- package/src/tests/react/components/tabs/addressesTabHelpers.test.js +78 -0
- package/src/tests/react/components/tabs/documentsTabContract.test.mjs +37 -0
- package/src/tests/react/components/tabs/documentsTabHelpers.test.mjs +93 -0
- package/src/tests/react/components/tabs/employeeContacts.test.js +46 -4
- package/src/tests/react/components/tabs/employeeLinkTypes.contract.test.mjs +52 -0
- package/src/tests/react/components/tabs/employeeUnlink.test.js +11 -11
- package/src/tests/react/components/tabs/employeesTabHelpers.media.test.js +2 -23
- package/src/tests/react/components/tabs/employeesTabHelpers.test.js +59 -0
- package/src/tests/react/components/tabs/generalTabHelpers.test.js +35 -0
- package/src/tests/react/components/tabs/peopleMediaTabs.test.js +217 -217
- package/src/tests/react/components/tabs/peopleType.contract.test.mjs +47 -0
- package/src/tests/react/components/tabs/resolveSeededLinkType.test.mjs +20 -0
- package/src/tests/react/components/tabs/salesmanTabMedia.no-redeclare.test.mjs +33 -0
- package/src/tests/react/components/tabs/usersTabHelpers.test.js +20 -0
- package/src/tests/react/components/tabs/usersTabHelpers.test.mjs +63 -0
- package/src/tests/react/pages/employees.test.js +2 -0
- package/src/tests/react/utils/humanCompanyLinkTypes.test.js +50 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import {
|
|
4
|
+
applyDocumentMask,
|
|
5
|
+
extractId,
|
|
6
|
+
isImageFile,
|
|
7
|
+
isPdfFile,
|
|
8
|
+
normalizeDocumentFiles,
|
|
9
|
+
removeMask,
|
|
10
|
+
toDocumentIri,
|
|
11
|
+
toFileIri,
|
|
12
|
+
toDocumentItem,
|
|
13
|
+
unwrapUploadFile,
|
|
14
|
+
} from '../../../../react/components/tabs/documentsTabHelpers.js';
|
|
15
|
+
|
|
16
|
+
test('extractId resolves numeric, string and IRI values', () => {
|
|
17
|
+
assert.equal(extractId(12), '12');
|
|
18
|
+
assert.equal(extractId('/documents/45'), '45');
|
|
19
|
+
assert.equal(extractId({ '@id': '/files/9', id: 9 }), '9');
|
|
20
|
+
assert.equal(extractId(null), '');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('applyDocumentMask formats CPF and CNPJ', () => {
|
|
24
|
+
assert.equal(applyDocumentMask('12345678901', 'CPF'), '123.456.789-01');
|
|
25
|
+
assert.equal(applyDocumentMask('12345678000199', 'CNPJ'), '12.345.678/0001-99');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('removeMask strips non-digits', () => {
|
|
29
|
+
assert.equal(removeMask('12.345.678/0001-99'), '12345678000199');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('file type helpers detect image and pdf urls', () => {
|
|
33
|
+
assert.equal(isImageFile('https://cdn.example/file.PNG'), true);
|
|
34
|
+
assert.equal(isPdfFile('https://cdn.example/doc.pdf'), true);
|
|
35
|
+
assert.equal(isPdfFile('https://cdn.example/doc.png'), false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('IRI helpers build resource paths', () => {
|
|
39
|
+
assert.equal(toDocumentIri(15), '/documents/15');
|
|
40
|
+
assert.equal(toFileIri({ id: 3 }), '/files/3');
|
|
41
|
+
assert.equal(toFileIri('/files/8'), '/files/8');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('normalizeDocumentFiles prefers documentFiles collection over legacy file', () => {
|
|
45
|
+
const resolveUrl = file => file?.url || '';
|
|
46
|
+
const document = {
|
|
47
|
+
id: 1,
|
|
48
|
+
document: '123',
|
|
49
|
+
documentType: { '@id': '/document_types/1', documentType: 'CNPJ' },
|
|
50
|
+
file: { id: 99, url: 'https://cdn.example/legacy.pdf' },
|
|
51
|
+
documentFiles: [
|
|
52
|
+
{
|
|
53
|
+
id: 10,
|
|
54
|
+
file: { id: 20, name: 'cartao-cnpj.png', url: 'https://cdn.example/cartao.png' },
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 11,
|
|
58
|
+
file: { id: 21, name: 'contrato.pdf', url: 'https://cdn.example/contrato.pdf' },
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const files = normalizeDocumentFiles(document, resolveUrl);
|
|
64
|
+
assert.equal(files.length, 2);
|
|
65
|
+
assert.equal(files[0].name, 'cartao-cnpj.png');
|
|
66
|
+
assert.equal(files[1].name, 'contrato.pdf');
|
|
67
|
+
assert.equal(files[0].legacy, false);
|
|
68
|
+
|
|
69
|
+
const legacyOnly = normalizeDocumentFiles(
|
|
70
|
+
{ file: { id: 5, name: 'rg.jpg', url: 'https://cdn.example/rg.jpg' } },
|
|
71
|
+
resolveUrl,
|
|
72
|
+
);
|
|
73
|
+
assert.equal(legacyOnly.length, 1);
|
|
74
|
+
assert.equal(legacyOnly[0].legacy, true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('toDocumentItem maps API payload', () => {
|
|
78
|
+
const item = toDocumentItem({
|
|
79
|
+
'@id': '/documents/7',
|
|
80
|
+
id: 7,
|
|
81
|
+
document: '999',
|
|
82
|
+
documentType: { '@id': '/document_types/2' },
|
|
83
|
+
});
|
|
84
|
+
assert.equal(item.id, '7');
|
|
85
|
+
assert.equal(item.value, '999');
|
|
86
|
+
assert.equal(item.type, '/document_types/2');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('unwrapUploadFile accepts hydra and nested shapes', () => {
|
|
90
|
+
assert.deepEqual(unwrapUploadFile({ file: { id: 1 } }), { id: 1 });
|
|
91
|
+
assert.deepEqual(unwrapUploadFile({ 'hydra:member': [{ id: 2 }] }), { id: 2 });
|
|
92
|
+
assert.deepEqual(unwrapUploadFile({ member: [{ id: 3 }] }), { id: 3 });
|
|
93
|
+
});
|
|
@@ -7,6 +7,7 @@ const {
|
|
|
7
7
|
extractPeopleLinkItems,
|
|
8
8
|
filterPeopleLinksByScope,
|
|
9
9
|
normalizeEmployeeLinkType,
|
|
10
|
+
EMPLOYEE_CONTACT_LINK_TYPES,
|
|
10
11
|
} = require('../../../../react/components/tabs/employeeContacts')
|
|
11
12
|
|
|
12
13
|
describe('employeeContacts', () => {
|
|
@@ -21,9 +22,23 @@ describe('employeeContacts', () => {
|
|
|
21
22
|
it('normalizes supported link types and falls back to employee', () => {
|
|
22
23
|
expect(normalizeEmployeeLinkType('OWNER')).toBe('owner')
|
|
23
24
|
expect(normalizeEmployeeLinkType('courier')).toBe('courier')
|
|
25
|
+
expect(normalizeEmployeeLinkType('salesman')).toBe('salesman')
|
|
26
|
+
expect(normalizeEmployeeLinkType('after-sales')).toBe('after-sales')
|
|
24
27
|
expect(normalizeEmployeeLinkType('unknown')).toBe('employee')
|
|
25
28
|
})
|
|
26
29
|
|
|
30
|
+
it('exports the same catalog used by create and edit pickers', () => {
|
|
31
|
+
expect(EMPLOYEE_CONTACT_LINK_TYPES).toEqual([
|
|
32
|
+
'employee',
|
|
33
|
+
'owner',
|
|
34
|
+
'director',
|
|
35
|
+
'manager',
|
|
36
|
+
'salesman',
|
|
37
|
+
'after-sales',
|
|
38
|
+
'courier',
|
|
39
|
+
])
|
|
40
|
+
})
|
|
41
|
+
|
|
27
42
|
it('builds numeric relationship filters for collection reads', () => {
|
|
28
43
|
expect(
|
|
29
44
|
buildPeopleLinkReadParams({
|
|
@@ -38,16 +53,16 @@ describe('employeeContacts', () => {
|
|
|
38
53
|
})
|
|
39
54
|
})
|
|
40
55
|
|
|
41
|
-
it('accepts linkTypes allowlist and itemsPerPage for contact reads (#636)', () => {
|
|
56
|
+
it('accepts linkTypes allowlist and itemsPerPage for contact reads (#636/#649)', () => {
|
|
42
57
|
expect(
|
|
43
58
|
buildPeopleLinkReadParams({
|
|
44
59
|
companyId: 3,
|
|
45
|
-
linkTypes:
|
|
60
|
+
linkTypes: EMPLOYEE_CONTACT_LINK_TYPES,
|
|
46
61
|
itemsPerPage: 100,
|
|
47
62
|
}),
|
|
48
63
|
).toEqual({
|
|
49
64
|
company: '3',
|
|
50
|
-
linkType:
|
|
65
|
+
linkType: EMPLOYEE_CONTACT_LINK_TYPES,
|
|
51
66
|
itemsPerPage: 100,
|
|
52
67
|
})
|
|
53
68
|
})
|
|
@@ -84,13 +99,40 @@ describe('employeeContacts', () => {
|
|
|
84
99
|
peopleLink: expect.objectContaining({id: 3229}),
|
|
85
100
|
}),
|
|
86
101
|
expect.objectContaining({
|
|
87
|
-
id:
|
|
102
|
+
id: 7,
|
|
88
103
|
linkType: 'owner',
|
|
89
104
|
name: 'Owner',
|
|
90
105
|
}),
|
|
91
106
|
])
|
|
92
107
|
})
|
|
93
108
|
|
|
109
|
+
it('keeps salesman and after-sales contacts in the company list (#649)', () => {
|
|
110
|
+
expect(
|
|
111
|
+
buildEmployeeContactsFromPeopleLinks(
|
|
112
|
+
{
|
|
113
|
+
member: [
|
|
114
|
+
{
|
|
115
|
+
id: 80,
|
|
116
|
+
linkType: 'salesman',
|
|
117
|
+
company: {id: 3},
|
|
118
|
+
people: {id: 81, name: 'Vendedor', peopleType: 'F'},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 82,
|
|
122
|
+
linkType: 'after-sales',
|
|
123
|
+
company: {id: 3},
|
|
124
|
+
people: {id: 83, name: 'Pos Venda', peopleType: 'F'},
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
{parentPeopleId: '3'},
|
|
129
|
+
),
|
|
130
|
+
).toEqual([
|
|
131
|
+
expect.objectContaining({id: 81, linkType: 'salesman'}),
|
|
132
|
+
expect.objectContaining({id: 83, linkType: 'after-sales'}),
|
|
133
|
+
])
|
|
134
|
+
})
|
|
135
|
+
|
|
94
136
|
it('rejects links returned outside the requested relationship scope', () => {
|
|
95
137
|
const requestedLink = {
|
|
96
138
|
id: 11,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract: Contatos create/edit share the same human company link-type catalog.
|
|
3
|
+
* Refs: app-community#446 #648 #649
|
|
4
|
+
*/
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { dirname, join } from 'node:path';
|
|
10
|
+
|
|
11
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), '../../../../..');
|
|
12
|
+
|
|
13
|
+
const read = relativePath => readFileSync(join(root, relativePath), 'utf8');
|
|
14
|
+
|
|
15
|
+
const EXPECTED = [
|
|
16
|
+
'employee',
|
|
17
|
+
'owner',
|
|
18
|
+
'director',
|
|
19
|
+
'manager',
|
|
20
|
+
'salesman',
|
|
21
|
+
'after-sales',
|
|
22
|
+
'courier',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
test('canonical catalog includes every human company link type', () => {
|
|
26
|
+
const src = read('src/react/utils/humanCompanyLinkTypes.js');
|
|
27
|
+
for (const type of EXPECTED) {
|
|
28
|
+
assert.match(src, new RegExp(`['"]${type}['"]`), `missing ${type}`);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('create and edit helpers reuse the same catalog module', () => {
|
|
33
|
+
const createSrc = read('src/react/components/tabs/employeesTabHelpers.js');
|
|
34
|
+
const editSrc = read('src/react/components/tabs/generalTabHelpers.js');
|
|
35
|
+
const contactsSrc = read('src/react/components/tabs/employeeContacts.js');
|
|
36
|
+
|
|
37
|
+
assert.match(createSrc, /humanCompanyLinkTypes/);
|
|
38
|
+
assert.match(editSrc, /humanCompanyLinkTypes/);
|
|
39
|
+
assert.match(contactsSrc, /humanCompanyLinkTypes/);
|
|
40
|
+
assert.match(createSrc, /LINK_TYPE_OPTIONS/);
|
|
41
|
+
assert.match(editSrc, /LINK_TYPE_OPTIONS/);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('employees page default context covers all human company roles', () => {
|
|
45
|
+
const src = read('src/react/pages/employees.js');
|
|
46
|
+
assert.match(src, /HUMAN_COMPANY_LINK_TYPES/);
|
|
47
|
+
assert.match(src, /DEFAULT_EMPLOYEE_CONTEXT_TYPES/);
|
|
48
|
+
assert.match(src, /salesman/);
|
|
49
|
+
assert.match(src, /after-sales/);
|
|
50
|
+
assert.match(src, /Cadastro de Vendedor/);
|
|
51
|
+
assert.match(src, /Cadastro de Pos-venda/);
|
|
52
|
+
});
|
|
@@ -16,9 +16,9 @@ describe('employeeUnlink', () => {
|
|
|
16
16
|
).toEqual({peopleId: '80', linkId: '201'});
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
it('prefers
|
|
20
|
-
const removePeople = jest.fn()
|
|
21
|
-
const removePeopleLink = jest.fn();
|
|
19
|
+
it('prefers people_link.remove and falls back to people.remove', async () => {
|
|
20
|
+
const removePeople = jest.fn();
|
|
21
|
+
const removePeopleLink = jest.fn().mockResolvedValue(null);
|
|
22
22
|
await expect(
|
|
23
23
|
removeEmployeeFromCompany({
|
|
24
24
|
peopleId: '80',
|
|
@@ -26,18 +26,18 @@ describe('employeeUnlink', () => {
|
|
|
26
26
|
removePeople,
|
|
27
27
|
removePeopleLink,
|
|
28
28
|
}),
|
|
29
|
-
).resolves.toEqual({mode: '
|
|
30
|
-
expect(
|
|
31
|
-
expect(
|
|
29
|
+
).resolves.toEqual({mode: 'link', peopleId: '80', linkId: '201'});
|
|
30
|
+
expect(removePeopleLink).toHaveBeenCalledWith('201');
|
|
31
|
+
expect(removePeople).not.toHaveBeenCalled();
|
|
32
32
|
|
|
33
33
|
await expect(
|
|
34
34
|
removeEmployeeFromCompany({
|
|
35
|
-
peopleId: '',
|
|
36
|
-
linkId: '
|
|
37
|
-
removePeople:
|
|
38
|
-
removePeopleLink:
|
|
35
|
+
peopleId: '80',
|
|
36
|
+
linkId: '',
|
|
37
|
+
removePeople: jest.fn().mockResolvedValue(null),
|
|
38
|
+
removePeopleLink: undefined,
|
|
39
39
|
}),
|
|
40
|
-
).resolves.toMatchObject({mode: '
|
|
40
|
+
).resolves.toMatchObject({mode: 'people', peopleId: '80'});
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
it('filters the local list after unlink', () => {
|
|
@@ -8,7 +8,7 @@ jest.mock('@controleonline/ui-common/src/react/utils/fileUrl', () => ({
|
|
|
8
8
|
}), { virtual: true });
|
|
9
9
|
|
|
10
10
|
jest.mock('@controleonline/ui-common/src/react/utils/entityDisplay', () => ({
|
|
11
|
-
|
|
11
|
+
normalizeText: value => String(value || '').trim(),
|
|
12
12
|
}), { virtual: true });
|
|
13
13
|
|
|
14
14
|
describe('fetchPeopleMediaUrls batch', () => {
|
|
@@ -36,24 +36,8 @@ describe('fetchPeopleMediaUrls batch', () => {
|
|
|
36
36
|
});
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
it('falls back to per-id calls when batch rejects', async () => {
|
|
40
|
-
const getPeopleMedia = jest
|
|
41
|
-
.fn()
|
|
42
|
-
.mockRejectedValueOnce(new Error('batch unsupported'))
|
|
43
|
-
.mockResolvedValueOnce([{ people: { id: 1 }, file: { url: 'https://cdn/1.png' } }])
|
|
44
|
-
.mockResolvedValueOnce([{ people: { id: 2 }, file: { url: 'https://cdn/2.png' } }]);
|
|
45
|
-
|
|
46
|
-
const result = await fetchPeopleMediaUrls({
|
|
47
|
-
peopleActions: { getPeopleMedia },
|
|
48
|
-
mediaType: 'avatar',
|
|
49
|
-
peopleIds: [1, 2],
|
|
50
|
-
});
|
|
51
|
-
|
|
52
39
|
expect(getPeopleMedia).toHaveBeenCalledTimes(3);
|
|
53
|
-
expect(result).toEqual({
|
|
54
|
-
'1': 'https://cdn/1.png',
|
|
55
|
-
'2': 'https://cdn/2.png',
|
|
56
|
-
});
|
|
40
|
+
expect(result).toEqual({ '1': 'https://cdn/1.png' });
|
|
57
41
|
});
|
|
58
42
|
});
|
|
59
43
|
|
|
@@ -70,9 +54,4 @@ describe('extractPeopleMediaUrl from people_links payload', () => {
|
|
|
70
54
|
});
|
|
71
55
|
expect(url).toBe('https://cdn/avatar.png');
|
|
72
56
|
});
|
|
73
|
-
|
|
74
|
-
it('returns empty string when peopleMedia is missing', () => {
|
|
75
|
-
expect(extractPeopleMediaUrl({ id: 10 })).toBe('');
|
|
76
|
-
expect(extractPeopleMediaUrl(null)).toBe('');
|
|
77
|
-
});
|
|
78
57
|
});
|
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
buildEmployeeCreatePayload,
|
|
9
9
|
extractId,
|
|
10
10
|
LINK_TYPE_OPTIONS,
|
|
11
|
+
PEOPLE_TYPE_OPTIONS,
|
|
12
|
+
normalizePeopleType,
|
|
13
|
+
formatPeopleTypeLabel,
|
|
14
|
+
formatEmployeeContactMeta,
|
|
11
15
|
buildEmployeeDetailNavParams,
|
|
12
16
|
} from '../../../../react/components/tabs/employeesTabHelpers';
|
|
13
17
|
|
|
@@ -37,9 +41,64 @@ describe('employeesTabHelpers', () => {
|
|
|
37
41
|
expect(payload.peopleType).toBe('F');
|
|
38
42
|
});
|
|
39
43
|
|
|
44
|
+
it('buildEmployeeCreatePayload accepts salesman and after-sales (#649)', () => {
|
|
45
|
+
expect(
|
|
46
|
+
buildEmployeeCreatePayload({
|
|
47
|
+
name: 'VEN',
|
|
48
|
+
alias: 'V',
|
|
49
|
+
linkType: 'salesman',
|
|
50
|
+
parentPeopleId: '15',
|
|
51
|
+
}).linkType,
|
|
52
|
+
).toBe('salesman');
|
|
53
|
+
expect(
|
|
54
|
+
buildEmployeeCreatePayload({
|
|
55
|
+
name: 'POS',
|
|
56
|
+
alias: 'P',
|
|
57
|
+
linkType: 'after-sales',
|
|
58
|
+
parentPeopleId: '15',
|
|
59
|
+
}).linkType,
|
|
60
|
+
).toBe('after-sales');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('buildEmployeeCreatePayload accepts peopleType J (PJ)', () => {
|
|
64
|
+
const payload = buildEmployeeCreatePayload({
|
|
65
|
+
name: 'EMPRESA X',
|
|
66
|
+
alias: 'X',
|
|
67
|
+
linkType: 'employee',
|
|
68
|
+
parentPeopleId: '15',
|
|
69
|
+
peopleType: 'J',
|
|
70
|
+
});
|
|
71
|
+
expect(payload.peopleType).toBe('J');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('normalizePeopleType only allows F|J', () => {
|
|
75
|
+
expect(normalizePeopleType('F')).toBe('F');
|
|
76
|
+
expect(normalizePeopleType('j')).toBe('J');
|
|
77
|
+
expect(normalizePeopleType('pj')).toBe('J');
|
|
78
|
+
expect(normalizePeopleType('contrato')).toBe('F');
|
|
79
|
+
expect(normalizePeopleType('')).toBe('F');
|
|
80
|
+
expect(PEOPLE_TYPE_OPTIONS.map(o => o.value)).toEqual(['F', 'J']);
|
|
81
|
+
expect(formatPeopleTypeLabel('J')).toBe('Pessoa Jurídica');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('formatEmployeeContactMeta includes peopleType', () => {
|
|
85
|
+
expect(
|
|
86
|
+
formatEmployeeContactMeta({ id: 9, peopleType: 'J', linkType: 'employee' }),
|
|
87
|
+
).toContain('Pessoa Jurídica');
|
|
88
|
+
});
|
|
89
|
+
|
|
40
90
|
it('extractId and LINK_TYPE_OPTIONS', () => {
|
|
41
91
|
expect(extractId('/people/3')).toBe('3');
|
|
42
92
|
expect(LINK_TYPE_OPTIONS.some(o => o.value === 'employee')).toBe(true);
|
|
93
|
+
expect(LINK_TYPE_OPTIONS.map(o => o.value)).toEqual([
|
|
94
|
+
'employee',
|
|
95
|
+
'owner',
|
|
96
|
+
'director',
|
|
97
|
+
'manager',
|
|
98
|
+
'salesman',
|
|
99
|
+
'after-sales',
|
|
100
|
+
'courier',
|
|
101
|
+
]);
|
|
43
102
|
});
|
|
44
103
|
|
|
45
104
|
it('buildEmployeeDetailNavParams opens employee detail on general tab', () => {
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import {
|
|
5
5
|
normalizeEnable,
|
|
6
|
+
normalizeIdentityValue,
|
|
6
7
|
normalizeLinkType,
|
|
8
|
+
resolveSeededLinkType,
|
|
9
|
+
normalizePeopleType,
|
|
10
|
+
PEOPLE_TYPE_OPTIONS,
|
|
7
11
|
formatYmdToBr,
|
|
8
12
|
parseBrDateToYmd,
|
|
9
13
|
formatDateInput,
|
|
@@ -13,6 +17,16 @@ import {
|
|
|
13
17
|
} from '../../../components/tabs/generalTabHelpers';
|
|
14
18
|
|
|
15
19
|
describe('generalTabHelpers', () => {
|
|
20
|
+
describe('normalizeIdentityValue', () => {
|
|
21
|
+
it('preserves mixed case typed by the user', () => {
|
|
22
|
+
expect(normalizeIdentityValue('Cláudia Silva')).toBe('Cláudia Silva');
|
|
23
|
+
expect(normalizeIdentityValue(' ACME Ltda ')).toBe('ACME Ltda');
|
|
24
|
+
expect(normalizeIdentityValue('kibelicia comida árabe')).toBe('kibelicia comida árabe');
|
|
25
|
+
});
|
|
26
|
+
it('collapses internal whitespace without changing case', () => {
|
|
27
|
+
expect(normalizeIdentityValue('Ana Maria')).toBe('Ana Maria');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
16
30
|
describe('normalizeEnable', () => {
|
|
17
31
|
it('accepts boolean true/false', () => {
|
|
18
32
|
expect(normalizeEnable(true)).toBe(true);
|
|
@@ -49,6 +63,8 @@ describe('generalTabHelpers', () => {
|
|
|
49
63
|
it('is case-insensitive', () => {
|
|
50
64
|
expect(normalizeLinkType('OWNER')).toBe('owner');
|
|
51
65
|
expect(normalizeLinkType(' Manager ')).toBe('manager');
|
|
66
|
+
expect(normalizeLinkType('SALESMAN')).toBe('salesman');
|
|
67
|
+
expect(normalizeLinkType(' After-Sales ')).toBe('after-sales');
|
|
52
68
|
});
|
|
53
69
|
});
|
|
54
70
|
|
|
@@ -127,4 +143,23 @@ describe('generalTabHelpers', () => {
|
|
|
127
143
|
).toBe(false);
|
|
128
144
|
});
|
|
129
145
|
});
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
describe('resolveSeededLinkType', () => {
|
|
149
|
+
it('prefers client.linkType over route seed after save', () => {
|
|
150
|
+
expect(resolveSeededLinkType('courier', 'owner')).toBe('courier');
|
|
151
|
+
expect(resolveSeededLinkType('', 'owner')).toBe('owner');
|
|
152
|
+
expect(resolveSeededLinkType(undefined, 'manager')).toBe('manager');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('normalizePeopleType', () => {
|
|
157
|
+
it('maps only F and J from API enum', () => {
|
|
158
|
+
expect(normalizePeopleType('F')).toBe('F');
|
|
159
|
+
expect(normalizePeopleType('J')).toBe('J');
|
|
160
|
+
expect(normalizePeopleType('juridica')).toBe('J');
|
|
161
|
+
expect(normalizePeopleType('contrato')).toBe('F');
|
|
162
|
+
expect(PEOPLE_TYPE_OPTIONS.map(o => o.value)).toEqual(['F', 'J']);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
130
165
|
});
|