@controleonline/ui-common 1.2.80 → 1.2.82
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/AGENTS.md +8 -0
- package/package.json +9 -3
- package/src/api/fetch.js +7 -7
- package/src/react/components/AddImportModal.js +6 -2
- package/src/react/components/BackgroundRuntimeBridge.js +201 -215
- package/src/react/components/ConfirmModal.js +44 -9
- package/src/react/components/ConfirmModal.styles.js +55 -41
- package/src/react/components/CopyDeviceConfigModal.js +193 -0
- package/src/react/components/CopyDeviceConfigModal.styles.js +123 -0
- package/src/react/components/DefaultProvider.native.js +22 -0
- package/src/react/components/DefaultProvider.web.js +25 -1
- package/src/react/components/EntityLogBranch.js +482 -0
- package/src/react/components/EntityLogCards.js +369 -0
- package/src/react/components/EntityLogContent.js +113 -794
- package/src/react/components/LinkedOrderProductsTab.js +3 -3
- package/src/react/components/PrintService.js +130 -409
- package/src/react/components/RemoteCheckoutService.js +2 -2
- package/src/react/components/RuntimeBottomNavigationBar.js +141 -146
- package/src/react/components/RuntimeFooterMarqueeText.js +137 -0
- package/src/react/components/RuntimeInfoFooter.js +12 -15
- package/src/react/components/SystemErrorToast.js +49 -29
- package/src/react/components/SystemErrorToast.styles.js +43 -38
- package/src/react/components/UserAvatar.js +120 -28
- package/src/react/config/deviceConfigBootstrap.js +330 -304
- package/src/react/hooks/usePrintSpoolEffects.js +161 -0
- package/src/react/pages/DeviceDetailPage.js +6 -2882
- package/src/react/pages/Devices/detail/DeviceDetailAlertsCommandsSection.js +233 -0
- package/src/react/pages/Devices/detail/DeviceDetailHeader.js +138 -0
- package/src/react/pages/Devices/detail/DeviceDetailMovementSections.js +167 -0
- package/src/react/pages/Devices/detail/DeviceDetailOrdersPrintSection.js +216 -0
- package/src/react/pages/Devices/detail/DeviceDetailPaymentSection.js +44 -0
- package/src/react/pages/Devices/detail/DeviceDetailPdvConfigSection.js +390 -0
- package/src/react/pages/Devices/detail/DeviceDetailRenderers.js +135 -0
- package/src/react/pages/Devices/detail/DeviceDetailScreen.js +495 -0
- package/src/react/pages/Devices/detail/OptionButtonChip.js +65 -0
- package/src/react/pages/Devices/detail/deviceDetailConstants.js +36 -0
- package/src/react/pages/Devices/detail/deviceDetailHelpers.js +70 -0
- package/src/react/pages/Devices/detail/useDeviceDetailActions.js +483 -0
- package/src/react/pages/Devices/detail/useDeviceDetailCopyConfig.js +87 -0
- package/src/react/pages/Devices/detail/useDeviceDetailLoaders.js +327 -0
- package/src/react/pages/Devices/detail/useDeviceDetailSaves.js +429 -0
- package/src/react/pages/Devices/detail/useDeviceDetailStateA.js +489 -0
- package/src/react/pages/Devices/detail/useDeviceDetailStateB.js +98 -0
- package/src/react/pages/Devices/deviceTypes/index.js +2 -0
- package/src/react/pages/Devices/deviceTypes/pdv.js +11 -0
- package/src/react/pages/Imports.js +3 -2
- package/src/react/pages/IntegrationConfigFields.js +126 -0
- package/src/react/pages/IntegrationConfigPage.js +122 -591
- package/src/react/pages/IntegrationConfigPage.utils.js +95 -0
- package/src/react/print/usePrintButtonController.js +9 -1
- package/src/react/services/Getnet/Checkout.js +81 -0
- package/src/react/services/Getnet/Getnet.js +77 -0
- package/src/react/services/paymentGatewayExecution.js +20 -0
- package/src/react/utils/bottomNavigationToolbar.js +161 -0
- package/src/react/utils/commercialDocumentOrders.js +18 -6
- package/src/react/utils/copyDeviceConfigs.js +198 -0
- package/src/react/utils/fileUrl.js +43 -0
- package/src/react/utils/importStatus.js +19 -10
- package/src/react/utils/logSettings.js +7 -7
- package/src/react/utils/maintenanceSettings.js +16 -0
- package/src/react/utils/paymentDevices.js +6 -0
- package/src/react/utils/printManagedPrinter.js +45 -0
- package/src/react/utils/printRouting.js +63 -0
- package/src/react/utils/printSpoolUtils.js +119 -0
- package/src/react/utils/printerDevices.js +368 -368
- package/src/react/utils/runtimeFooter.js +10 -19
- package/src/react/utils/shopFranchises.js +186 -65
- package/src/react/utils/systemErrorMessage.js +71 -67
- package/src/store/device_config/customActions.js +119 -30
- package/src/tests/browser/manager/bottom-navigation-role-toolbar.spec.js +353 -0
- package/src/tests/browser/manager/device-detail-alias-save.spec.js +345 -0
- package/src/tests/browser/manager/device-detail-copy-config.spec.js +365 -0
- package/src/tests/browser/manager/runtime-footer-marquee.spec.js +262 -0
- package/src/tests/browser/manager/theme-confirm-toast.spec.js +236 -0
- package/src/tests/react/components/RuntimeFooterMarqueeText.test.js +171 -0
- package/src/tests/react/components/UserAvatar.test.js +6 -2
- package/src/tests/react/components/themeAwareModals.test.mjs +60 -0
- package/src/tests/react/config/deviceConfigBootstrap.test.js +104 -66
- package/src/tests/react/pages/deviceDetailDelete.test.js +78 -0
- package/src/tests/react/pages/receitaFederalIntegrationContract.test.mjs +48 -0
- package/src/tests/react/utils/bottomNavigationToolbar.test.js +222 -0
- package/src/tests/react/utils/copyDeviceConfigs.test.mjs +127 -0
- package/src/tests/react/utils/fileUrl.test.js +38 -0
- package/src/tests/react/utils/importStatus.test.js +26 -9
- package/src/tests/react/utils/passwordPolicy.test.js +27 -0
- package/src/tests/react/utils/printRouting.test.mjs +86 -0
- package/src/tests/react/utils/printSpoolUtils.test.mjs +52 -0
- package/src/tests/react/utils/runtimeFooter.test.js +7 -0
- package/src/tests/react/utils/shopFranchises.test.js +121 -115
- package/src/tests/react/utils/systemErrorMessage.test.js +32 -59
|
@@ -8,706 +8,25 @@ import {
|
|
|
8
8
|
View,
|
|
9
9
|
} from 'react-native';
|
|
10
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';
|
|
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
22
|
import { resolveStoreConfigByEntity } from '@controleonline/ui-common/src/react/utils/storeColumns';
|
|
23
23
|
import createStyles, {
|
|
24
24
|
buildEntityLogPalette,
|
|
25
25
|
} from './EntityLogContent.styles';
|
|
26
|
+
import {
|
|
27
|
+
EntityLogBranch,
|
|
28
|
+
} from './EntityLogBranch';
|
|
26
29
|
|
|
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
|
-
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
|
-
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
|
-
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
|
-
|
|
370
|
-
const EntityLogBranch = ({
|
|
371
|
-
autoLoad = true,
|
|
372
|
-
entity,
|
|
373
|
-
entityClass,
|
|
374
|
-
entityId,
|
|
375
|
-
entityIri = '',
|
|
376
|
-
entityLabel,
|
|
377
|
-
fetchEntityDetails,
|
|
378
|
-
getEntityState,
|
|
379
|
-
fetchLogs,
|
|
380
|
-
getLogsState,
|
|
381
|
-
onFocusBranch,
|
|
382
|
-
parentOffset = 0,
|
|
383
|
-
relationConfig,
|
|
384
|
-
styles,
|
|
385
|
-
trailKeys,
|
|
386
|
-
}) => {
|
|
387
|
-
const branchKey = useMemo(
|
|
388
|
-
() => buildEntityKey(entityClass, entityId),
|
|
389
|
-
[entityClass, entityId],
|
|
390
|
-
);
|
|
391
|
-
const [expandedRelations, setExpandedRelations] = useState({});
|
|
392
|
-
const [expandedItems, setExpandedItems] = useState({});
|
|
393
|
-
const [branchOffset, setBranchOffset] = useState(parentOffset);
|
|
394
|
-
const [nestedOffsets, setNestedOffsets] = useState({});
|
|
395
|
-
const logsState = getLogsState(entityClass, entityId);
|
|
396
|
-
const detailState = getEntityState(entityClass, entityId, entity);
|
|
397
|
-
const resolvedEntity = useMemo(() => {
|
|
398
|
-
if (!detailState?.item || typeof detailState.item !== 'object') {
|
|
399
|
-
return entity;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (!entity || typeof entity !== 'object') {
|
|
403
|
-
return detailState.item;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Mantem o seed local, mas o detalhe remoto prevalece quando trouxer mais dados.
|
|
407
|
-
return {
|
|
408
|
-
...entity,
|
|
409
|
-
...detailState.item,
|
|
410
|
-
};
|
|
411
|
-
}, [detailState?.item, entity]);
|
|
412
|
-
const isNestedBranch = trailKeys.length > 0;
|
|
413
|
-
const storeConfig = useMemo(
|
|
414
|
-
() =>
|
|
415
|
-
resolveStoreConfigByEntity({
|
|
416
|
-
entity: resolvedEntity,
|
|
417
|
-
entityIri,
|
|
418
|
-
}),
|
|
419
|
-
[entityIri, resolvedEntity],
|
|
420
|
-
);
|
|
421
|
-
const storeColumns = storeConfig?.columns || [];
|
|
422
|
-
const storeName = storeConfig?.storeName || '';
|
|
423
|
-
|
|
424
|
-
const branchTitle = useMemo(
|
|
425
|
-
() =>
|
|
426
|
-
entityLabel ||
|
|
427
|
-
buildEntityLabel({
|
|
428
|
-
entity: resolvedEntity,
|
|
429
|
-
className: entityClass,
|
|
430
|
-
id: entityId,
|
|
431
|
-
}),
|
|
432
|
-
[resolvedEntity, entityClass, entityId, entityLabel],
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
const children = useMemo(
|
|
436
|
-
() =>
|
|
437
|
-
buildEntityChildren(resolvedEntity, {
|
|
438
|
-
relationConfig,
|
|
439
|
-
ancestryKeys: [...trailKeys, branchKey],
|
|
440
|
-
}),
|
|
441
|
-
[branchKey, resolvedEntity, relationConfig, trailKeys],
|
|
442
|
-
);
|
|
443
|
-
|
|
444
|
-
useEffect(() => {
|
|
445
|
-
if (!autoLoad || !entityClass || !entityId) {
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
void fetchLogs({
|
|
450
|
-
className: entityClass,
|
|
451
|
-
rowId: entityId,
|
|
452
|
-
});
|
|
453
|
-
void fetchEntityDetails({
|
|
454
|
-
className: entityClass,
|
|
455
|
-
rowId: entityId,
|
|
456
|
-
entity,
|
|
457
|
-
entityIri,
|
|
458
|
-
});
|
|
459
|
-
}, [
|
|
460
|
-
autoLoad,
|
|
461
|
-
entityClass,
|
|
462
|
-
entityId,
|
|
463
|
-
entity,
|
|
464
|
-
entityIri,
|
|
465
|
-
fetchEntityDetails,
|
|
466
|
-
fetchLogs,
|
|
467
|
-
]);
|
|
468
|
-
|
|
469
|
-
useEffect(() => {
|
|
470
|
-
if (!isNestedBranch || !branchOffset || typeof onFocusBranch !== 'function') {
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Depois de montar um ramo novo, trazemos o topo dele para a viewport.
|
|
475
|
-
const timeoutId = setTimeout(() => {
|
|
476
|
-
onFocusBranch(Math.max(0, branchOffset - 18));
|
|
477
|
-
}, 120);
|
|
478
|
-
|
|
479
|
-
return () => clearTimeout(timeoutId);
|
|
480
|
-
}, [branchOffset, isNestedBranch, onFocusBranch]);
|
|
481
|
-
|
|
482
|
-
const toggleRelation = useCallback(key => {
|
|
483
|
-
setExpandedRelations(prev => ({
|
|
484
|
-
...prev,
|
|
485
|
-
[key]: !prev[key],
|
|
486
|
-
}));
|
|
487
|
-
if (typeof onFocusBranch === 'function') {
|
|
488
|
-
setTimeout(() => {
|
|
489
|
-
onFocusBranch(Math.max(0, branchOffset - 18));
|
|
490
|
-
}, 60);
|
|
491
|
-
}
|
|
492
|
-
}, [branchOffset, onFocusBranch]);
|
|
493
|
-
|
|
494
|
-
const toggleItem = useCallback(key => {
|
|
495
|
-
setExpandedItems(prev => ({
|
|
496
|
-
...prev,
|
|
497
|
-
[key]: !prev[key],
|
|
498
|
-
}));
|
|
499
|
-
if (typeof onFocusBranch === 'function') {
|
|
500
|
-
setTimeout(() => {
|
|
501
|
-
onFocusBranch(Math.max(0, branchOffset - 18));
|
|
502
|
-
}, 60);
|
|
503
|
-
}
|
|
504
|
-
}, [branchOffset, onFocusBranch]);
|
|
505
|
-
|
|
506
|
-
const registerNestedOffset = useCallback((key, layoutY) => {
|
|
507
|
-
setNestedOffsets(prev => {
|
|
508
|
-
const nextOffset = branchOffset + layoutY;
|
|
509
|
-
if (prev[key] === nextOffset) {
|
|
510
|
-
return prev;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
return {
|
|
514
|
-
...prev,
|
|
515
|
-
[key]: nextOffset,
|
|
516
|
-
};
|
|
517
|
-
});
|
|
518
|
-
}, [branchOffset]);
|
|
519
|
-
|
|
520
|
-
return (
|
|
521
|
-
<View
|
|
522
|
-
style={styles.branchCard}
|
|
523
|
-
onLayout={event => {
|
|
524
|
-
setBranchOffset(parentOffset + event.nativeEvent.layout.y);
|
|
525
|
-
}}>
|
|
526
|
-
<View style={styles.branchHeader}>
|
|
527
|
-
<View style={styles.branchHeaderRow}>
|
|
528
|
-
<View style={styles.branchTitleWrap}>
|
|
529
|
-
<Text style={styles.branchTitle}>{branchTitle}</Text>
|
|
530
|
-
<Text style={styles.branchMeta}>
|
|
531
|
-
{entityClass?.split('\\').pop()} #{entityId || '--'}
|
|
532
|
-
</Text>
|
|
533
|
-
</View>
|
|
534
|
-
</View>
|
|
535
|
-
</View>
|
|
536
|
-
|
|
537
|
-
<View style={styles.branchBody}>
|
|
538
|
-
<EntitySummaryCard
|
|
539
|
-
columns={storeColumns}
|
|
540
|
-
detailState={detailState}
|
|
541
|
-
entity={resolvedEntity}
|
|
542
|
-
entityClass={entityClass}
|
|
543
|
-
storeName={storeName}
|
|
544
|
-
styles={styles}
|
|
545
|
-
/>
|
|
546
|
-
|
|
547
|
-
{!!children.length && (
|
|
548
|
-
<View style={styles.relationSection}>
|
|
549
|
-
<Text style={styles.sectionTitle}>Relacionamentos</Text>
|
|
550
|
-
|
|
551
|
-
{children.map(candidate => (
|
|
552
|
-
<View key={`${branchKey}-${candidate.key}`} style={styles.relationCollectionWrap}>
|
|
553
|
-
<TouchableOpacity
|
|
554
|
-
activeOpacity={0.85}
|
|
555
|
-
onPress={() => toggleRelation(candidate.key)}
|
|
556
|
-
style={styles.relationButton}>
|
|
557
|
-
<View style={styles.relationButtonHeader}>
|
|
558
|
-
<Text style={styles.relationButtonLabel}>
|
|
559
|
-
{candidate.label}
|
|
560
|
-
</Text>
|
|
561
|
-
<Icon
|
|
562
|
-
name={expandedRelations[candidate.key] ? 'expand-less' : 'expand-more'}
|
|
563
|
-
size={20}
|
|
564
|
-
color="#64748B"
|
|
565
|
-
/>
|
|
566
|
-
</View>
|
|
567
|
-
<Text style={styles.relationButtonMeta}>
|
|
568
|
-
{candidate.isCollection
|
|
569
|
-
? `${candidate.items.length} item(ns)`
|
|
570
|
-
: candidate.items[0]?.label || 'Ver historico'}
|
|
571
|
-
</Text>
|
|
572
|
-
</TouchableOpacity>
|
|
573
|
-
|
|
574
|
-
{expandedRelations[candidate.key] && (
|
|
575
|
-
<View
|
|
576
|
-
style={styles.nestedWrap}
|
|
577
|
-
onLayout={event =>
|
|
578
|
-
registerNestedOffset(candidate.key, event.nativeEvent.layout.y)
|
|
579
|
-
}>
|
|
580
|
-
{candidate.items.map(item => (
|
|
581
|
-
<View key={item.key} style={styles.relationCollectionWrap}>
|
|
582
|
-
{candidate.isCollection ? (
|
|
583
|
-
<>
|
|
584
|
-
<TouchableOpacity
|
|
585
|
-
activeOpacity={0.85}
|
|
586
|
-
onPress={() => toggleItem(item.key)}
|
|
587
|
-
style={styles.relationButton}>
|
|
588
|
-
<View style={styles.relationButtonHeader}>
|
|
589
|
-
<Text style={styles.relationButtonLabel}>
|
|
590
|
-
{item.label}
|
|
591
|
-
</Text>
|
|
592
|
-
<Icon
|
|
593
|
-
name={expandedItems[item.key] ? 'expand-less' : 'expand-more'}
|
|
594
|
-
size={20}
|
|
595
|
-
color="#64748B"
|
|
596
|
-
/>
|
|
597
|
-
</View>
|
|
598
|
-
<Text style={styles.relationButtonMeta}>
|
|
599
|
-
{item.className?.split('\\').pop()} #{item.id}
|
|
600
|
-
</Text>
|
|
601
|
-
</TouchableOpacity>
|
|
602
|
-
|
|
603
|
-
{expandedItems[item.key] && (
|
|
604
|
-
<View
|
|
605
|
-
style={styles.nestedWrap}
|
|
606
|
-
onLayout={event =>
|
|
607
|
-
registerNestedOffset(item.key, event.nativeEvent.layout.y)
|
|
608
|
-
}>
|
|
609
|
-
<EntityLogBranch
|
|
610
|
-
autoLoad
|
|
611
|
-
entity={item.entity}
|
|
612
|
-
entityClass={item.className}
|
|
613
|
-
entityId={item.id}
|
|
614
|
-
entityIri={item.iri}
|
|
615
|
-
entityLabel={item.label}
|
|
616
|
-
fetchEntityDetails={fetchEntityDetails}
|
|
617
|
-
fetchLogs={fetchLogs}
|
|
618
|
-
getEntityState={getEntityState}
|
|
619
|
-
getLogsState={getLogsState}
|
|
620
|
-
onFocusBranch={onFocusBranch}
|
|
621
|
-
parentOffset={nestedOffsets[item.key] || branchOffset}
|
|
622
|
-
relationConfig={relationConfig}
|
|
623
|
-
styles={styles}
|
|
624
|
-
trailKeys={[...trailKeys, branchKey]}
|
|
625
|
-
/>
|
|
626
|
-
</View>
|
|
627
|
-
)}
|
|
628
|
-
</>
|
|
629
|
-
) : (
|
|
630
|
-
<View
|
|
631
|
-
style={styles.nestedWrap}
|
|
632
|
-
onLayout={event =>
|
|
633
|
-
registerNestedOffset(item.key, event.nativeEvent.layout.y)
|
|
634
|
-
}>
|
|
635
|
-
<EntityLogBranch
|
|
636
|
-
autoLoad
|
|
637
|
-
entity={item.entity}
|
|
638
|
-
entityClass={item.className}
|
|
639
|
-
entityId={item.id}
|
|
640
|
-
entityIri={item.iri}
|
|
641
|
-
entityLabel={item.label}
|
|
642
|
-
fetchEntityDetails={fetchEntityDetails}
|
|
643
|
-
fetchLogs={fetchLogs}
|
|
644
|
-
getEntityState={getEntityState}
|
|
645
|
-
getLogsState={getLogsState}
|
|
646
|
-
onFocusBranch={onFocusBranch}
|
|
647
|
-
parentOffset={nestedOffsets[item.key] || branchOffset}
|
|
648
|
-
relationConfig={relationConfig}
|
|
649
|
-
styles={styles}
|
|
650
|
-
trailKeys={[...trailKeys, branchKey]}
|
|
651
|
-
/>
|
|
652
|
-
</View>
|
|
653
|
-
)}
|
|
654
|
-
</View>
|
|
655
|
-
))}
|
|
656
|
-
</View>
|
|
657
|
-
)}
|
|
658
|
-
</View>
|
|
659
|
-
))}
|
|
660
|
-
</View>
|
|
661
|
-
)}
|
|
662
|
-
|
|
663
|
-
<View style={styles.relationSection}>
|
|
664
|
-
<Text style={styles.sectionTitle}>Timeline</Text>
|
|
665
|
-
|
|
666
|
-
{logsState.status === 'loading' ? (
|
|
667
|
-
<View style={styles.stateBox}>
|
|
668
|
-
<ActivityIndicator size="small" color="#2563EB" />
|
|
669
|
-
<Text style={styles.stateText}>Carregando historico...</Text>
|
|
670
|
-
</View>
|
|
671
|
-
) : null}
|
|
672
|
-
|
|
673
|
-
{logsState.status === 'error' ? (
|
|
674
|
-
<View style={styles.stateBox}>
|
|
675
|
-
<Icon name="error-outline" size={20} color="#DC2626" />
|
|
676
|
-
<Text style={styles.stateTitle}>Nao foi possivel carregar</Text>
|
|
677
|
-
<Text style={styles.stateText}>{logsState.error || 'Erro desconhecido'}</Text>
|
|
678
|
-
</View>
|
|
679
|
-
) : null}
|
|
680
|
-
|
|
681
|
-
{logsState.status === 'success' && !logsState.items.length ? (
|
|
682
|
-
<View style={styles.stateBox}>
|
|
683
|
-
<Icon name="history-toggle-off" size={22} color="#94A3B8" />
|
|
684
|
-
<Text style={styles.stateTitle}>Nenhum log encontrado</Text>
|
|
685
|
-
<Text style={styles.stateText}>
|
|
686
|
-
Ainda nao existem registros para esta entidade.
|
|
687
|
-
</Text>
|
|
688
|
-
</View>
|
|
689
|
-
) : null}
|
|
690
|
-
|
|
691
|
-
{logsState.status === 'success' && !!logsState.items.length ? (
|
|
692
|
-
<View style={styles.logList}>
|
|
693
|
-
{logsState.items.map(log => (
|
|
694
|
-
<LogCard
|
|
695
|
-
key={log?.id || `${branchKey}-${log?.createdAt}`}
|
|
696
|
-
columns={storeColumns}
|
|
697
|
-
log={log}
|
|
698
|
-
rowContext={resolvedEntity}
|
|
699
|
-
storeName={storeName}
|
|
700
|
-
styles={styles}
|
|
701
|
-
/>
|
|
702
|
-
))}
|
|
703
|
-
</View>
|
|
704
|
-
) : null}
|
|
705
|
-
</View>
|
|
706
|
-
</View>
|
|
707
|
-
</View>
|
|
708
|
-
);
|
|
709
|
-
};
|
|
710
|
-
|
|
711
30
|
const EntityLogContent = ({
|
|
712
31
|
entity = null,
|
|
713
32
|
entityClass = '',
|
|
@@ -724,18 +43,18 @@ const EntityLogContent = ({
|
|
|
724
43
|
const logCacheRef = useRef(new Map());
|
|
725
44
|
const entityCacheRef = useRef(new Map());
|
|
726
45
|
const [, setCacheVersion] = useState(0);
|
|
727
|
-
|
|
728
|
-
const rootKey = useMemo(
|
|
729
|
-
() => buildEntityKey(entityClass, entityId),
|
|
730
|
-
[entityClass, entityId],
|
|
731
|
-
);
|
|
732
|
-
|
|
733
|
-
useEffect(() => {
|
|
734
|
-
logCacheRef.current = new Map();
|
|
735
|
-
entityCacheRef.current = new Map();
|
|
736
|
-
setCacheVersion(version => version + 1);
|
|
737
|
-
}, [rootKey]);
|
|
738
|
-
|
|
46
|
+
|
|
47
|
+
const rootKey = useMemo(
|
|
48
|
+
() => buildEntityKey(entityClass, entityId),
|
|
49
|
+
[entityClass, entityId],
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
logCacheRef.current = new Map();
|
|
54
|
+
entityCacheRef.current = new Map();
|
|
55
|
+
setCacheVersion(version => version + 1);
|
|
56
|
+
}, [rootKey]);
|
|
57
|
+
|
|
739
58
|
const getLogsState = useCallback((className, rowId, targetEntityIri = '') => {
|
|
740
59
|
const key = buildEntityKey(className, rowId, targetEntityIri);
|
|
741
60
|
return logCacheRef.current.get(key) || {
|
|
@@ -764,47 +83,47 @@ const EntityLogContent = ({
|
|
|
764
83
|
if (!key) {
|
|
765
84
|
return;
|
|
766
85
|
}
|
|
767
|
-
|
|
768
|
-
const current = logCacheRef.current.get(key);
|
|
769
|
-
if (current?.status === 'loading' || current?.status === 'success') {
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
logCacheRef.current.set(key, {
|
|
774
|
-
error: '',
|
|
775
|
-
items: current?.items || [],
|
|
776
|
-
status: 'loading',
|
|
777
|
-
totalItems: current?.totalItems || 0,
|
|
778
|
-
});
|
|
779
|
-
setCacheVersion(version => version + 1);
|
|
780
|
-
|
|
86
|
+
|
|
87
|
+
const current = logCacheRef.current.get(key);
|
|
88
|
+
if (current?.status === 'loading' || current?.status === 'success') {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
logCacheRef.current.set(key, {
|
|
93
|
+
error: '',
|
|
94
|
+
items: current?.items || [],
|
|
95
|
+
status: 'loading',
|
|
96
|
+
totalItems: current?.totalItems || 0,
|
|
97
|
+
});
|
|
98
|
+
setCacheVersion(version => version + 1);
|
|
99
|
+
|
|
781
100
|
try {
|
|
782
101
|
const response = await entityLogActions.getTimeline({
|
|
783
102
|
...(className ? { class: className } : {}),
|
|
784
103
|
...(targetEntityIri ? { entity: targetEntityIri } : {}),
|
|
785
104
|
row: rowId,
|
|
786
105
|
});
|
|
787
|
-
const items = Array.isArray(response?.items) ? response.items : [];
|
|
788
|
-
const totalItems = Number(response?.totalItems || items.length || 0);
|
|
789
|
-
|
|
790
|
-
logCacheRef.current.set(key, {
|
|
791
|
-
error: '',
|
|
792
|
-
items,
|
|
793
|
-
status: 'success',
|
|
794
|
-
totalItems,
|
|
795
|
-
});
|
|
796
|
-
setCacheVersion(version => version + 1);
|
|
797
|
-
} catch (error) {
|
|
798
|
-
logCacheRef.current.set(key, {
|
|
799
|
-
error: error?.message || 'Erro ao buscar logs.',
|
|
800
|
-
items: [],
|
|
801
|
-
status: 'error',
|
|
802
|
-
totalItems: 0,
|
|
803
|
-
});
|
|
804
|
-
setCacheVersion(version => version + 1);
|
|
805
|
-
}
|
|
106
|
+
const items = Array.isArray(response?.items) ? response.items : [];
|
|
107
|
+
const totalItems = Number(response?.totalItems || items.length || 0);
|
|
108
|
+
|
|
109
|
+
logCacheRef.current.set(key, {
|
|
110
|
+
error: '',
|
|
111
|
+
items,
|
|
112
|
+
status: 'success',
|
|
113
|
+
totalItems,
|
|
114
|
+
});
|
|
115
|
+
setCacheVersion(version => version + 1);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
logCacheRef.current.set(key, {
|
|
118
|
+
error: error?.message || 'Erro ao buscar logs.',
|
|
119
|
+
items: [],
|
|
120
|
+
status: 'error',
|
|
121
|
+
totalItems: 0,
|
|
122
|
+
});
|
|
123
|
+
setCacheVersion(version => version + 1);
|
|
124
|
+
}
|
|
806
125
|
}, [entityLogActions]);
|
|
807
|
-
|
|
126
|
+
|
|
808
127
|
const fetchEntityDetails = useCallback(async ({
|
|
809
128
|
className,
|
|
810
129
|
rowId,
|
|
@@ -819,61 +138,61 @@ const EntityLogContent = ({
|
|
|
819
138
|
if (!key) {
|
|
820
139
|
return;
|
|
821
140
|
}
|
|
822
|
-
|
|
823
|
-
const current = entityCacheRef.current.get(key);
|
|
824
|
-
if (current?.status === 'loading' || current?.status === 'success') {
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
const resolvedEntityIri =
|
|
829
|
-
(typeof seedEntity?.['@id'] === 'string' && seedEntity['@id'].trim()) ||
|
|
830
|
-
(typeof explicitEntityIri === 'string' ? explicitEntityIri.trim() : '');
|
|
831
|
-
|
|
832
|
-
if (!resolvedEntityIri) {
|
|
833
|
-
entityCacheRef.current.set(key, {
|
|
834
|
-
error: 'Entidade sem identificador para consulta.',
|
|
835
|
-
item: seedEntity,
|
|
836
|
-
status: 'error',
|
|
837
|
-
});
|
|
838
|
-
setCacheVersion(version => version + 1);
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
// O cache evita recarregar o mesmo detalhe toda vez que o usuario expande o ramo.
|
|
843
|
-
entityCacheRef.current.set(key, {
|
|
844
|
-
error: '',
|
|
845
|
-
item: current?.item || seedEntity,
|
|
846
|
-
status: 'loading',
|
|
847
|
-
});
|
|
848
|
-
setCacheVersion(version => version + 1);
|
|
849
|
-
|
|
850
|
-
try {
|
|
851
|
-
const item = await entityLogActions.getEntityDetail(resolvedEntityIri);
|
|
852
|
-
|
|
853
|
-
entityCacheRef.current.set(key, {
|
|
854
|
-
error: '',
|
|
855
|
-
item,
|
|
856
|
-
status: 'success',
|
|
857
|
-
});
|
|
858
|
-
setCacheVersion(version => version + 1);
|
|
859
|
-
} catch (error) {
|
|
860
|
-
entityCacheRef.current.set(key, {
|
|
861
|
-
error: error?.message || 'Erro ao buscar detalhes.',
|
|
862
|
-
item: current?.item || seedEntity,
|
|
863
|
-
status: 'error',
|
|
864
|
-
});
|
|
865
|
-
setCacheVersion(version => version + 1);
|
|
866
|
-
}
|
|
867
|
-
}, [entityLogActions]);
|
|
868
|
-
|
|
141
|
+
|
|
142
|
+
const current = entityCacheRef.current.get(key);
|
|
143
|
+
if (current?.status === 'loading' || current?.status === 'success') {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const resolvedEntityIri =
|
|
148
|
+
(typeof seedEntity?.['@id'] === 'string' && seedEntity['@id'].trim()) ||
|
|
149
|
+
(typeof explicitEntityIri === 'string' ? explicitEntityIri.trim() : '');
|
|
150
|
+
|
|
151
|
+
if (!resolvedEntityIri) {
|
|
152
|
+
entityCacheRef.current.set(key, {
|
|
153
|
+
error: 'Entidade sem identificador para consulta.',
|
|
154
|
+
item: seedEntity,
|
|
155
|
+
status: 'error',
|
|
156
|
+
});
|
|
157
|
+
setCacheVersion(version => version + 1);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// O cache evita recarregar o mesmo detalhe toda vez que o usuario expande o ramo.
|
|
162
|
+
entityCacheRef.current.set(key, {
|
|
163
|
+
error: '',
|
|
164
|
+
item: current?.item || seedEntity,
|
|
165
|
+
status: 'loading',
|
|
166
|
+
});
|
|
167
|
+
setCacheVersion(version => version + 1);
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const item = await entityLogActions.getEntityDetail(resolvedEntityIri);
|
|
171
|
+
|
|
172
|
+
entityCacheRef.current.set(key, {
|
|
173
|
+
error: '',
|
|
174
|
+
item,
|
|
175
|
+
status: 'success',
|
|
176
|
+
});
|
|
177
|
+
setCacheVersion(version => version + 1);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
entityCacheRef.current.set(key, {
|
|
180
|
+
error: error?.message || 'Erro ao buscar detalhes.',
|
|
181
|
+
item: current?.item || seedEntity,
|
|
182
|
+
status: 'error',
|
|
183
|
+
});
|
|
184
|
+
setCacheVersion(version => version + 1);
|
|
185
|
+
}
|
|
186
|
+
}, [entityLogActions]);
|
|
187
|
+
|
|
869
188
|
const resolvedEntityLabel = useMemo(
|
|
870
189
|
() =>
|
|
871
190
|
entityLabel ||
|
|
872
|
-
buildEntityLabel({
|
|
873
|
-
entity,
|
|
874
|
-
className: entityClass,
|
|
875
|
-
id: entityId,
|
|
876
|
-
}),
|
|
191
|
+
buildEntityLabel({
|
|
192
|
+
entity,
|
|
193
|
+
className: entityClass,
|
|
194
|
+
id: entityId,
|
|
195
|
+
}),
|
|
877
196
|
[entity, entityClass, entityId, entityLabel],
|
|
878
197
|
);
|
|
879
198
|
|
|
@@ -905,5 +224,5 @@ const EntityLogContent = ({
|
|
|
905
224
|
</ScrollView>
|
|
906
225
|
);
|
|
907
226
|
};
|
|
908
|
-
|
|
227
|
+
|
|
909
228
|
export default EntityLogContent;
|