@controleonline/ui-common 1.2.80 → 1.2.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 (90) hide show
  1. package/AGENTS.md +8 -0
  2. package/package.json +9 -3
  3. package/src/api/fetch.js +7 -7
  4. package/src/react/components/AddImportModal.js +6 -2
  5. package/src/react/components/BackgroundRuntimeBridge.js +201 -215
  6. package/src/react/components/ConfirmModal.js +44 -9
  7. package/src/react/components/ConfirmModal.styles.js +55 -41
  8. package/src/react/components/CopyDeviceConfigModal.js +193 -0
  9. package/src/react/components/CopyDeviceConfigModal.styles.js +123 -0
  10. package/src/react/components/DefaultProvider.native.js +22 -0
  11. package/src/react/components/DefaultProvider.web.js +25 -1
  12. package/src/react/components/EntityLogBranch.js +482 -0
  13. package/src/react/components/EntityLogCards.js +369 -0
  14. package/src/react/components/EntityLogContent.js +113 -794
  15. package/src/react/components/LinkedOrderProductsTab.js +3 -3
  16. package/src/react/components/PrintService.js +130 -409
  17. package/src/react/components/RemoteCheckoutService.js +2 -2
  18. package/src/react/components/RuntimeBottomNavigationBar.js +141 -146
  19. package/src/react/components/RuntimeFooterMarqueeText.js +137 -0
  20. package/src/react/components/RuntimeInfoFooter.js +12 -15
  21. package/src/react/components/SystemErrorToast.js +49 -29
  22. package/src/react/components/SystemErrorToast.styles.js +43 -38
  23. package/src/react/components/UserAvatar.js +120 -28
  24. package/src/react/config/deviceConfigBootstrap.js +330 -304
  25. package/src/react/hooks/usePrintSpoolEffects.js +161 -0
  26. package/src/react/pages/DeviceDetailPage.js +6 -2882
  27. package/src/react/pages/Devices/detail/DeviceDetailAlertsCommandsSection.js +233 -0
  28. package/src/react/pages/Devices/detail/DeviceDetailHeader.js +138 -0
  29. package/src/react/pages/Devices/detail/DeviceDetailMovementSections.js +167 -0
  30. package/src/react/pages/Devices/detail/DeviceDetailOrdersPrintSection.js +216 -0
  31. package/src/react/pages/Devices/detail/DeviceDetailPaymentSection.js +40 -0
  32. package/src/react/pages/Devices/detail/DeviceDetailPdvConfigSection.js +390 -0
  33. package/src/react/pages/Devices/detail/DeviceDetailRenderers.js +135 -0
  34. package/src/react/pages/Devices/detail/DeviceDetailScreen.js +493 -0
  35. package/src/react/pages/Devices/detail/OptionButtonChip.js +65 -0
  36. package/src/react/pages/Devices/detail/deviceDetailConstants.js +36 -0
  37. package/src/react/pages/Devices/detail/deviceDetailHelpers.js +70 -0
  38. package/src/react/pages/Devices/detail/useDeviceDetailActions.js +483 -0
  39. package/src/react/pages/Devices/detail/useDeviceDetailCopyConfig.js +87 -0
  40. package/src/react/pages/Devices/detail/useDeviceDetailLoaders.js +327 -0
  41. package/src/react/pages/Devices/detail/useDeviceDetailSaves.js +429 -0
  42. package/src/react/pages/Devices/detail/useDeviceDetailStateA.js +484 -0
  43. package/src/react/pages/Devices/detail/useDeviceDetailStateB.js +71 -0
  44. package/src/react/pages/Devices/deviceTypes/index.js +2 -0
  45. package/src/react/pages/Devices/deviceTypes/pdv.js +11 -0
  46. package/src/react/pages/Imports.js +3 -2
  47. package/src/react/pages/IntegrationConfigFields.js +126 -0
  48. package/src/react/pages/IntegrationConfigPage.js +122 -591
  49. package/src/react/pages/IntegrationConfigPage.utils.js +95 -0
  50. package/src/react/print/usePrintButtonController.js +9 -1
  51. package/src/react/services/Getnet/Checkout.js +81 -0
  52. package/src/react/services/Getnet/Getnet.js +77 -0
  53. package/src/react/services/paymentGatewayExecution.js +20 -0
  54. package/src/react/utils/bottomNavigationToolbar.js +161 -0
  55. package/src/react/utils/commercialDocumentOrders.js +18 -6
  56. package/src/react/utils/copyDeviceConfigs.js +198 -0
  57. package/src/react/utils/fileUrl.js +43 -0
  58. package/src/react/utils/importStatus.js +19 -10
  59. package/src/react/utils/logSettings.js +7 -7
  60. package/src/react/utils/maintenanceSettings.js +16 -0
  61. package/src/react/utils/paymentDevices.js +6 -0
  62. package/src/react/utils/printManagedPrinter.js +45 -0
  63. package/src/react/utils/printRouting.js +63 -0
  64. package/src/react/utils/printSpoolUtils.js +119 -0
  65. package/src/react/utils/printerDevices.js +368 -368
  66. package/src/react/utils/runtimeFooter.js +10 -19
  67. package/src/react/utils/shopFranchises.js +186 -65
  68. package/src/react/utils/systemErrorMessage.js +71 -67
  69. package/src/store/device_config/customActions.js +119 -30
  70. package/src/tests/browser/manager/bottom-navigation-role-toolbar.spec.js +353 -0
  71. package/src/tests/browser/manager/device-detail-alias-save.spec.js +345 -0
  72. package/src/tests/browser/manager/device-detail-copy-config.spec.js +365 -0
  73. package/src/tests/browser/manager/runtime-footer-marquee.spec.js +262 -0
  74. package/src/tests/browser/manager/theme-confirm-toast.spec.js +236 -0
  75. package/src/tests/react/components/RuntimeFooterMarqueeText.test.js +171 -0
  76. package/src/tests/react/components/UserAvatar.test.js +6 -2
  77. package/src/tests/react/components/themeAwareModals.test.mjs +60 -0
  78. package/src/tests/react/config/deviceConfigBootstrap.test.js +104 -66
  79. package/src/tests/react/pages/deviceDetailDelete.test.js +78 -0
  80. package/src/tests/react/pages/receitaFederalIntegrationContract.test.mjs +48 -0
  81. package/src/tests/react/utils/bottomNavigationToolbar.test.js +222 -0
  82. package/src/tests/react/utils/copyDeviceConfigs.test.mjs +127 -0
  83. package/src/tests/react/utils/fileUrl.test.js +38 -0
  84. package/src/tests/react/utils/importStatus.test.js +26 -9
  85. package/src/tests/react/utils/passwordPolicy.test.js +27 -0
  86. package/src/tests/react/utils/printRouting.test.mjs +86 -0
  87. package/src/tests/react/utils/printSpoolUtils.test.mjs +52 -0
  88. package/src/tests/react/utils/runtimeFooter.test.js +7 -0
  89. package/src/tests/react/utils/shopFranchises.test.js +121 -115
  90. package/src/tests/react/utils/systemErrorMessage.test.js +32 -59
