@controleonline/ui-common 1.2.86 → 1.2.88

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 (73) hide show
  1. package/.github/workflows/integration-source-gate.yml +68 -0
  2. package/.github/workflows/manager-worker.yml +7 -156
  3. package/.github/workflows/publish-npm.yml +73 -27
  4. package/.github/workflows/pull-request-checks.yml +7 -5
  5. package/docs/technical/Device-Detail-Alias-UI-Sync.md +141 -0
  6. package/package.json +13 -8
  7. package/src/react/components/AddImportModal.js +61 -34
  8. package/src/react/components/BottomNavigationBar.js +178 -174
  9. package/src/react/components/BottomNavigationBar.styles.js +139 -118
  10. package/src/react/components/DefaultProvider.native.js +427 -421
  11. package/src/react/components/DefaultProvider.web.js +131 -125
  12. package/src/react/components/RemoteCheckoutService.js +3 -3
  13. package/src/react/components/RuntimeFooterMarqueeText.js +88 -36
  14. package/src/react/components/RuntimeInfoFooter.js +76 -43
  15. package/src/react/components/RuntimeInfoFooter.styles.js +22 -14
  16. package/src/react/config/deviceConfigBootstrap.js +3 -3
  17. package/src/react/pages/Devices/detail/DeviceDetailAlertsCommandsSection.js +4 -22
  18. package/src/react/pages/Devices/detail/DeviceDetailMovementSections.js +6 -2
  19. package/src/react/pages/Devices/detail/DeviceDetailOrdersPrintSection.js +4 -0
  20. package/src/react/pages/Devices/detail/DeviceDetailScreen.js +23 -338
  21. package/src/react/pages/Devices/detail/createDeviceDetailContext.js +20 -0
  22. package/src/react/pages/Devices/detail/useDeviceDetailActions.js +11 -36
  23. package/src/react/pages/Devices/detail/useDeviceDetailLoaders.js +129 -37
  24. package/src/react/pages/Devices/detail/useDeviceDetailSaves.js +11 -38
  25. package/src/react/pages/Devices/detail/useDeviceDetailStateA.js +9 -28
  26. package/src/react/pages/Imports.js +13 -11
  27. package/src/react/pages/IntegrationConfigFields.js +1 -0
  28. package/src/react/pages/IntegrationConfigPage.js +101 -45
  29. package/src/react/pages/IntegrationConfigPage.utils.js +21 -13
  30. package/src/react/pages/fetchPeopleConfigs.js +21 -0
  31. package/src/react/pages/integrationsCatalog.js +15 -110
  32. package/src/react/services/Cielo/Cielo.js +9 -9
  33. package/src/react/services/Getnet/Getnet.web.js +25 -0
  34. package/src/react/utils/companyAuthority.js +36 -0
  35. package/src/react/utils/fileUrl.js +14 -5
  36. package/src/react/utils/paymentDevices.js +375 -270
  37. package/src/react/utils/printerDevices.js +2 -2
  38. package/src/react/utils/runtimeFooter.js +178 -224
  39. package/src/react/utils/runtimeFooterText.js +160 -0
  40. package/src/react/utils/runtimeLanguage.js +5 -5
  41. package/src/react/utils/runtimeZoom.js +29 -5
  42. package/src/react/utils/shopFranchises.js +167 -12
  43. package/src/react/utils/storeColumns.js +235 -217
  44. package/src/react/utils/systemErrorMessage.js +2 -1
  45. package/src/store/imports/customActions.js +42 -2
  46. package/src/store/integration/index.js +43 -0
  47. package/src/tests/browser/manager/device-detail-apply-config-defined.spec.js +31 -0
  48. package/src/tests/browser/manager/device-detail-composition.spec.js +295 -0
  49. package/src/tests/browser/manager/device-detail-copy-config.spec.js +11 -1
  50. package/src/tests/browser/manager/device-detail-pdv-current-company.spec.js +273 -0
  51. package/src/tests/browser/manager/runtime-footer-rotation.spec.js +397 -0
  52. package/src/tests/react/components/DefaultProviderAuthority.test.js +28 -0
  53. package/src/tests/react/components/RuntimeFooterMarqueeText.build.test.js +29 -0
  54. package/src/tests/react/config/deviceConfigBootstrap.test.js +24 -1
  55. package/src/tests/react/pages/applyCurrentDeviceConfig.test.js +85 -0
  56. package/src/tests/react/pages/deviceDetailComposition.test.js +65 -0
  57. package/src/tests/react/pages/deviceDetailHookDependencies.test.mjs +31 -0
  58. package/src/tests/react/pages/deviceDetailLoaderDependencies.test.mjs +28 -0
  59. package/src/tests/react/pages/receitaFederalIntegrationContract.test.mjs +27 -6
  60. package/src/tests/react/services/Cielo.test.js +2 -2
  61. package/src/tests/react/utils/companyAuthority.test.js +53 -0
  62. package/src/tests/react/utils/formatter.test.mjs +6 -0
  63. package/src/tests/react/utils/paymentDevices.test.js +72 -0
  64. package/src/tests/react/utils/runtimeFooter.test.js +35 -0
  65. package/src/tests/react/utils/runtimeFooterText.test.js +88 -0
  66. package/src/tests/react/utils/runtimeLanguage.test.js +4 -4
  67. package/src/tests/react/utils/runtimeZoom.test.js +20 -0
  68. package/src/tests/react/utils/shopFranchises.test.js +32 -13
  69. package/src/tests/react/utils/translate.test.mjs +6 -6
  70. package/src/utils/formatter.js +7 -3
  71. package/src/utils/translate.js +16 -16
  72. package/src/utils/translatePending.js +6 -6
  73. package/src/utils/translateStorage.js +2 -2
