@controleonline/ui-customers 1.0.81

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 (102) hide show
  1. package/.github/agents/developer.agent.md +30 -0
  2. package/.github/agents/devops.agent.md +30 -0
  3. package/.github/agents/qa.agent.md +30 -0
  4. package/.github/agents/security.agent.md +30 -0
  5. package/.scrutinizer.yml +20 -0
  6. package/AGENTS.md +50 -0
  7. package/FUNDING.yml +1 -0
  8. package/README.md +23 -0
  9. package/docs/technical/Client-Details-Franquia-Filial.md +18 -0
  10. package/docs/technical/Client-Details-People-Name-Case.md +103 -0
  11. package/docs/technical/Cliente-Vendedor-Vinculo-e-Permissoes.md +152 -0
  12. package/docs/wiki.md +1 -0
  13. package/package.json +32 -0
  14. package/scripts/validate-wiki-pointer.cjs +13 -0
  15. package/src/react/components/tabs/AddressesTab.js +494 -0
  16. package/src/react/components/tabs/CategoriesTab.js +196 -0
  17. package/src/react/components/tabs/ClassificationChips.js +494 -0
  18. package/src/react/components/tabs/ContactTab.js +621 -0
  19. package/src/react/components/tabs/ContactTab.styles.js +119 -0
  20. package/src/react/components/tabs/ContractsTab.js +13 -0
  21. package/src/react/components/tabs/DocumentsTab.js +463 -0
  22. package/src/react/components/tabs/DocumentsTab.styles.js +133 -0
  23. package/src/react/components/tabs/EmployeeContactRow.js +83 -0
  24. package/src/react/components/tabs/EmployeesTab.js +454 -0
  25. package/src/react/components/tabs/EmployeesTab.styles.js +172 -0
  26. package/src/react/components/tabs/FiscalTab.js +6 -0
  27. package/src/react/components/tabs/FranchiseCommissionSection.js +307 -0
  28. package/src/react/components/tabs/FranchiseLinksManageModal.js +215 -0
  29. package/src/react/components/tabs/FranchiseLinksTab.js +282 -0
  30. package/src/react/components/tabs/GeneralTab.js +466 -0
  31. package/src/react/components/tabs/GeneralTab.styles.js +101 -0
  32. package/src/react/components/tabs/MediaTab.js +356 -0
  33. package/src/react/components/tabs/PeopleCategoriesPanel.js +601 -0
  34. package/src/react/components/tabs/ProductsTab.js +236 -0
  35. package/src/react/components/tabs/ProductsTab.styles.js +143 -0
  36. package/src/react/components/tabs/SalesmanCommissionBlock.js +120 -0
  37. package/src/react/components/tabs/SalesmanManageModal.js +208 -0
  38. package/src/react/components/tabs/SalesmanTab.js +436 -0
  39. package/src/react/components/tabs/SalesmanTab.styles.js +4 -0
  40. package/src/react/components/tabs/UserApiKeyModal.js +90 -0
  41. package/src/react/components/tabs/UserFormModal.js +301 -0
  42. package/src/react/components/tabs/UsersTab.js +426 -0
  43. package/src/react/components/tabs/UsersTab.styles.js +283 -0
  44. package/src/react/components/tabs/UsersTab.styles.test.js +42 -0
  45. package/src/react/components/tabs/contactMutationMessage.js +18 -0
  46. package/src/react/components/tabs/employeeContacts.js +214 -0
  47. package/src/react/components/tabs/employeeUnlink.js +68 -0
  48. package/src/react/components/tabs/employeesTabHelpers.js +227 -0
  49. package/src/react/components/tabs/franchiseCommissionHelpers.js +134 -0
  50. package/src/react/components/tabs/franchiseLinksTab.helpers.js +247 -0
  51. package/src/react/components/tabs/generalTabHelpers.js +120 -0
  52. package/src/react/components/tabs/salesmanTab.helpers.js +114 -0
  53. package/src/react/components/tabs/salesmanTabHelpers.js +173 -0
  54. package/src/react/components/tabs/salesmanTabMedia.js +55 -0
  55. package/src/react/components/tabs/salesmanTabSession.js +39 -0
  56. package/src/react/components/tabs/useEmployeeUnlink.js +58 -0
  57. package/src/react/components/tabs/useFranchiseLinksManage.js +215 -0
  58. package/src/react/components/tabs/useSalesmanManage.js +222 -0
  59. package/src/react/components/tabs/usersTabHelpers.js +221 -0
  60. package/src/react/domain/productSupplierFlow.js +37 -0
  61. package/src/react/index.js +4 -0
  62. package/src/react/pages/ClientDetailsSkeleton.js +84 -0
  63. package/src/react/pages/clientDetailsHelpers.js +17 -0
  64. package/src/react/pages/clients.js +27 -0
  65. package/src/react/pages/details.js +529 -0
  66. package/src/react/pages/details.page.styles.js +105 -0
  67. package/src/react/pages/details.styles.js +15 -0
  68. package/src/react/pages/employees.js +69 -0
  69. package/src/react/pages/franchisees.js +30 -0
  70. package/src/react/pages/prospects.js +21 -0
  71. package/src/react/pages/provider.js +21 -0
  72. package/src/react/router/routes.js +104 -0
  73. package/src/react/styles/details.js +571 -0
  74. package/src/store/people_categories/index.js +72 -0
  75. package/src/tests/browser/manager/client-details-create-user.spec.js +321 -0
  76. package/src/tests/browser/manager/client-details-employee-unlink.spec.js +215 -0
  77. package/src/tests/browser/manager/client-details-employees-refresh.spec.js +271 -0
  78. package/src/tests/browser/manager/client-details-enable.spec.js +241 -0
  79. package/src/tests/browser/manager/client-details-fiscal-tabs.spec.js +410 -0
  80. package/src/tests/browser/manager/client-details-franchise-commission.spec.js +318 -0
  81. package/src/tests/browser/manager/client-details-franchise-links.spec.js +365 -0
  82. package/src/tests/browser/manager/client-details-media.spec.js +405 -0
  83. package/src/tests/browser/manager/client-details-relationships.spec.js +313 -0
  84. package/src/tests/browser/manager/client-details-salesman-commission.spec.js +291 -0
  85. package/src/tests/browser/manager/client-details-salesman-manage.spec.js +448 -0
  86. package/src/tests/react/components/tabs/contactMutationMessage.test.js +25 -0
  87. package/src/tests/react/components/tabs/employeeContacts.test.js +266 -0
  88. package/src/tests/react/components/tabs/employeeUnlink.test.js +67 -0
  89. package/src/tests/react/components/tabs/employeesTabHelpers.media.test.js +78 -0
  90. package/src/tests/react/components/tabs/employeesTabHelpers.test.js +64 -0
  91. package/src/tests/react/components/tabs/fiscalTabContract.test.mjs +29 -0
  92. package/src/tests/react/components/tabs/franchiseCommissionHelpers.test.js +128 -0
  93. package/src/tests/react/components/tabs/franchiseLinksTab.helpers.test.mjs +166 -0
  94. package/src/tests/react/components/tabs/generalTabHelpers.test.js +130 -0
  95. package/src/tests/react/components/tabs/peopleMediaTabs.test.js +322 -0
  96. package/src/tests/react/components/tabs/salesmanTab.helpers.test.mjs +75 -0
  97. package/src/tests/react/components/tabs/salesmanTabHelpers.test.js +142 -0
  98. package/src/tests/react/components/tabs/salesmanTabHelpers.test.mjs +92 -0
  99. package/src/tests/react/components/tabs/usersTabHelpers.test.js +59 -0
  100. package/src/tests/react/domain/productSupplierFlow.test.mjs +55 -0
  101. package/src/tests/react/pages/clientDetailsHelpers.test.js +98 -0
  102. package/src/tests/react/pages/employees.test.js +44 -0
