@controleonline/ui-common 1.2.89 → 1.2.91

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 (24) hide show
  1. package/docs/compact-order-history-1047.md +25 -0
  2. package/package.json +1 -1
  3. package/src/react/components/CopyDeviceConfigModal.js +7 -4
  4. package/src/react/components/DefaultProvider.native.configuration.js +275 -0
  5. package/src/react/components/DefaultProvider.native.js +24 -534
  6. package/src/react/components/DefaultProvider.native.lifecycle.js +323 -0
  7. package/src/react/components/DefaultProvider.web.configuration.js +327 -0
  8. package/src/react/components/DefaultProvider.web.js +23 -603
  9. package/src/react/components/DefaultProvider.web.lifecycle.js +336 -0
  10. package/src/react/components/resolveDomainThemeColors.js +5 -0
  11. package/src/react/pages/Connections.js +5 -7
  12. package/src/react/pages/MercadoLivreIntegrationPage.js +5 -75
  13. package/src/react/pages/mercadoLivreIntegrationHelpers.js +79 -0
  14. package/src/tests/browser/manager/bottom-navigation-role-toolbar.spec.js +14 -0
  15. package/src/tests/browser/manager/connections.spec.js +18 -0
  16. package/src/tests/browser/manager/device-detail-alias-save.spec.js +3 -1
  17. package/src/tests/browser/manager/device-detail-copy-config.spec.js +6 -0
  18. package/src/tests/browser/manager/device-detail-pdv-current-company.spec.js +3 -1
  19. package/src/tests/browser/manager/devices-current.spec.js +345 -343
  20. package/src/tests/browser/manager/devices-type-filters.spec.js +2 -0
  21. package/src/tests/browser/manager/managerSession.fixture.js +28 -0
  22. package/src/tests/react/components/DefaultProviderAuthority.test.js +4 -1
  23. package/src/tests/react/components/providerEffects.fingerprint.json +47 -0
  24. package/src/tests/react/components/providerEffects.test.js +24 -0