@@ -1,117 +1,117 @@
1
- import { getAllStores } from '@store';
2
- import { formatHumanLabel } from '@controleonline/ui-common/src/react/utils/entityDisplay';
3
-
4
- const normalizeText = value => String(value || '').trim();
5
-
6
- const singularizeResourceName = resourceName => {
7
- const normalized = normalizeText(resourceName).replace(/^\/+|\/+$/g, '');
8
- if (!normalized) {
9
- return '';
10
- }
11
-
12
- if (normalized.endsWith('ies')) {
13
- return `${normalized.slice(0, -3)}y`;
14
- }
15
-
16
- if (normalized.endsWith('sses') || normalized.endsWith('ses')) {
17
- return normalized.slice(0, -2);
18
- }
19
-
20
- if (normalized.endsWith('s') && !normalized.endsWith('ss')) {
21
- return normalized.slice(0, -1);
22
- }
23
-
24
- return normalized;
25
- };
26
-
27
- const normalizeKey = value =>
28
- normalizeText(value)
29
- .replace(/[^a-zA-Z0-9]/g, '')
30
- .toLowerCase();
31
-
32
- const extractEntityResource = entityIri => {
33
- const normalizedIri = normalizeText(entityIri);
34
- if (!normalizedIri) {
35
- return '';
36
- }
37
-
38
- const segments = normalizedIri.split('/').filter(Boolean);
39
- if (segments.length < 2) {
40
- return '';
41
- }
42
-
43
- return normalizeText(segments[segments.length - 2]);
44
- };
45
-
46
- const getStoreColumns = store => {
47
- const columns = store?.getters?.columns || store?.columns;
48
- return Array.isArray(columns) ? columns.filter(Boolean) : [];
49
- };
50
-
51
- const getStoreEndpoint = store =>
52
- normalizeText(store?.getters?.resourceEndpoint || store?.resourceEndpoint).replace(
53
- /^\/+|\/+$/g,
54
- '',
55
- );
56
-
57
- const normalizeComparableValue = value => {
58
- if (value === null || value === undefined || value === '') {
59
- return '';
60
- }
61
-
62
- if (typeof value === 'object' && !Array.isArray(value)) {
63
- return normalizeComparableValue(
64
- value?.value ?? value?.['@id'] ?? value?.id ?? value?.label ?? '',
65
- );
66
- }
67
-
68
- return normalizeText(value);
69
- };
70
-
71
- const buildRowContext = (row, fieldName, value) => {
72
- if (!fieldName) {
73
- return row || {};
74
- }
75
-
76
- if (!row || typeof row !== 'object' || Array.isArray(row)) {
77
- return {[fieldName]: value};
78
- }
79
-
80
- return {
81
- ...row,
82
- [fieldName]: value,
83
- };
84
- };
85
-
86
- const resolveListValue = ({column, value, row}) => {
87
- if (!Array.isArray(column?.list) || !column.list.length) {
88
- return value;
89
- }
90
-
91
- const comparableValue = normalizeComparableValue(value);
92
- const matchedItem = column.list.find(item => {
93
- const currentValue =
94
- item?.value ?? item?.['@id'] ?? item?.id ?? item?.label ?? item;
95
-
96
- return normalizeComparableValue(currentValue) === comparableValue;
97
- });
98
-
99
- if (!matchedItem) {
100
- return value;
101
- }
102
-
103
- if (typeof column.formatList === 'function') {
104
- const formattedItem = column.formatList(matchedItem, column, row);
105
- if (formattedItem && typeof formattedItem === 'object' && !Array.isArray(formattedItem)) {
106
- return formattedItem?.label ?? formattedItem?.value ?? matchedItem?.label ?? value;
107
- }
108
-
109
- return formattedItem;
110
- }
111
-
112
- return matchedItem?.label ?? value;
113
- };
114
-
1
+ import { getAllStores } from '@store';
2
+ import { formatHumanLabel } from '@controleonline/ui-common/src/react/utils/entityDisplay';
3
+
4
+ const normalizeText = value => String(value || '').trim();
5
+
6
+ const singularizeResourceName = resourceName => {
7
+ const normalized = normalizeText(resourceName).replace(/^\/+|\/+$/g, '');
8
+ if (!normalized) {
9
+ return '';
10
+ }
11
+
12
+ if (normalized.endsWith('ies')) {
13
+ return `${normalized.slice(0, -3)}y`;
14
+ }
15
+
16
+ if (normalized.endsWith('sses') || normalized.endsWith('ses')) {
17
+ return normalized.slice(0, -2);
18
+ }
19
+
20
+ if (normalized.endsWith('s') && !normalized.endsWith('ss')) {
21
+ return normalized.slice(0, -1);
22
+ }
23
+
24
+ return normalized;
25
+ };
26
+
27
+ const normalizeKey = value =>
28
+ normalizeText(value)
29
+ .replace(/[^a-zA-Z0-9]/g, '')
30
+ .toLowerCase();
31
+
32
+ const extractEntityResource = entityIri => {
33
+ const normalizedIri = normalizeText(entityIri);
34
+ if (!normalizedIri) {
35
+ return '';
36
+ }
37
+
38
+ const segments = normalizedIri.split('/').filter(Boolean);
39
+ if (segments.length < 2) {
40
+ return '';
41
+ }
42
+
43
+ return normalizeText(segments[segments.length - 2]);
44
+ };
45
+
46
+ const getStoreColumns = store => {
47
+ const columns = store?.getters?.columns || store?.columns;
48
+ return Array.isArray(columns) ? columns.filter(Boolean) : [];
49
+ };
50
+
51
+ const getStoreEndpoint = store =>
52
+ normalizeText(store?.getters?.resourceEndpoint || store?.resourceEndpoint).replace(
53
+ /^\/+|\/+$/g,
54
+ '',
55
+ );
56
+
57
+ const normalizeComparableValue = value => {
58
+ if (value === null || value === undefined || value === '') {
59
+ return '';
60
+ }
61
+
62
+ if (typeof value === 'object' && !Array.isArray(value)) {
63
+ return normalizeComparableValue(
64
+ value?.value ?? value?.['@id'] ?? value?.id ?? value?.label ?? '',
65
+ );
66
+ }
67
+
68
+ return normalizeText(value);
69
+ };
70
+
71
+ const buildRowContext = (row, fieldName, value) => {
72
+ if (!fieldName) {
73
+ return row || {};
74
+ }
75
+
76
+ if (!row || typeof row !== 'object' || Array.isArray(row)) {
77
+ return {[fieldName]: value};
78
+ }
79
+
80
+ return {
81
+ ...row,
82
+ [fieldName]: value,
83
+ };
84
+ };
85
+
86
+ const resolveListValue = ({column, value, row}) => {
87
+ if (!Array.isArray(column?.list) || !column.list.length) {
88
+ return value;
89
+ }
90
+
91
+ const comparableValue = normalizeComparableValue(value);
92
+ const matchedItem = column.list.find(item => {
93
+ const currentValue =
94
+ item?.value ?? item?.['@id'] ?? item?.id ?? item?.label ?? item;
95
+
96
+ return normalizeComparableValue(currentValue) === comparableValue;
97
+ });
98
+
99
+ if (!matchedItem) {
100
+ return value;
101
+ }
102
+
103
+ if (typeof column.formatList === 'function') {
104
+ const formattedItem = column.formatList(matchedItem, column, row);
105
+ if (formattedItem && typeof formattedItem === 'object' && !Array.isArray(formattedItem)) {
106
+ return formattedItem?.label ?? formattedItem?.value ?? matchedItem?.label ?? value;
107
+ }
108
+
109
+ return formattedItem;
110
+ }
111
+
112
+ return matchedItem?.label ?? value;
113
+ };
114
+
115
115
  const normalizeFormattedValue = value => {
116
116
  if (value === null || value === undefined) {
117
117
  return value;
@@ -130,6 +130,9 @@ const normalizeFormattedValue = value => {
130
130
  value?.value ??
131
131
  value?.name ??
132
132
  value?.alias ??
133
+ value?.status ??
134
+ value?.realStatus ??
135
+ value?.real_status ??
133
136
  value?.icon ??
134
137
  value?.fileName ??
135
138
  value?.filename ??
@@ -176,17 +179,32 @@ const shouldTranslateColumnValue = ({column, storeName, value}) => {
176
179
  return candidates.some(candidate => DEFAULT_TRANSLATABLE_FIELDS.has(candidate));
177
180
  };
178
181
 
182
+ const STATUS_DISPLAY_PT = {
183
+ paid: 'Pago',
184
+ };
185
+
179
186
  const translateColumnValue = ({column, storeName, value}) => {
187
+ const statusFallback = STATUS_DISPLAY_PT[normalizeText(value).toLowerCase()];
188
+
180
189
  if (!shouldTranslateColumnValue({column, storeName, value})) {
181
- return value;
190
+ return statusFallback || value;
182
191
  }
183
192
 
184
193
  const translateFn =
185
- typeof globalThis !== 'undefined' && typeof globalThis?.t?.t === 'function'
186
- ? globalThis.t.t.bind(globalThis.t)
187
- : null;
194
+ typeof globalThis !== 'undefined' && typeof globalThis?.t?.t === 'function'
195
+ ? globalThis.t.t.bind(globalThis.t)
196
+ : null;
197
+
198
+ const translated = translateFn ? translateFn(storeName, 'span', value) : value;
199
+ if (
200
+ statusFallback &&
201
+ (!normalizeText(translated) ||
202
+ normalizeText(translated).toLowerCase() === normalizeText(value).toLowerCase())
203
+ ) {
204
+ return statusFallback;
205
+ }
188
206
 
189
- return translateFn ? translateFn(storeName, 'span', value) : value;
207
+ return translated;
190
208
  };
191
209
 
192
210
  const translateColumnLabel = ({column, fieldName, storeName, fallbackLabel = ''}) => {
@@ -308,30 +326,30 @@ export const resolveStoreConfigByEntity = ({entity = null, entityIri = ''} = {})
308
326
  storeName: '',
309
327
  };
310
328
  }
311
-
312
- const normalizedResourceName = singularizeResourceName(resourceName);
313
- const stores = getAllStores();
314
-
315
- const candidates = Object.entries(stores || {})
316
- .map(([storeName, store]) => {
317
- const resourceEndpoint = getStoreEndpoint(store);
318
- const normalizedEndpoint = singularizeResourceName(resourceEndpoint);
319
- const columns = getStoreColumns(store);
320
-
321
- if (
322
- !resourceEndpoint ||
323
- (resourceEndpoint !== resourceName && normalizedEndpoint !== normalizedResourceName)
324
- ) {
325
- return null;
326
- }
327
-
328
- let score = 0;
329
- if (resourceEndpoint === resourceName) score += 4;
330
- if (storeName === resourceName) score += 3;
331
- if (storeName === normalizedResourceName) score += 2;
332
- if (resourceEndpoint === storeName) score += 1;
333
- if (columns.length) score += 1;
334
-
329
+
330
+ const normalizedResourceName = singularizeResourceName(resourceName);
331
+ const stores = getAllStores();
332
+
333
+ const candidates = Object.entries(stores || {})
334
+ .map(([storeName, store]) => {
335
+ const resourceEndpoint = getStoreEndpoint(store);
336
+ const normalizedEndpoint = singularizeResourceName(resourceEndpoint);
337
+ const columns = getStoreColumns(store);
338
+
339
+ if (
340
+ !resourceEndpoint ||
341
+ (resourceEndpoint !== resourceName && normalizedEndpoint !== normalizedResourceName)
342
+ ) {
343
+ return null;
344
+ }
345
+
346
+ let score = 0;
347
+ if (resourceEndpoint === resourceName) score += 4;
348
+ if (storeName === resourceName) score += 3;
349
+ if (storeName === normalizedResourceName) score += 2;
350
+ if (resourceEndpoint === storeName) score += 1;
351
+ if (columns.length) score += 1;
352
+
335
353
  return {
336
354
  columns,
337
355
  resourceEndpoint,
@@ -340,16 +358,16 @@ export const resolveStoreConfigByEntity = ({entity = null, entityIri = ''} = {})
340
358
  store,
341
359
  storeName,
342
360
  };
343
- })
344
- .filter(Boolean)
345
- .sort((left, right) => {
346
- if (right.score !== left.score) {
347
- return right.score - left.score;
348
- }
349
-
350
- return right.columns.length - left.columns.length;
351
- });
352
-
361
+ })
362
+ .filter(Boolean)
363
+ .sort((left, right) => {
364
+ if (right.score !== left.score) {
365
+ return right.score - left.score;
366
+ }
367
+
368
+ return right.columns.length - left.columns.length;
369
+ });
370
+
353
371
  return candidates[0] || {
354
372
  columns: [],
355
373
  resourceEndpoint: '',
@@ -358,38 +376,38 @@ export const resolveStoreConfigByEntity = ({entity = null, entityIri = ''} = {})
358
376
  storeName: '',
359
377
  };
360
378
  };
361
-
362
- export const resolveStoreColumn = (columns = [], fieldName = '') => {
363
- const normalizedFieldName = normalizeKey(fieldName);
364
- if (!normalizedFieldName) {
365
- return null;
366
- }
367
-
368
- return columns.find(column => {
369
- const candidateNames = [
370
- column?.key,
371
- column?.name,
372
- column?.label,
373
- ]
374
- .map(normalizeKey)
375
- .filter(Boolean);
376
-
377
- return candidateNames.includes(normalizedFieldName);
378
- }) || null;
379
- };
380
-
379
+
380
+ export const resolveStoreColumn = (columns = [], fieldName = '') => {
381
+ const normalizedFieldName = normalizeKey(fieldName);
382
+ if (!normalizedFieldName) {
383
+ return null;
384
+ }
385
+
386
+ return columns.find(column => {
387
+ const candidateNames = [
388
+ column?.key,
389
+ column?.name,
390
+ column?.label,
391
+ ]
392
+ .map(normalizeKey)
393
+ .filter(Boolean);
394
+
395
+ return candidateNames.includes(normalizedFieldName);
396
+ }) || null;
397
+ };
398
+
381
399
  export const formatStoreColumnLabel = ({
382
400
  columns = [],
383
401
  fieldName = '',
384
402
  fallbackLabel = '',
385
403
  storeName = '',
386
- } = {}) => {
387
- const column = resolveStoreColumn(columns, fieldName);
388
- if (!column?.label) {
389
- return fallbackLabel;
390
- }
391
-
392
- const normalizedLabel = normalizeText(column.label);
404
+ } = {}) => {
405
+ const column = resolveStoreColumn(columns, fieldName);
406
+ if (!column?.label) {
407
+ return fallbackLabel;
408
+ }
409
+
410
+ const normalizedLabel = normalizeText(column.label);
393
411
  if (!normalizedLabel) {
394
412
  return fallbackLabel;
395
413
  }
@@ -407,40 +425,40 @@ export const formatStoreColumnLabel = ({
407
425
  const resolvedLabel = translateColumnValue({
408
426
  column,
409
427
  storeName,
410
- value: normalizedLabel,
411
- });
412
-
413
- return formatHumanLabel(resolvedLabel) || resolvedLabel;
414
- };
415
-
416
- // Replica a logica central de columns do store para usos fora das listagens padrao.
417
- export const formatStoreColumnValue = ({
418
- columns = [],
419
- fieldName = '',
420
- row = null,
421
- storeName = '',
422
- value,
423
- } = {}) => {
424
- const column = resolveStoreColumn(columns, fieldName);
425
- if (!column) {
426
- return undefined;
427
- }
428
-
429
- const rowContext = buildRowContext(row, fieldName, value);
430
- const resolvedValue = resolveListValue({
431
- column,
432
- row: rowContext,
433
- value,
434
- });
435
-
436
- let formattedValue = resolvedValue;
437
- if (typeof column.format === 'function') {
438
- formattedValue = column.format(resolvedValue, column, rowContext, false);
439
- }
440
-
441
- return translateColumnValue({
442
- column,
443
- storeName,
444
- value: normalizeFormattedValue(formattedValue),
445
- });
446
- };
428
+ value: normalizedLabel,
429
+ });
430
+
431
+ return formatHumanLabel(resolvedLabel) || resolvedLabel;
432
+ };
433
+
434
+ // Replica a logica central de columns do store para usos fora das listagens padrao.
435
+ export const formatStoreColumnValue = ({
436
+ columns = [],
437
+ fieldName = '',
438
+ row = null,
439
+ storeName = '',
440
+ value,
441
+ } = {}) => {
442
+ const column = resolveStoreColumn(columns, fieldName);
443
+ if (!column) {
444
+ return undefined;
445
+ }
446
+
447
+ const rowContext = buildRowContext(row, fieldName, value);
448
+ const resolvedValue = resolveListValue({
449
+ column,
450
+ row: rowContext,
451
+ value,
452
+ });
453
+
454
+ let formattedValue = resolvedValue;
455
+ if (typeof column.format === 'function') {
456
+ formattedValue = column.format(resolvedValue, column, rowContext, false);
457
+ }
458
+
459
+ return translateColumnValue({
460
+ column,
461
+ storeName,
462
+ value: normalizeFormattedValue(formattedValue),
463
+ });
464
+ };
@@ -19,7 +19,7 @@ const resolveMessageList = items =>
19
19
  .join('\n')