@@ -0,0 +1,321 @@
1
+ const {expect, test} = require('playwright/test');
2
+ const {API_ORIGIN} = require('../../../../../../../src/tests/browser/apiOrigin');
3
+ const {version: appVersion} = require('../../../../../../../package.json');
4
+
5
+ const CORS_HEADERS = {
6
+ 'access-control-allow-origin': '*',
7
+ 'access-control-allow-headers':
8
+ 'API-TOKEN, APP-DOMAIN, DEVICE, ACCEPT, CONTENT-TYPE, X-Requested-With',
9
+ 'access-control-allow-methods': 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
10
+ };
11
+
12
+ const collection = (member = [], summary = {}) => ({
13
+ member,
14
+ 'hydra:member': member,
15
+ totalItems: member.length,
16
+ 'hydra:totalItems': member.length,
17
+ summary,
18
+ });
19
+
20
+ const jsonHeaders = () => ({
21
+ ...CORS_HEADERS,
22
+ 'content-type': 'application/ld+json; charset=utf-8',
23
+ });
24
+
25
+ const textHeaders = () => ({
26
+ ...CORS_HEADERS,
27
+ 'content-type': 'text/css; charset=utf-8',
28
+ });
29
+
30
+ const createCompany = () => ({
31
+ id: 3,
32
+ name: 'Controle Online',
33
+ alias: 'CONTROLE ONLINE',
34
+ panel_enabled: true,
35
+ enabled: true,
36
+ commercial_enabled: true,
37
+ theme: {
38
+ colors: {
39
+ primary: '#0EA5E9',
40
+ secondary: '#F97316',
41
+ },
42
+ },
43
+ configs: {},
44
+ });
45
+
46
+ /**
47
+ * Browser smoke for ControleOnline/app-community#369:
48
+ * create user on Client Details with contextKey=employee without
49
+ * HTTP 500 "Authentication required".
50
+ */
51
+ const mockClientDetailsCreateUserApi = async page => {
52
+ const company = createCompany();
53
+ const createdUsers = [];
54
+ const postUsersRequests = [];
55
+
56
+ const employee = {
57
+ '@id': '/people/106218',
58
+ id: 106218,
59
+ name: 'Funcionario Teste',
60
+ alias: 'FUNC TESTE',
61
+ peopleType: 'F',
62
+ enabled: true,
63
+ user: [],
64
+ };
65
+
66
+ await page.route(`${API_ORIGIN}/**`, async route => {
67
+ const request = route.request();
68
+ const url = new URL(request.url());
69
+ const pathname = url.pathname.replace(/^\/+/, '');
70
+ const method = request.method().toUpperCase();
71
+
72
+ if (method === 'OPTIONS') {
73
+ return route.fulfill({
74
+ status: 204,
75
+ headers: CORS_HEADERS,
76
+ body: '',
77
+ });
78
+ }
79
+
80
+ if (pathname === 'themes-colors.css') {
81
+ return route.fulfill({
82
+ status: 200,
83
+ headers: textHeaders(),
84
+ body: ':root { --primary: #0ea5e9; --secondary: #f97316; }',
85
+ });
86
+ }
87
+
88
+ if (pathname === 'runtime/ip') {
89
+ return route.fulfill({
90
+ status: 200,
91
+ headers: jsonHeaders(),
92
+ body: JSON.stringify({ip: '127.0.0.1'}),
93
+ });
94
+ }
95
+
96
+ if (pathname === 'people/company/default') {
97
+ return route.fulfill({
98
+ status: 200,
99
+ headers: jsonHeaders(),
100
+ body: JSON.stringify(company),
101
+ });
102
+ }
103
+
104
+ if (pathname === 'people/companies/my') {
105
+ return route.fulfill({
106
+ status: 200,
107
+ headers: jsonHeaders(),
108
+ body: JSON.stringify(collection([company])),
109
+ });
110
+ }
111
+
112
+ if (pathname === 'people/106218') {
113
+ return route.fulfill({
114
+ status: 200,
115
+ headers: jsonHeaders(),
116
+ body: JSON.stringify({
117
+ ...employee,
118
+ user: createdUsers.map(u => ({
119
+ '@id': `/users/${u.id}`,
120
+ id: u.id,
121
+ username: u.username,
122
+ apiKey: u.apiKey || '',
123
+ })),
124
+ }),
125
+ });
126
+ }
127
+
128
+ // POST /users — must receive API-TOKEN (auth) and not return 500
129
+ if (pathname === 'users' && method === 'POST') {
130
+ const headers = request.headers();
131
+ const apiToken =
132
+ headers['api-token'] || headers['API-TOKEN'] || headers['api_token'] || '';
133
+ let body = {};
134
+ try {
135
+ body = request.postDataJSON() || {};
136
+ } catch {
137
+ body = {};
138
+ }
139
+
140
+ postUsersRequests.push({
141
+ apiToken,
142
+ body,
143
+ method,
144
+ pathname,
145
+ });
146
+
147
+ // Simulate the fixed backend: ROLE_HUMAN + valid token → 201
148
+ if (!apiToken) {
149
+ return route.fulfill({
150
+ status: 401,
151
+ headers: jsonHeaders(),
152
+ body: JSON.stringify({message: 'Authentication required'}),
153
+ });
154
+ }
155
+
156
+ const newUser = {
157
+ '@id': `/users/${9000 + createdUsers.length}`,
158
+ id: 9000 + createdUsers.length,
159
+ username: body.username || 'new-user',
160
+ apiKey: 'ak-test-' + Date.now(),
161
+ people: body.people || '/people/106218',
162
+ };
163
+ createdUsers.push(newUser);
164
+
165
+ return route.fulfill({
166
+ status: 201,
167
+ headers: jsonHeaders(),
168
+ body: JSON.stringify(newUser),
169
+ });
170
+ }
171
+
172
+ if (pathname === 'users' || pathname.startsWith('users/')) {
173
+ return route.fulfill({
174
+ status: 200,
175
+ headers: jsonHeaders(),
176
+ body: JSON.stringify(collection(createdUsers)),
177
+ });
178
+ }
179
+
180
+ if (pathname === 'people_links') {
181
+ return route.fulfill({
182
+ status: 200,
183
+ headers: jsonHeaders(),
184
+ body: JSON.stringify(collection([])),
185
+ });
186
+ }
187
+
188
+ if (pathname.startsWith('statuses') || pathname.startsWith('categories')) {
189
+ return route.fulfill({
190
+ status: 200,
191
+ headers: jsonHeaders(),
192
+ body: JSON.stringify(collection([])),
193
+ });
194
+ }
195
+
196
+ // Default empty collection for other GETs
197
+ return route.fulfill({
198
+ status: 200,
199
+ headers: jsonHeaders(),
200
+ body: JSON.stringify(collection([])),
201
+ });
202
+ });
203
+
204
+ await page.addInitScript(
205
+ ({appVersion}) => {
206
+ const setLocalStorageItem = (key, value) => {
207
+ try {
208
+ localStorage.setItem(key, value);
209
+ } catch {
210
+ // ignore
211
+ }
212
+ };
213
+
214
+ setLocalStorageItem(
215
+ 'session',
216
+ JSON.stringify({
217
+ id: 7,
218
+ people: '/people/7',
219
+ api_key: 'test-api-key',
220
+ token: 'test-api-key',
221
+ active: 1,
222
+ mycompany: 3,
223
+ roles: ['ROLE_SUPER', 'ROLE_HUMAN'],
224
+ }),
225
+ );
226
+ setLocalStorageItem('config', JSON.stringify({language: 'pt-br'}));
227
+ setLocalStorageItem('app-type', 'MANAGER');
228
+ setLocalStorageItem(
229
+ 'device',
230
+ JSON.stringify({
231
+ id: 'web-manager',
232
+ device: 'web-manager',
233
+ type: 'WEB',
234
+ appName: 'Browser Manager',
235
+ appVersion,
236
+ buildNumber: appVersion,
237
+ systemName: 'web',
238
+ systemVersion: 'web',
239
+ deviceType: 'web',
240
+ metadata: {},
241
+ }),
242
+ );
243
+ },
244
+ {appVersion},
245
+ );
246
+
247
+ return {
248
+ getPostUsersRequests: () => postUsersRequests,
249
+ getCreatedUsers: () => createdUsers,
250
+ };
251
+ };
252
+
253
+ test.describe('client details create user browser smoke (#369)', () => {
254
+ test('creates user on contextKey=employee without Authentication required 500', async ({
255
+ page,
256
+ }) => {
257
+ const api = await mockClientDetailsCreateUserApi(page);
258
+
259
+ await page.goto(
260
+ '/client-details?clientId=106218&contextKey=employee&initialTab=users',
261
+ );
262
+
263
+ // Page / tab shell should load
264
+ await expect(
265
+ page.getByText(/Dados Cadastrais|Usuários|Users|Geral/i).first(),
266
+ ).toBeVisible({timeout: 15000});
267
+
268
+ // Open create-user modal (Users tab with isEditing=true always in details)
269
+ const addUserTrigger = page
270
+ .getByText(/Adicionar Usuário|Adicionar|Add user|Novo usuário/i)
271
+ .first();
272
+ await expect(addUserTrigger).toBeVisible({timeout: 15000});
273
+ await addUserTrigger.click();
274
+
275
+ await expect(page.getByText(/Adicionar Usuário/i)).toBeVisible({
276
+ timeout: 10000,
277
+ });
278
+
279
+ // Fill form — username + password + confirm
280
+ const usernameInput = page.locator('input').first();
281
+ await usernameInput.fill('employee.user.369');
282
+
283
+ const passwordInputs = page.locator('input[type="password"], input');
284
+ // Prefer password-type fields; fall back to remaining inputs
285
+ const count = await passwordInputs.count();
286
+ if (count >= 3) {
287
+ await passwordInputs.nth(1).fill('Secret123!');
288
+ await passwordInputs.nth(2).fill('Secret123!');
289
+ } else {
290
+ // Modal fields: username already filled; next two are password/confirm
291
+ const allInputs = page.locator('input');
292
+ const inputCount = await allInputs.count();
293
+ if (inputCount >= 3) {
294
+ await allInputs.nth(1).fill('Secret123!');
295
+ await allInputs.nth(2).fill('Secret123!');
296
+ }
297
+ }
298
+
299
+ // Save
300
+ await page.getByText(/^Salvar$/i).click();
301
+
302
+ // Success path: no 500 Authentication required; user appears or success toast
303
+ await expect(
304
+ page.getByText(/Usuário criado com sucesso|employee\.user\.369|sucesso/i),
305
+ ).toBeVisible({timeout: 15000});
306
+
307
+ const posts = api.getPostUsersRequests();
308
+ expect(posts.length).toBeGreaterThanOrEqual(1);
309
+
310
+ const lastPost = posts[posts.length - 1];
311
+ // Auth header must be present (session api_key injected by ui-common)
312
+ expect(String(lastPost.apiToken || '').length).toBeGreaterThan(0);
313
+ // people IRI must be sent
314
+ expect(String(lastPost.body?.people || '')).toMatch(/people\/106218/);
315
+ expect(lastPost.body?.username).toBe('employee.user.369');
316
+
317
+ // Created user should be in mock store / list
318
+ const created = api.getCreatedUsers();
319
+ expect(created.some(u => u.username === 'employee.user.369')).toBeTruthy();
320
+ });
321
+ });
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Browser smoke — client-details Contatos: remover colaborador.
3
+ * fluxo: outros
4
+ * flowchartIds: [1]
5
+ * app-community#624
6
+ *
7
+ * Steps:
8
+ * 1. Open Client Details contacts tab
9
+ * 2. Active employee is listed; disabled link is hidden
10
+ * 3. Trash → confirmation dialog
11
+ * 4. Confirm → DELETE /people/{id} → row disappears without F5
12
+ */
13
+ const {expect, test} = require('playwright/test');
14
+ const {API_ORIGIN} = require('../../../../../../../src/tests/browser/apiOrigin');
15
+ const {version: appVersion} = require('../../../../../../../package.json');
16
+
17
+ const CORS_HEADERS = {
18
+ 'access-control-allow-origin': '*',
19
+ 'access-control-allow-headers':
20
+ 'API-TOKEN, APP-DOMAIN, DEVICE, ACCEPT, CONTENT-TYPE, X-Requested-With',
21
+ 'access-control-allow-methods': 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
22
+ };
23
+
24
+ const jsonHeaders = () => ({
25
+ ...CORS_HEADERS,
26
+ 'content-type': 'application/ld+json; charset=utf-8',
27
+ });
28
+
29
+ const collection = (member = []) => ({
30
+ member,
31
+ 'hydra:member': member,
32
+ totalItems: member.length,
33
+ 'hydra:totalItems': member.length,
34
+ });
35
+
36
+ const company = {
37
+ '@id': '/people/2',
38
+ id: 2,
39
+ name: 'CONTROLE ONLINE',
40
+ alias: 'CONTROLE ONLINE',
41
+ panel_enabled: true,
42
+ enabled: true,
43
+ commercial_enabled: true,
44
+ theme: {colors: {primary: '#0EA5E9', secondary: '#F97316'}},
45
+ configs: {},
46
+ };
47
+
48
+ const client = {
49
+ '@id': '/people/31487',
50
+ id: 31487,
51
+ name: 'CLIENTE TESTE LTDA',
52
+ alias: 'CLIENTE TESTE',
53
+ peopleType: 'J',
54
+ enable: true,
55
+ };
56
+
57
+ const employeeActive = {
58
+ '@id': '/people/90010',
59
+ id: 90010,
60
+ name: 'COLABORADOR ATIVO',
61
+ alias: 'ATIVO',
62
+ peopleType: 'F',
63
+ enable: true,
64
+ };
65
+
66
+ const employeeDisabled = {
67
+ '@id': '/people/90011',
68
+ id: 90011,
69
+ name: 'COLABORADOR INATIVO',
70
+ alias: 'INATIVO',
71
+ peopleType: 'F',
72
+ enable: true,
73
+ };
74
+
75
+ const mockUnlinkApi = async page => {
76
+ const deletedPeople = new Set();
77
+
78
+ await page.route(`${API_ORIGIN}/**`, async route => {
79
+ const request = route.request();
80
+ const url = new URL(request.url());
81
+ const pathname = url.pathname.replace(/^\/+/, '');
82
+ const method = request.method().toUpperCase();
83
+
84
+ if (method === 'OPTIONS') {
85
+ return route.fulfill({status: 204, headers: CORS_HEADERS, body: ''});
86
+ }
87
+
88
+ if (pathname === 'people/company/default') {
89
+ return route.fulfill({
90
+ status: 200,
91
+ headers: jsonHeaders(),
92
+ body: JSON.stringify(company),
93
+ });
94
+ }
95
+
96
+ if (pathname === 'people/31487' && method === 'GET') {
97
+ return route.fulfill({
98
+ status: 200,
99
+ headers: jsonHeaders(),
100
+ body: JSON.stringify(client),
101
+ });
102
+ }
103
+
104
+ if (pathname === 'people/90010' && method === 'DELETE') {
105
+ deletedPeople.add(90010);
106
+ return route.fulfill({status: 204, headers: CORS_HEADERS, body: ''});
107
+ }
108
+
109
+ if (
110
+ (pathname === 'people_links' || pathname === 'people-links') &&
111
+ method === 'GET'
112
+ ) {
113
+ const links = [
114
+ {
115
+ '@id': '/people_links/501',
116
+ id: 501,
117
+ enable: 1,
118
+ linkType: 'employee',
119
+ people: employeeActive,
120
+ company: client,
121
+ },
122
+ {
123
+ '@id': '/people_links/502',
124
+ id: 502,
125
+ enable: 0,
126
+ linkType: 'employee',
127
+ people: employeeDisabled,
128
+ company: client,
129
+ },
130
+ ].filter(link => !deletedPeople.has(Number(link.people.id)));
131
+
132
+ return route.fulfill({
133
+ status: 200,
134
+ headers: jsonHeaders(),
135
+ body: JSON.stringify(collection(links)),
136
+ });
137
+ }
138
+
139
+ if (method === 'GET') {
140
+ return route.fulfill({
141
+ status: 200,
142
+ headers: jsonHeaders(),
143
+ body: JSON.stringify(collection([])),
144
+ });
145
+ }
146
+
147
+ return route.fulfill({
148
+ status: 200,
149
+ headers: jsonHeaders(),
150
+ body: JSON.stringify({}),
151
+ });
152
+ });
153
+
154
+ await page.addInitScript(
155
+ ({version}) => {
156
+ localStorage.setItem(
157
+ 'session',
158
+ JSON.stringify({
159
+ id: 19,
160
+ people: '/people/19',
161
+ api_key: 'test-api-key',
162
+ active: 1,
163
+ mycompany: 2,
164
+ roles: ['ROLE_SUPER'],
165
+ }),
166
+ );
167
+ localStorage.setItem('app-type', 'MANAGER');
168
+ localStorage.setItem('config', JSON.stringify({language: 'pt-br'}));
169
+ localStorage.setItem(
170
+ 'device',
171
+ JSON.stringify({
172
+ id: 'web-manager',
173
+ device: 'web-manager',
174
+ type: 'WEB',
175
+ appName: 'Browser Manager',
176
+ appVersion: version,
177
+ buildNumber: version,
178
+ systemName: 'web',
179
+ systemVersion: 'web',
180
+ deviceType: 'web',
181
+ metadata: {},
182
+ }),
183
+ );
184
+ },
185
+ {version: appVersion},
186
+ );
187
+ };
188
+
189
+ test.describe('client-details employee unlink (#624)', () => {
190
+ test('hides inactive links and removes employee after confirmation', async ({
191
+ page,
192
+ }) => {
193
+ await mockUnlinkApi(page);
194
+ await page.goto(
195
+ '/client-details?clientId=31487&contextKey=client&initialTab=contacts',
196
+ );
197
+
198
+ await expect(page.getByText(/COLABORADOR ATIVO/i).first()).toBeVisible({
199
+ timeout: 15000,
200
+ });
201
+ await expect(page.getByText(/COLABORADOR INATIVO/i)).toHaveCount(0);
202
+
203
+ await expect(page.getByTestId('employee-unlink-501')).toBeVisible();
204
+ await page.getByTestId('employee-unlink-501').click();
205
+ await expect(
206
+ page.getByText(/marcado como removido e o vínculo/i),
207
+ ).toBeVisible({timeout: 10000});
208
+ await page.getByText('Remover', {exact: true}).last().click();
209
+
210
+ await expect(page.getByTestId('employee-unlink-501')).toHaveCount(0, {
211
+ timeout: 10000,
212
+ });
213
+ await expect(page.getByText(/COLABORADOR ATIVO/i)).toHaveCount(0);
214
+ });
215
+ });