@@ -0,0 +1,336 @@
1
+ import {useEffect} from 'react';
2
+
3
+ // Extracted bootstrap effects retain their dependency arrays and invocation order.
4
+
5
+ export function useWebTranslationRefresh({setTranslateVersion}) {
6
+ useEffect(() => {
7
+ global.refreshTranslationsUI = () => {
8
+ setTranslateVersion(version => version + 1);
9
+ };
10
+
11
+ return () => {
12
+ if (global.refreshTranslationsUI) {
13
+ delete global.refreshTranslationsUI;
14
+ }
15
+ };
16
+ }, []);
17
+ }
18
+
19
+ export function useWebTranslationReset({isLogged, isPublicRouteActive, setTranslateReady, setActiveTranslateBootstrapKey, translateBootstrapKeyRef, translateActions}) {
20
+ useEffect(() => {
21
+ if (isLogged && !isPublicRouteActive) {
22
+ return;
23
+ }
24
+
25
+ setTranslateReady(true);
26
+ setActiveTranslateBootstrapKey('');
27
+ translateBootstrapKeyRef.current = '';
28
+ translateActions.setMessages({});
29
+ translateActions.setPendingMessages?.({});
30
+
31
+ if (global.t) {
32
+ delete global.t;
33
+ }
34
+ }, [isLogged, isPublicRouteActive, translateActions]);
35
+ }
36
+
37
+ export function useWebDeviceConfigReset({deviceConfigPeopleIri, lastDeviceConfigPeopleIriRef, setDeviceConfigFetched, setDeviceRuntimeConfigSynced, deviceConfigsActions}) {
38
+ useEffect(() => {
39
+ if (
40
+ !deviceConfigPeopleIri ||
41
+ lastDeviceConfigPeopleIriRef.current === deviceConfigPeopleIri
42
+ ) {
43
+ return;
44
+ }
45
+
46
+ lastDeviceConfigPeopleIriRef.current = deviceConfigPeopleIri;
47
+ setDeviceConfigFetched(false);
48
+ setDeviceRuntimeConfigSynced(false);
49
+ deviceConfigsActions.setItem({});
50
+ }, [deviceConfigPeopleIri, deviceConfigsActions]);
51
+ }
52
+
53
+ export function useWebRuntimeIp({isLogged, api, getRuntimeIpFromResponse, setWebRuntimeIp, user}) {
54
+ useEffect(() => {
55
+ if (!isLogged) {
56
+ return;
57
+ }
58
+
59
+ let cancelled = false;
60
+
61
+ api
62
+ .fetch('runtime/ip', {
63
+ method: 'GET',
64
+ })
65
+ .then(response => {
66
+ const nextRuntimeIp = getRuntimeIpFromResponse(response);
67
+ if (!nextRuntimeIp || cancelled) {
68
+ return;
69
+ }
70
+
71
+ setWebRuntimeIp(currentIp =>
72
+ currentIp === nextRuntimeIp ? currentIp : nextRuntimeIp,
73
+ );
74
+ })
75
+ .catch(() => {});
76
+
77
+ return () => {
78
+ cancelled = true;
79
+ };
80
+ }, [isLogged, user?.id]);
81
+ }
82
+
83
+ export function useWebWebDeviceIdentity({isLogged, isShopClientApp, buildWebDevice, lastWebDeviceSyncRef, setDevice, deviceActions, packageVersion, user, webRuntimeIp}) {
84
+ useEffect(() => {
85
+ if (!isLogged && !isShopClientApp) {
86
+ return;
87
+ }
88
+
89
+ const nextDevice = buildWebDevice();
90
+ if (!nextDevice) {
91
+ return;
92
+ }
93
+
94
+ const signature = JSON.stringify({
95
+ id: nextDevice.id,
96
+ appVersion: nextDevice.appVersion,
97
+ appName: nextDevice.appName,
98
+ type: nextDevice.type,
99
+ metadata: nextDevice.metadata || {},
100
+ externalIp: nextDevice.externalIp || null,
101
+ });
102
+
103
+ if (lastWebDeviceSyncRef.current === signature) {
104
+ return;
105
+ }
106
+
107
+ lastWebDeviceSyncRef.current = signature;
108
+ setDevice(nextDevice);
109
+ localStorage.setItem('device', JSON.stringify(nextDevice));
110
+ deviceActions.setItem(nextDevice);
111
+ localStorage.removeItem('master-device');
112
+ }, [deviceActions, isLogged, isShopClientApp, packageVersion, user?.id, webRuntimeIp]);
113
+ }
114
+
115
+ export function useWebMainCompany({isShopClientApp, device, peopleActions}) {
116
+ useEffect(() => {
117
+ if (isShopClientApp || device?.id) {
118
+ peopleActions.mainCompany().catch(() => {});
119
+ }
120
+ }, [device?.id, isShopClientApp, peopleActions]);
121
+ }
122
+
123
+ export function useWebDeviceRegistration({isLogged, device, currentCompany, canAdministerCompany, mainCompany, user, deviceActions, buildDeviceRegistrationPayload, app_type, hasDeviceRecordChanges, runtimeDeviceType, setDevice}) {
124
+ useEffect(() => {
125
+ if (!isLogged || !device?.id) {
126
+ return;
127
+ }
128
+
129
+ // Device registration changes tenant context and is not valid without an
130
+ // active company or administrative authority for that company.
131
+ if (
132
+ !currentCompany?.id ||
133
+ !canAdministerCompany({company: currentCompany, mainCompany, user})
134
+ ) {
135
+ return;
136
+ }
137
+
138
+ let cancelled = false;
139
+
140
+ const syncDeviceRegistration = async () => {
141
+ const items = await deviceActions.getItems({
142
+ device: device.id,
143
+ });
144
+
145
+ if (cancelled) {
146
+ return;
147
+ }
148
+
149
+ const existingDevice =
150
+ Array.isArray(items) && items.length > 0 ? items[0] : null;
151
+ const nextDevice = buildDeviceRegistrationPayload({
152
+ deviceInfo: device,
153
+ appType: app_type,
154
+ existingDevice,
155
+ });
156
+
157
+ if (!hasDeviceRecordChanges({existingDevice, nextDevice})) {
158
+ const nextLocalDevice = {
159
+ ...device,
160
+ entityId: existingDevice?.id || device?.entityId || null,
161
+ entityIri:
162
+ existingDevice?.['@id'] ||
163
+ device?.entityIri ||
164
+ (existingDevice?.id ? `/devices/${existingDevice.id}` : null),
165
+ alias: existingDevice?.alias || nextDevice.alias,
166
+ type: device?.type || runtimeDeviceType,
167
+ metadata: existingDevice?.metadata || nextDevice.metadata,
168
+ };
169
+
170
+ if (JSON.stringify(nextLocalDevice) !== JSON.stringify(device)) {
171
+ setDevice(nextLocalDevice);
172
+ localStorage.setItem('device', JSON.stringify(nextLocalDevice));
173
+ deviceActions.setItem(nextLocalDevice);
174
+ }
175
+ return;
176
+ }
177
+
178
+ const savedDevice = await deviceActions.save(nextDevice);
179
+ if (cancelled || !savedDevice) {
180
+ return;
181
+ }
182
+
183
+ const nextLocalDevice = {
184
+ ...device,
185
+ entityId: savedDevice?.id || device?.entityId || null,
186
+ entityIri:
187
+ savedDevice?.['@id'] ||
188
+ device?.entityIri ||
189
+ (savedDevice?.id ? `/devices/${savedDevice.id}` : null),
190
+ alias: savedDevice.alias || nextDevice.alias,
191
+ type: device?.type || runtimeDeviceType,
192
+ metadata: savedDevice.metadata || nextDevice.metadata,
193
+ };
194
+
195
+ if (JSON.stringify(nextLocalDevice) !== JSON.stringify(device)) {
196
+ setDevice(nextLocalDevice);
197
+ localStorage.setItem('device', JSON.stringify(nextLocalDevice));
198
+ deviceActions.setItem(nextLocalDevice);
199
+ }
200
+ };
201
+
202
+ syncDeviceRegistration().catch(() => {});
203
+
204
+ return () => {
205
+ cancelled = true;
206
+ };
207
+ }, [
208
+ device?.appVersion,
209
+ device?.batteryLevel,
210
+ device?.buildNumber,
211
+ device?.deviceType,
212
+ device?.externalIp,
213
+ device?.id,
214
+ device?.manufacturer,
215
+ device?.model,
216
+ device?.systemVersion,
217
+ isLogged,
218
+ currentCompany,
219
+ mainCompany,
220
+ runtimeDeviceType,
221
+ user,
222
+ ]);
223
+ }
224
+
225
+ export function useWebPrinters({isShopClientApp, isLogged, currentCompany, printerActions}) {
226
+ useEffect(() => {
227
+ if (isShopClientApp || !isLogged || !currentCompany?.id) {
228
+ return;
229
+ }
230
+
231
+ printerActions
232
+ .ensureCompanyPrintersLoaded({people: currentCompany.id})
233
+ .catch(() => {});
234
+ }, [currentCompany?.id, isLogged, isShopClientApp, printerActions]);
235
+ }
236
+
237
+ export function useWebPaymentTypes({isShopClientApp, companyConfigs, currentCompany, device_config, walletPaymentTypeRequestRef, walletPaymentTypeLoadedKeyRef, paymentTypeActions, mainConfigsDiscovered, resolveDevicePaymentTypeIds, api, selectPosWalletPaymentTypes, getPaymentGateway}) {
238
+ useEffect(() => {
239
+ const paymentConfigSource = isShopClientApp
240
+ ? Object.keys(companyConfigs || {}).length > 0
241
+ ? companyConfigs
242
+ : currentCompany?.configs
243
+ : device_config?.configs;
244
+
245
+ if (!currentCompany?.id || !paymentConfigSource) {
246
+ walletPaymentTypeRequestRef.current = {key: '', promise: null};
247
+ walletPaymentTypeLoadedKeyRef.current = '';
248
+ paymentTypeActions.setItems([]);
249
+ return;
250
+ }
251
+
252
+ if (!isShopClientApp && !mainConfigsDiscovered) {
253
+ return;
254
+ }
255
+
256
+ const paymentTypeSignature = resolveDevicePaymentTypeIds(paymentConfigSource)
257
+ .sort()
258
+ .join(',');
259
+ const requestKey = `${currentCompany.id}:${paymentTypeSignature}`;
260
+
261
+ if (walletPaymentTypeLoadedKeyRef.current === requestKey) {
262
+ return;
263
+ }
264
+
265
+ const existingRequestPromise =
266
+ walletPaymentTypeRequestRef.current.key === requestKey &&
267
+ walletPaymentTypeRequestRef.current.promise
268
+ ? walletPaymentTypeRequestRef.current.promise
269
+ : null;
270
+
271
+ let isMounted = true;
272
+
273
+ const requestPromise =
274
+ existingRequestPromise ||
275
+ api.fetch('wallet_payment_types', {
276
+ params: {
277
+ people: `/people/${currentCompany.id}`,
278
+ },
279
+ });
280
+
281
+ if (!existingRequestPromise) {
282
+ walletPaymentTypeRequestRef.current = {
283
+ key: requestKey,
284
+ promise: requestPromise,
285
+ };
286
+ }
287
+
288
+ requestPromise
289
+ .then(response => {
290
+ if (!isMounted) {
291
+ return;
292
+ }
293
+
294
+ const walletPaymentTypes = Array.isArray(response?.member)
295
+ ? response.member
296
+ : Array.isArray(response?.['hydra:member'])
297
+ ? response['hydra:member']
298
+ : Array.isArray(response)
299
+ ? response
300
+ : [];
301
+ paymentTypeActions.setItems(
302
+ selectPosWalletPaymentTypes({
303
+ walletPaymentTypes,
304
+ deviceConfigs: paymentConfigSource,
305
+ companyConfigs,
306
+ gateway: getPaymentGateway(device_config || paymentConfigSource),
307
+ }),
308
+ );
309
+ walletPaymentTypeLoadedKeyRef.current = requestKey;
310
+ })
311
+ .catch(() => {
312
+ if (isMounted) {
313
+ paymentTypeActions.setItems([]);
314
+ }
315
+ })
316
+ .finally(() => {
317
+ if (
318
+ walletPaymentTypeRequestRef.current.key === requestKey &&
319
+ walletPaymentTypeRequestRef.current.promise === requestPromise
320
+ ) {
321
+ walletPaymentTypeRequestRef.current = {key: '', promise: null};
322
+ }
323
+ });
324
+
325
+ return () => {
326
+ isMounted = false;
327
+ };
328
+ }, [
329
+ companyConfigs,
330
+ currentCompany?.configs,
331
+ currentCompany?.id,
332
+ device_config?.configs,
333
+ isShopClientApp,
334
+ paymentTypeActions,
335
+ ]);
336
+ }
@@ -0,0 +1,5 @@
1
+ // The domain palette owns branding. Franchise selection must not change it.
2
+ export const resolveDomainThemeColors = (baseThemeColors = {}, mainCompany = {}) => ({
3
+ ...(mainCompany?.theme?.colors || {}),
4
+ ...(baseThemeColors || {}),
5
+ });
@@ -209,13 +209,11 @@ export default function Connections() {
209
209
  showColumnFiltersButton
210
210
  onRowPress={openWhatsAppChannel}
211
211
  renderCard={renderConnectionCard}
212
- searchProps={{
213
- compact: true,
214
- placeholder:
215
- tt('connections_label', 'search') || 'Buscar conexao, telefone ou status',
216
- searchKey: 'search',
217
- storeName: 'connections',
218
- }}
212
+ showSearch
213
+ searchPlaceholder={
214
+ tt('connections_label', 'search') || 'Buscar conexao, telefone ou status'
215
+ }
216
+ searchKey="search"
219
217
  totalItemsLabel="connections"
220
218
  visibleColumnsPreferenceKey="connections"
221
219
  accentColor={brandColors.primary}
@@ -23,6 +23,10 @@ import {
23
23
  } from '@controleonline/../../src/styles/branding';
24
24
 
25
25
  import styles from './IntegrationConfigPage.styles';
26
+ import {
27
+ normalizeTextValue, isConnected, formatApiError, resolveFrontOAuthRedirectUri,
28
+ replaceFrontOAuthStatus, formatOAuthStatusError,
29
+ } from './mercadoLivreIntegrationHelpers';
26
30
 
27
31
  const PROVIDER = {
28
32
  key: 'mercadolivre',
@@ -45,81 +49,6 @@ const shadowStyle = Platform.select({
45
49
  web: {boxShadow: '0 10px 24px rgba(15,23,42,0.08)'},
46
50
  });
47
51
 
48
- const normalizeTextValue = value => String(value ?? '').trim();
49
-
50
- const isConnected = value =>
51
- value === true ||
52
- value === 1 ||
53
- value === '1' ||
54
- String(value).trim().toLowerCase() === 'true';
55
-
56
- const formatApiError = error =>
57
- error?.message ||
58
- error?.description ||
59
- error?.error ||
60
- tt('marketplace_error', 'mercadoLivreLoad') ||
61
- 'Nao foi possivel carregar a integracao Mercado Livre.';
62
-
63
- const MERCADO_LIVRE_OAUTH_QUERY_KEYS = [
64
- 'code',
65
- 'state',
66
- 'error',
67
- 'error_description',
68
- 'mercadolivre_connected',
69
- 'mercadolivre_error',
70
- 'mercadolivre_message',
71
- ];
72
-
73
- const resolveFrontOAuthRedirectUri = () => {
74
- if (Platform.OS !== 'web' || typeof window === 'undefined') {
75
- return undefined;
76
- }
77
-
78
- const url = new URL(window.location.href);
79
- MERCADO_LIVRE_OAUTH_QUERY_KEYS.forEach(key => url.searchParams.delete(key));
80
- url.hash = '';
81
-
82
- return url.toString();
83
- };
84
-
85
- const replaceFrontOAuthStatus = statusParams => {
86
- if (Platform.OS !== 'web' || typeof window === 'undefined') {
87
- return;
88
- }
89
-
90
- const url = new URL(window.location.href);
91
- MERCADO_LIVRE_OAUTH_QUERY_KEYS.forEach(key => url.searchParams.delete(key));
92
- Object.entries(statusParams || {}).forEach(([key, value]) => {
93
- if (value !== undefined && value !== null && value !== '') {
94
- url.searchParams.set(key, String(value));
95
- }
96
- });
97
- window.history.replaceState({}, document.title, url.toString());
98
- };
99
-
100
- const formatOAuthStatusError = (error, message) => {
101
- const messageText = normalizeTextValue(message);
102
- if (messageText) {
103
- return `${
104
- tt('marketplace_error', 'mercadoLivreConnectWithMessage') ||
105
- 'Nao foi possivel conectar o Mercado Livre:'
106
- } ${messageText}`;
107
- }
108
-
109
- const errorText = normalizeTextValue(error);
110
- if (!errorText) {
111
- return (
112
- tt('marketplace_error', 'mercadoLivreConnect') ||
113
- 'Nao foi possivel conectar o Mercado Livre.'
114
- );
115
- }
116
-
117
- return `${
118
- tt('marketplace_error', 'mercadoLivreConnect') ||
119
- 'Nao foi possivel conectar o Mercado Livre.'
120
- } (${errorText}).`;
121
- };
122
-
123
52
  export default function MercadoLivreIntegrationPage() {
124
53
  const peopleStore = useStore('people');
125
54
  const themeStore = useStore('theme');
@@ -513,6 +442,7 @@ export default function MercadoLivreIntegrationPage() {
513
442
  </View>
514
443
 
515
444
  <TouchableOpacity
445
+ accessibilityRole="button"
516
446
  style={[
517
447
  styles.saveButton,
518
448
  {backgroundColor: PROVIDER.button},
@@ -0,0 +1,79 @@
1
+ import {Platform} from 'react-native';
2
+
3
+ const tt = (type, key) => global.t?.t('configs', type, key);
4
+
5
+ export const normalizeTextValue = value => String(value ?? '').trim();
6
+
7
+ export const isConnected = value =>
8
+ value === true ||
9
+ value === 1 ||
10
+ value === '1' ||
11
+ String(value).trim().toLowerCase() === 'true';
12
+
13
+ export const formatApiError = error =>
14
+ error?.message ||
15
+ error?.description ||
16
+ error?.error ||
17
+ tt('marketplace_error', 'mercadoLivreLoad') ||
18
+ 'Nao foi possivel carregar a integracao Mercado Livre.';
19
+
20
+ const MERCADO_LIVRE_OAUTH_QUERY_KEYS = [
21
+ 'code',
22
+ 'state',
23
+ 'error',
24
+ 'error_description',
25
+ 'mercadolivre_connected',
26
+ 'mercadolivre_error',
27
+ 'mercadolivre_message',
28
+ ];
29
+
30
+ export const resolveFrontOAuthRedirectUri = () => {
31
+ if (Platform.OS !== 'web' || typeof window === 'undefined') {
32
+ return undefined;
33
+ }
34
+
35
+ const url = new URL(window.location.href);
36
+ MERCADO_LIVRE_OAUTH_QUERY_KEYS.forEach(key => url.searchParams.delete(key));
37
+ url.hash = '';
38
+
39
+ return url.toString();
40
+ };
41
+
42
+ export const replaceFrontOAuthStatus = statusParams => {
43
+ if (Platform.OS !== 'web' || typeof window === 'undefined') {
44
+ return;
45
+ }
46
+
47
+ const url = new URL(window.location.href);
48
+ MERCADO_LIVRE_OAUTH_QUERY_KEYS.forEach(key => url.searchParams.delete(key));
49
+ Object.entries(statusParams || {}).forEach(([key, value]) => {
50
+ if (value !== undefined && value !== null && value !== '') {
51
+ url.searchParams.set(key, String(value));
52
+ }
53
+ });
54
+ window.history.replaceState({}, document.title, url.toString());
55
+ };
56
+
57
+ export const formatOAuthStatusError = (error, message) => {
58
+ const messageText = normalizeTextValue(message);
59
+ if (messageText) {
60
+ return `${
61
+ tt('marketplace_error', 'mercadoLivreConnectWithMessage') ||
62
+ 'Nao foi possivel conectar o Mercado Livre:'
63
+ } ${messageText}`;
64
+ }
65
+
66
+ const errorText = normalizeTextValue(error);
67
+ if (!errorText) {
68
+ return (
69
+ tt('marketplace_error', 'mercadoLivreConnect') ||
70
+ 'Nao foi possivel conectar o Mercado Livre.'
71
+ );
72
+ }
73
+
74
+ return `${
75
+ tt('marketplace_error', 'mercadoLivreConnect') ||
76
+ 'Nao foi possivel conectar o Mercado Livre.'
77
+ } (${errorText}).`;
78
+ };
79
+
@@ -141,9 +141,23 @@ const installSession = async (page, {companyId = 3} = {}) => {
141
141
  active: 1,
142
142
  mycompany: companyId,
143
143
  roles: ['ROLE_ADMIN'],
144
+ name: 'Test User',
145
+ realname: 'Test User',
146
+ username: 'tester',
144
147
  }),
145
148
  );
146
149
  set('config', JSON.stringify({language: 'pt-br'}));
150
+ // Resolve menu labels locally; the mock API does not provide translations.
151
+ const labels = {
152
+ home: 'Início', opportunities: 'Oportunidades',
153
+ customers: 'Clientes', profile: 'Perfil',
154
+ };
155
+ const companyMessages = {
156
+ menu: {menu: labels}, configs: {toolbar: labels},
157
+ };
158
+ set('translates', JSON.stringify({
159
+ 'pt-br': {companies: {3: companyMessages, 9: companyMessages}},
160
+ }));
147
161
  set('app-type', 'MANAGER');
148
162
  set(
149
163
  'device',
@@ -64,6 +64,13 @@ const mockConnectionsApi = async page => {
64
64
  return route.fulfill({status: 204, headers: CORS_HEADERS, body: ''});
65
65
  }
66
66
 
67
+ if (pathname === 'people/companies/my') {
68
+ return route.fulfill({
69
+ status: 200, headers: jsonHeaders(),
70
+ body: JSON.stringify(collection([company])),
71
+ });
72
+ }
73
+
67
74
  if (pathname === 'companies' || pathname.startsWith('people/')) {
68
75
  return route.fulfill({
69
76
  status: 200,
@@ -103,9 +110,20 @@ const mockConnectionsApi = async page => {
103
110
  active: 1,
104
111
  mycompany: 3,
105
112
  roles: ['ROLE_ADMIN'],
113
+ name: 'Test User',
114
+ realname: 'Test User',
115
+ username: 'tester',
106
116
  }),
107
117
  );
108
118
  set('config', JSON.stringify({language: 'pt-br'}));
119
+ set('translates', JSON.stringify({
120
+ 'pt-br': {companies: {3: {configs: {
121
+ connections_title: {connections: 'Conexões'},
122
+ connections_label: {
123
+ whatsApp: 'WhatsApp', search: 'Buscar conexao, telefone ou status',
124
+ },
125
+ }}}},
126
+ }));
109
127
  set('app-type', 'ERP');
110
128
  set(
111
129
  'device',
@@ -1,3 +1,4 @@
1
+ const {installManagerSession} = require('./managerSession.fixture');
1
2
  /**
2
3
  * Smoke browser: Manager /device-detail alias save updates header without refresh.
3
4
  * fluxo: manager-devices
@@ -16,7 +17,7 @@ const { API_ORIGIN } = require('../../../../../../../src/tests/browser/apiOrigin
16
17
  const APP_VERSION = packageJson?.version || '1.0.0';
17
18
  const CURRENT_DEVICE_ID = 'web-7';
18
19
  const DEVICE_ENTITY_ID = 396;
19
- const INITIAL_ALIAS = 'Caixa atual';
20
+ const INITIAL_ALIAS = 'Caixa Santa Redonda';
20
21
  const NEXT_ALIAS = 'Caixa Renomeado #382';
21
22
 
22
23
  const CORS_HEADERS = {
@@ -86,6 +87,7 @@ const MODULES_MAX_500 = [
86
87
  ];
87
88
 
88
89
  const mockDevicesApi = async (page) => {
90
+ await installManagerSession(page);
89
91
  const company = {
90
92
  id: 3,
91
93
  name: 'Teste',
@@ -1,3 +1,4 @@
1
+ const {installManagerSession} = require('./managerSession.fixture');
1
2
  /**
2
3
  * Smoke browser: Manager /device-detail copy configs from another device.
3
4
  * fluxo: manager-devices
@@ -110,6 +111,7 @@ const otherTenant = createDeviceConfig({
110
111
  });
111
112
 
112
113
  const mockCopyApi = async (page) => {
114
+ await installManagerSession(page);
113
115
  const company = {
114
116
  id: 3,
115
117
  name: 'Teste',
@@ -328,6 +330,10 @@ test.describe('device-detail copy config (browser smoke #629)', () => {
328
330
  await expect(page.getByTestId('copy-device-config-hint')).toContainText(
329
331
  DEST_ALIAS,
330
332
  );
333
+ // The translated prefix cannot supply a fixture alias: target identity is dynamic.
334
+ await expect(page.getByTestId('copy-device-config-hint')).not.toContainText(
335
+ SRC_ALIAS,
336
+ );
331
337
  await shot('02-copy-modal-picker.png');
332
338
 
333
339
  await expect(
@@ -1,3 +1,4 @@
1
+ const {installManagerSession} = require('./managerSession.fixture');
1
2
  /**
2
3
  * Smoke browser: DeviceConfig PDV detail opens without currentCompany ReferenceError.
3
4
  * fluxo: device-configuracao
@@ -91,6 +92,7 @@ const createDeviceConfig = ({id, type, device = currentDevice}) => ({
91
92
  });
92
93
 
93
94
  const mockDevicesApi = async page => {
95
+ await installManagerSession(page);
94
96
  const company = {
95
97
  id: 3,
96
98
  name: 'Teste',
@@ -248,7 +250,7 @@ test.describe('device-detail PDV currentCompany (#704)', () => {
248
250
 
249
251
  await page.getByTestId(`device-config-${PDV_CONFIG_ID}`).click();
250
252
  await expect(page).toHaveURL(/device-detail/, {timeout: 15000});
251
- await expect(page.getByText('Configuração do PDV')).toBeVisible({
253
+ await expect(page.getByText('Detalhes do Device', {exact: true})).toBeVisible({
252
254
  timeout: 15000,
253
255
  });
254
256
  await writeEvidence(