20
20
  .trim()
21
21
 
22
- export const resolveSystemErrorMessage = error => {
22
+ const resolveSystemErrorMessage = error => {
23
23
  if (error === undefined || error === null) {
24
24
  return ''
25
25
  }
@@ -68,4 +68,5 @@ export const resolveSystemErrorMessage = error => {
68
68
  )
69
69
  }
70
70
 
71
+ export { resolveSystemErrorMessage }
71
72
  export default resolveSystemErrorMessage
@@ -1,6 +1,8 @@
1
1
  import {Platform} from 'react-native';
2
2
  import {api} from '@controleonline/ui-common/src/api';
3
3
 
4
+ const FORBIDDEN_EXTENSIONS = new Set(['*', '*.*', '', '.', '.*']);
5
+
4
6
  const extractId = value => {
5
7
  if (value === null || value === undefined || value === '') {
6
8
  return '';
@@ -15,11 +17,42 @@ const extractId = value => {
15
17
  return match ? match[1] : '';
16
18
  };
17
19
 
20
+ const normalizeExtension = value =>
21
+ String(value || '')
22
+ .trim()
23
+ .replace(/^\./, '')
24
+ .toLowerCase();
25
+
26
+ const fileExtension = file => {
27
+ const name = String(file?.name || file?.uri || '');
28
+ const match = name.toLowerCase().match(/\.([a-z0-9]+)$/);
29
+ return match ? match[1] : '';
30
+ };
31
+
32
+ const sanitizeAllowedExtensions = (extensions, importType) => {
33
+ const fallback =
34
+ importType === 'invoice_tax' || importType === 'xml' ? ['xml', 'zip'] : ['csv'];
35
+ const source = Array.isArray(extensions) && extensions.length > 0 ? extensions : fallback;
36
+ const cleaned = source
37
+ .map(normalizeExtension)
38
+ .filter(item => item && !FORBIDDEN_EXTENSIONS.has(item) && !item.includes('*'));
39
+
40
+ if (cleaned.length === 0) {
41
+ throw new Error('Tipo de importacao sem extensao permitida. Importar *.* nao e permitido.');
42
+ }
43
+
44
+ return [...new Set(cleaned)];
45
+ };
46
+
18
47
  export const uploadImportFile = (_context, payload = {}) => {
19
48
  const normalizedPayload = payload && typeof payload === 'object' ? payload : {};
20
49
  const file = normalizedPayload.file;
21
50
  const importType = String(normalizedPayload.importType || '').trim();
22
51
  const peopleId = extractId(normalizedPayload.peopleId || normalizedPayload.people);
52
+ const allowedExtensions = sanitizeAllowedExtensions(
53
+ normalizedPayload.allowedExtensions,
54
+ importType,
55
+ );
23
56
 
24
57
  if (!file) {
25
58
  throw new Error('Arquivo de importacao nao informado.');
@@ -33,6 +66,13 @@ export const uploadImportFile = (_context, payload = {}) => {
33
66
  throw new Error('Empresa nao identificada para a importacao.');
34
67
  }
35
68
 
69
+ const extension = fileExtension(file);
70
+ if (!allowedExtensions.includes(extension)) {
71
+ throw new Error(
72
+ `Extensao nao permitida. Aceitas: ${allowedExtensions.map(item => `.${item}`).join(', ')}.`,
73
+ );
74
+ }
75
+
36
76
  const formData = new FormData();
37
77
  formData.append('importType', importType);
38
78
  formData.append('people', peopleId);
@@ -42,8 +82,8 @@ export const uploadImportFile = (_context, payload = {}) => {
42
82
  } else {
43
83
  formData.append('file', {
44
84
  uri: file.uri,
45
- name: file.name || 'import.csv',
46
- type: file.mimeType || 'text/csv',
85
+ name: file.name || `import.${extension || allowedExtensions[0]}`,
86
+ type: file.mimeType || 'application/octet-stream',
47
87
  });
48
88
  }
49
89
 
@@ -0,0 +1,43 @@
1
+ import * as actions from '@controleonline/ui-default/src/store/default/actions';
2
+ import * as getters from '@controleonline/ui-default/src/store/default/getters';
3
+ import mutations from '@controleonline/ui-default/src/store/default/mutations';
4
+
5
+ export default {
6
+ namespaced: true,
7
+ state: {
8
+ item: null,
9
+ items: [],
10
+ resourceEndpoint: 'integrations',
11
+ isLoading: false,
12
+ error: '',
13
+ totalItems: 0,
14
+ summary: {},
15
+ filters: {},
16
+ reload: false,
17
+ add: false,
18
+ columns: [
19
+ {name: 'id', label: 'ID', isIdentity: true, editable: false, externalFilter: true},
20
+ {name: 'queueName', label: 'Fila', editable: false, externalFilter: false},
21
+ {
22
+ name: 'status',
23
+ label: 'Status',
24
+ editable: false,
25
+ externalFilter: true,
26
+ list: 'status/getItems',
27
+ listRequestParams: {context: 'integration'},
28
+ searchParam: 'status',
29
+ format: value => (value?.status || value?.realStatus || '-'),
30
+ formatList: value => (value && value['@id'] ? {value: value['@id'].split('/').pop(), label: value.status || value.realStatus || value['@id']} : value),
31
+ saveFormat: value => (value ? `/statuses/${value.value || value}` : null),
32
+ },
33
+ {name: 'retry', label: 'Tentativas', type: 'number', editable: false, externalFilter: true},
34
+ {name: 'body', label: 'Payload', editable: false, externalFilter: false},
35
+ ],
36
+ configs: {
37
+ showRowActions: true,
38
+ },
39
+ },
40
+ actions,
41
+ getters,
42
+ mutations,
43
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Static smoke: DeviceDetail loader declares applyCurrentDeviceConfig + useFocusEffect.
3
+ * fluxo: device-configuracao
4
+ * Refs: app-community#706
5
+ */
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const {expect, test} = require('playwright/test');
9
+
10
+ const FLOW_ID = 'device-configuracao';
11
+ const loaderPath = path.resolve(
12
+ __dirname,
13
+ '../../../react/pages/Devices/detail/useDeviceDetailLoaders.js',
14
+ );
15
+ const stateAPath = path.resolve(
16
+ __dirname,
17
+ '../../../react/pages/Devices/detail/useDeviceDetailStateA.js',
18
+ );
19
+
20
+ test('device-configuracao #706 loader refs are declared', async () => {
21
+ const loader = fs.readFileSync(loaderPath, 'utf8');
22
+ const stateA = fs.readFileSync(stateAPath, 'utf8');
23
+ expect(loader).toContain('useFocusEffect');
24
+ expect(loader).toContain('const applyCurrentDeviceConfig = useCallback');
25
+ expect(loader).toContain('currentCompany');
26
+ expect(loader).toContain('displayStore');
27
+ expect(loader).toContain('printerStore');
28
+ expect(stateA).toMatch(/displayStore,/);
29
+ expect(stateA).toMatch(/printerStore,/);
30
+ expect(FLOW_ID).toBe('device-configuracao');
31
+ });