@@ -0,0 +1,369 @@
1
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ Image,
5
+ ScrollView,
6
+ Text,
7
+ TouchableOpacity,
8
+ View,
9
+ } from 'react-native';
10
+ import Icon from 'react-native-vector-icons/MaterialIcons';
11
+ import { useStore } from '@store';
12
+ import Formatter from '@controleonline/ui-common/src/utils/formatter';
13
+ import {
14
+ buildEntityChildren,
15
+ buildEntityKey,
16
+ buildEntityLabel,
17
+ buildEntitySummaryFields,
18
+ formatLogFieldLabel,
19
+ formatLogValue,
20
+ resolveEntityImageUrl,
21
+ } from '@controleonline/ui-common/src/react/utils/entityLog';
22
+ import { resolveStoreConfigByEntity } from '@controleonline/ui-common/src/react/utils/storeColumns';
23
+ import createStyles, {
24
+ buildEntityLogPalette,
25
+ } from './EntityLogContent.styles';
26
+
27
+ const ACTION_META = {
28
+ insert: {
29
+ color: '#16A34A',
30
+ icon: 'add-circle',
31
+ label: 'Criado',
32
+ },
33
+ update: {
34
+ color: '#2563EB',
35
+ icon: 'edit-note',
36
+ label: 'Alterado',
37
+ },
38
+ delete: {
39
+ color: '#DC2626',
40
+ icon: 'delete-outline',
41
+ label: 'Removido',
42
+ },
43
+ info: {
44
+ color: '#0F766E',
45
+ icon: 'info-outline',
46
+ label: 'Info',
47
+ },
48
+ notice: {
49
+ color: '#0369A1',
50
+ icon: 'notifications-none',
51
+ label: 'Aviso',
52
+ },
53
+ warning: {
54
+ color: '#D97706',
55
+ icon: 'warning-amber',
56
+ label: 'Alerta',
57
+ },
58
+ error: {
59
+ color: '#DC2626',
60
+ icon: 'error-outline',
61
+ label: 'Erro',
62
+ },
63
+ critical: {
64
+ color: '#991B1B',
65
+ icon: 'report-gmailerrorred',
66
+ label: 'Critico',
67
+ },
68
+ debug: {
69
+ color: '#475569',
70
+ icon: 'bug-report',
71
+ label: 'Debug',
72
+ },
73
+ };
74
+
75
+ const GENERIC_PAYLOAD_META_FIELDS = new Set(['channel', 'context', 'level', 'message']);
76
+
77
+ const buildContextEntries = context => {
78
+ if (!context) {
79
+ return [];
80
+ }
81
+
82
+ if (Array.isArray(context)) {
83
+ return context.map((value, index) => [`item_${index + 1}`, value]);
84
+ }
85
+
86
+ if (typeof context === 'object') {
87
+ return Object.entries(context);
88
+ }
89
+
90
+ return [['context', context]];
91
+ };
92
+
93
+ const formatContextValue = value => {
94
+ if (value === null || value === undefined || value === '') {
95
+ return '—';
96
+ }
97
+
98
+ if (typeof value === 'boolean') {
99
+ return value ? 'Sim' : 'Nao';
100
+ }
101
+
102
+ if (typeof value === 'string') {
103
+ return value;
104
+ }
105
+
106
+ if (typeof value === 'number') {
107
+ return String(value);
108
+ }
109
+
110
+ try {
111
+ return JSON.stringify(value, null, 2);
112
+ } catch {
113
+ return String(value);
114
+ }
115
+ };
116
+
117
+ const resolvePayload = log => {
118
+ if (log?.payload && typeof log.payload === 'object' && !Array.isArray(log.payload)) {
119
+ return log.payload;
120
+ }
121
+
122
+ if (typeof log?.object === 'string') {
123
+ try {
124
+ const parsed = JSON.parse(log.object);
125
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
126
+ } catch {
127
+ return {};
128
+ }
129
+ }
130
+
131
+ return {};
132
+ };
133
+
134
+ // O card de resumo mostra os dados essenciais da entidade expandida.
135
+ export const EntitySummaryCard = ({
136
+ columns = [],
137
+ detailState,
138
+ entity,
139
+ entityClass,
140
+ storeName = '',
141
+ styles,
142
+ }) => {
143
+ const summaryFields = useMemo(
144
+ () => buildEntitySummaryFields({
145
+ entity,
146
+ className: entityClass,
147
+ columns,
148
+ storeName,
149
+ }),
150
+ [columns, entity, entityClass, storeName],
151
+ );
152
+ const imageUrl = useMemo(() => resolveEntityImageUrl(entity), [entity]);
153
+
154
+ if (detailState?.status === 'loading' && !imageUrl && !summaryFields.length) {
155
+ return (
156
+ <View style={styles.entityStateCard}>
157
+ <ActivityIndicator size="small" color="#2563EB" />
158
+ <Text style={styles.entityStateText}>Carregando detalhes...</Text>
159
+ </View>
160
+ );
161
+ }
162
+
163
+ if (
164
+ detailState?.status !== 'loading' &&
165
+ detailState?.status !== 'success' &&
166
+ !imageUrl &&
167
+ !summaryFields.length
168
+ ) {
169
+ return null;
170
+ }
171
+
172
+ return (
173
+ <View style={styles.entitySummaryCard}>
174
+ <View style={styles.entitySummaryHeader}>
175
+ <Text style={styles.sectionTitle}>Resumo</Text>
176
+ {detailState?.status === 'loading' ? (
177
+ <Text style={styles.entityStateText}>Atualizando...</Text>
178
+ ) : null}
179
+ </View>
180
+
181
+ <View style={styles.entitySummaryBody}>
182
+ {imageUrl ? (
183
+ <Image
184
+ source={{ uri: imageUrl }}
185
+ style={styles.entityPreviewImage}
186
+ resizeMode="cover"
187
+ />
188
+ ) : null}
189
+
190
+ <View style={styles.entitySummaryGrid}>
191
+ {summaryFields.length ? (
192
+ summaryFields.map(field => (
193
+ <View key={field.key} style={styles.entitySummaryField}>
194
+ <Text style={styles.entitySummaryLabel}>{field.label}</Text>
195
+ <Text style={styles.entitySummaryValue}>{field.value}</Text>
196
+ </View>
197
+ ))
198
+ ) : (
199
+ <Text style={styles.emptyInlineText}>Sem dados resumidos.</Text>
200
+ )}
201
+ </View>
202
+ </View>
203
+
204
+ {detailState?.status === 'error' ? (
205
+ <Text style={styles.entityStateErrorText}>
206
+ {detailState?.error || 'Nao foi possivel carregar os detalhes.'}
207
+ </Text>
208
+ ) : null}
209
+ </View>
210
+ );
211
+ };
212
+
213
+ const getActionMeta = action => ACTION_META[String(action || '').toLowerCase()] || {
214
+ color: '#64748B',
215
+ icon: 'history',
216
+ label: 'Evento',
217
+ };
218
+
219
+ export const renderChangeValue = (styles, title, value) => (
220
+ <View style={styles.changeValueBox}>
221
+ {title ? <Text style={styles.changeValueTitle}>{title}</Text> : null}
222
+ <Text style={styles.changeValueText}>{value}</Text>
223
+ </View>
224
+ );
225
+
226
+ export const LogCard = ({
227
+ columns = [],
228
+ log,
229
+ rowContext = null,
230
+ storeName = '',
231
+ styles,
232
+ }) => {
233
+ const payload = useMemo(() => resolvePayload(log), [log]);
234
+ const actionMeta = useMemo(() => getActionMeta(log?.action), [log?.action]);
235
+ const entries = useMemo(() => Object.entries(payload || {}), [payload]);
236
+ const detailEntries = useMemo(
237
+ () => entries.filter(([field]) => !GENERIC_PAYLOAD_META_FIELDS.has(field)),
238
+ [entries],
239
+ );
240
+ const genericMessage = useMemo(
241
+ () => (typeof payload?.message === 'string' ? payload.message.trim() : ''),
242
+ [payload?.message],
243
+ );
244
+ const genericChannel = useMemo(
245
+ () => (typeof payload?.channel === 'string' ? payload.channel.trim() : ''),
246
+ [payload?.channel],
247
+ );
248
+ const contextEntries = useMemo(
249
+ () => buildContextEntries(payload?.context),
250
+ [payload?.context],
251
+ );
252
+ const logDate = useMemo(
253
+ () => Formatter.formatDateYmdTodmY(log?.createdAt, true),
254
+ [log?.createdAt],
255
+ );
256
+
257
+ return (
258
+ <View style={styles.logCard}>
259
+ <View style={styles.logMetaRow}>
260
+ <View style={styles.logMetaLeft}>
261
+ <View
262
+ style={[
263
+ styles.actionBadge,
264
+ {
265
+ borderColor: `${actionMeta.color}44`,
266
+ backgroundColor: `${actionMeta.color}14`,
267
+ },
268
+ ]}>
269
+ <Icon name={actionMeta.icon} size={14} color={actionMeta.color} />
270
+ <Text style={[styles.actionText, { color: actionMeta.color }]}>
271
+ {actionMeta.label}
272
+ </Text>
273
+ </View>
274
+
275
+ <Text style={styles.logDate}>{logDate || 'Sem data'}</Text>
276
+ </View>
277
+
278
+ {!!log?.userDisplayName && (
279
+ <Text style={styles.logUser}>{log.userDisplayName}</Text>
280
+ )}
281
+ </View>
282
+
283
+ {!!genericChannel && (
284
+ <Text style={styles.logChannel}>{genericChannel}</Text>
285
+ )}
286
+
287
+ {!!genericMessage && (
288
+ <View style={styles.genericMessageBox}>
289
+ <Text style={styles.genericMessage}>{genericMessage}</Text>
290
+ </View>
291
+ )}
292
+
293
+ {!!contextEntries.length && (
294
+ <View style={styles.contextSection}>
295
+ {contextEntries.map(([field, value]) => (
296
+ <View key={`${log?.id || 'log'}-context-${field}`} style={styles.changeRow}>
297
+ <Text style={styles.changeLabel}>
298
+ {formatLogFieldLabel(field, {
299
+ columns,
300
+ storeName,
301
+ })}
302
+ </Text>
303
+
304
+ {renderChangeValue(styles, '', formatContextValue(value))}
305
+ </View>
306
+ ))}
307
+ </View>
308
+ )}
309
+
310
+ {detailEntries.length ? (
311
+ detailEntries.map(([field, value]) => {
312
+ const isDiffValue =
313
+ Array.isArray(value) &&
314
+ value.length === 2 &&
315
+ Object.prototype.hasOwnProperty.call(value, 0) &&
316
+ Object.prototype.hasOwnProperty.call(value, 1);
317
+
318
+ return (
319
+ <View key={`${log?.id || 'log'}-${field}`} style={styles.changeRow}>
320
+ <Text style={styles.changeLabel}>
321
+ {formatLogFieldLabel(field, {
322
+ columns,
323
+ storeName,
324
+ })}
325
+ </Text>
326
+
327
+ {isDiffValue ? (
328
+ <View style={styles.changeValuesRow}>
329
+ {renderChangeValue(styles, 'Antes', formatLogValue(value[0], {
330
+ columns,
331
+ fieldName: field,
332
+ row: rowContext,
333
+ storeName,
334
+ }))}
335
+ <View style={styles.arrowWrap}>
336
+ <Icon name="east" size={16} color="#94A3B8" />
337
+ </View>
338
+ {renderChangeValue(styles, 'Depois', formatLogValue(value[1], {
339
+ columns,
340
+ fieldName: field,
341
+ row: rowContext,
342
+ storeName,
343
+ }))}
344
+ </View>
345
+ ) : (
346
+ renderChangeValue(
347
+ styles,
348
+ '',
349
+ formatLogValue(value, {
350
+ columns,
351
+ fieldName: field,
352
+ row: rowContext,
353
+ storeName,
354
+ }),
355
+ )
356
+ )}
357
+ </View>
358
+ );
359
+ })
360
+ ) : (
361
+ !genericMessage &&
362
+ !contextEntries.length && (
363
+ <Text style={styles.emptyInlineText}>Sem detalhes adicionais.</Text>
364
+ )
365
+ )}
366
+ </View>
367
+ );
368
+ };
369
+