@controleonline/ui-common 1.2.70

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 (203) hide show
  1. package/.github/agents/developer.agent.md +30 -0
  2. package/.github/agents/devops.agent.md +30 -0
  3. package/.github/agents/qa.agent.md +30 -0
  4. package/.github/agents/security.agent.md +30 -0
  5. package/.github/workflows/npmjs.yml +35 -0
  6. package/.github/workflows/pull-request-checks.yml +19 -0
  7. package/.scrutinizer.yml +24 -0
  8. package/FUNDING.yml +1 -0
  9. package/package.json +28 -0
  10. package/src/api/fetch.js +74 -0
  11. package/src/api/index.js +155 -0
  12. package/src/api/localDB.js +131 -0
  13. package/src/api/queue.js +66 -0
  14. package/src/react/assets/tap.mp3 +0 -0
  15. package/src/react/components/AddImportModal.js +157 -0
  16. package/src/react/components/AddImportModal.styles.js +81 -0
  17. package/src/react/components/BackgroundRuntimeBridge.js +219 -0
  18. package/src/react/components/BottomNavigationBar.config.js +238 -0
  19. package/src/react/components/BottomNavigationBar.js +120 -0
  20. package/src/react/components/BottomNavigationBar.styles.js +110 -0
  21. package/src/react/components/CategoryForm.js +572 -0
  22. package/src/react/components/CategoryForm.styles.js +334 -0
  23. package/src/react/components/ConfirmModal.js +24 -0
  24. package/src/react/components/ConfirmModal.styles.js +43 -0
  25. package/src/react/components/ContextHelpButton.js +69 -0
  26. package/src/react/components/ContextHelpButton.styles.js +62 -0
  27. package/src/react/components/DefaultProvider.native.js +848 -0
  28. package/src/react/components/DefaultProvider.styles.js +21 -0
  29. package/src/react/components/DefaultProvider.web.js +909 -0
  30. package/src/react/components/DeliveryPushBridge.native.js +314 -0
  31. package/src/react/components/DeliveryPushBridge.web.js +5 -0
  32. package/src/react/components/DeviceAlertSoundService.js +499 -0
  33. package/src/react/components/EntityLogContent.js +909 -0
  34. package/src/react/components/EntityLogContent.styles.js +400 -0
  35. package/src/react/components/EventBus.js +12 -0
  36. package/src/react/components/KioskModeBridge.js +65 -0
  37. package/src/react/components/LauncherModeBridge.js +47 -0
  38. package/src/react/components/LinkedOrderProductsTab.js +388 -0
  39. package/src/react/components/LinkedOrderProductsTab.styles.js +180 -0
  40. package/src/react/components/ManagerPushBridge.native.js +345 -0
  41. package/src/react/components/MessageService.js +314 -0
  42. package/src/react/components/Paywall/Google.js +89 -0
  43. package/src/react/components/PrintService.js +778 -0
  44. package/src/react/components/ProductCatalogCacheService.js +45 -0
  45. package/src/react/components/RemoteCheckoutService.js +292 -0
  46. package/src/react/components/RuntimeBottomNavigationBar.js +151 -0
  47. package/src/react/components/RuntimeInfoFooter.js +214 -0
  48. package/src/react/components/RuntimeInfoFooter.styles.js +56 -0
  49. package/src/react/components/SystemErrorToast.js +29 -0
  50. package/src/react/components/SystemErrorToast.styles.js +38 -0
  51. package/src/react/components/TouchFeedbackProvider.js +65 -0
  52. package/src/react/components/UnifiedPaymentBar.js +155 -0
  53. package/src/react/components/UnifiedPaymentBar.styles.js +174 -0
  54. package/src/react/components/WebsocketListener.native.js +487 -0
  55. package/src/react/components/WebsocketListener.web.js +102 -0
  56. package/src/react/components/inputs/IdInput.js +49 -0
  57. package/src/react/components/inputs/IdInput.styles.js +26 -0
  58. package/src/react/components/inputs/InvoiceDueDateFilterInput.js +74 -0
  59. package/src/react/components/inputs/InvoiceDueDateFilterInput.styles.js +34 -0
  60. package/src/react/components/inputs/InvoiceDueDateRangeFilterInput.js +115 -0
  61. package/src/react/components/inputs/InvoiceDueDateRangeFilterInput.styles.js +39 -0
  62. package/src/react/components/lists/CategoriesList.js +56 -0
  63. package/src/react/components/lists/PaymentTypeList.js +55 -0
  64. package/src/react/components/lists/ReceiverList.js +56 -0
  65. package/src/react/components/lists/SelectList.styles.js +18 -0
  66. package/src/react/components/lists/StatusList.js +55 -0
  67. package/src/react/components/lists/WalletList.js +54 -0
  68. package/src/react/components/radio/Spotify.js +144 -0
  69. package/src/react/components/radio/Spotify.styles.js +32 -0
  70. package/src/react/components/radio/YouTube.js +82 -0
  71. package/src/react/components/radio/YouTube.styles.js +16 -0
  72. package/src/react/components/toastConfig.js +16 -0
  73. package/src/react/config/deviceConfigBootstrap.js +571 -0
  74. package/src/react/index.js +4 -0
  75. package/src/react/localStorage.js +30 -0
  76. package/src/react/pages/EntityLogPage.js +124 -0
  77. package/src/react/pages/EntityLogPage.styles.js +17 -0
  78. package/src/react/pages/GenericLogPage.js +760 -0
  79. package/src/react/pages/GenericLogPage.styles.js +401 -0
  80. package/src/react/pages/Imports.js +361 -0
  81. package/src/react/pages/Imports.styles.js +223 -0
  82. package/src/react/pages/SettingsPage/PaymentTypesByWalletTab.js +462 -0
  83. package/src/react/pages/SettingsPage/index.js +1171 -0
  84. package/src/react/pages/SettingsPage/index.styles.js +122 -0
  85. package/src/react/print/jobs.js +147 -0
  86. package/src/react/print/providers/local.js +53 -0
  87. package/src/react/print/providers/remote.js +81 -0
  88. package/src/react/print/selection.js +296 -0
  89. package/src/react/print/usePrintButtonController.js +474 -0
  90. package/src/react/router/routes.js +42 -0
  91. package/src/react/services/NetworkPrinterService.native.js +391 -0
  92. package/src/react/services/NetworkPrinterService.web.js +15 -0
  93. package/src/react/stores/index.js +128 -0
  94. package/src/react/stores/storeErrorBridge.js +36 -0
  95. package/src/react/utils/backgroundRuntimeRegistration.js +69 -0
  96. package/src/react/utils/cashPayment.js +87 -0
  97. package/src/react/utils/commercialDocumentOrders.js +269 -0
  98. package/src/react/utils/dateRangeFilter.js +264 -0
  99. package/src/react/utils/deviceRuntime.js +193 -0
  100. package/src/react/utils/entityDisplay.js +231 -0
  101. package/src/react/utils/entityLog.js +649 -0
  102. package/src/react/utils/fileUrl.js +102 -0
  103. package/src/react/utils/frontendDebugLog.js +45 -0
  104. package/src/react/utils/googleMapsConfig.js +49 -0
  105. package/src/react/utils/importStatus.js +100 -0
  106. package/src/react/utils/invoiceDueDateMessages.js +14 -0
  107. package/src/react/utils/invoicePresentation.js +46 -0
  108. package/src/react/utils/logSettings.js +143 -0
  109. package/src/react/utils/maintenanceSettings.js +53 -0
  110. package/src/react/utils/managerOrderNotifications.js +523 -0
  111. package/src/react/utils/mapNavigation.js +76 -0
  112. package/src/react/utils/menuCatalogConfig.js +8 -0
  113. package/src/react/utils/menuCatalogDownload.js +157 -0
  114. package/src/react/utils/networkCameraDevices.js +175 -0
  115. package/src/react/utils/networkDeviceProfiles.js +135 -0
  116. package/src/react/utils/normalizedCatalogDownload.js +157 -0
  117. package/src/react/utils/notificationNavigation.js +94 -0
  118. package/src/react/utils/notificationSound.js +42 -0
  119. package/src/react/utils/paymentDevices.js +397 -0
  120. package/src/react/utils/paymentGatewayExecution.js +91 -0
  121. package/src/react/utils/paymentOptions.js +88 -0
  122. package/src/react/utils/peopleDisplay.js +39 -0
  123. package/src/react/utils/printerDevices.js +465 -0
  124. package/src/react/utils/remotePayment.js +61 -0
  125. package/src/react/utils/runtimeFooter.js +475 -0
  126. package/src/react/utils/runtimeLanguage.js +50 -0
  127. package/src/react/utils/runtimeMenu.js +224 -0
  128. package/src/react/utils/screenMetrics.js +30 -0
  129. package/src/react/utils/shopConfig.js +294 -0
  130. package/src/react/utils/shopFranchises.js +114 -0
  131. package/src/react/utils/socketRuntimePipeline.js +454 -0
  132. package/src/react/utils/spoolAckTracker.js +77 -0
  133. package/src/react/utils/storeColumns.js +427 -0
  134. package/src/react/utils/systemErrorChannel.js +109 -0
  135. package/src/react/utils/systemErrorMessage.js +67 -0
  136. package/src/react/utils/toastPresentation.js +53 -0
  137. package/src/react/utils/websocketSharedRuntime.js +826 -0
  138. package/src/store/acl/getters.js +4 -0
  139. package/src/store/acl/index.js +19 -0
  140. package/src/store/acl/mutation_types.js +4 -0
  141. package/src/store/acl/mutations.js +17 -0
  142. package/src/store/address/city.js +22 -0
  143. package/src/store/categories/category_file/index.js +25 -0
  144. package/src/store/categories/index.js +93 -0
  145. package/src/store/configs/customActions.js +131 -0
  146. package/src/store/configs/index.js +25 -0
  147. package/src/store/connections/customActions.js +26 -0
  148. package/src/store/connections/index.js +124 -0
  149. package/src/store/device/index.js +22 -0
  150. package/src/store/device_config/customActions.js +190 -0
  151. package/src/store/device_config/index.js +28 -0
  152. package/src/store/entity_log/customActions.js +63 -0
  153. package/src/store/entity_log/index.js +25 -0
  154. package/src/store/extra/data.js +85 -0
  155. package/src/store/extra/fields.js +88 -0
  156. package/src/store/file/actions.js +27 -0
  157. package/src/store/file/index.js +24 -0
  158. package/src/store/imports/index.js +88 -0
  159. package/src/store/print/customActions.js +251 -0
  160. package/src/store/print/index.js +28 -0
  161. package/src/store/printer/customActions.js +54 -0
  162. package/src/store/printer/index.js +26 -0
  163. package/src/store/runtime_debug/customActions.js +71 -0
  164. package/src/store/runtime_debug/index.js +30 -0
  165. package/src/store/status/index.js +65 -0
  166. package/src/store/timezones/index.js +43 -0
  167. package/src/store/websocket/customActions.js +24 -0
  168. package/src/store/websocket/index.js +28 -0
  169. package/src/tests/react/components/LauncherModeBridge.test.js +93 -0
  170. package/src/tests/react/config/deviceConfigBootstrap.test.js +337 -0
  171. package/src/tests/react/print/localPrintProvider.test.js +45 -0
  172. package/src/tests/react/utils/backgroundRuntimeRegistration.test.js +123 -0
  173. package/src/tests/react/utils/cashPayment.test.js +41 -0
  174. package/src/tests/react/utils/dateRangeFilter.test.mjs +29 -0
  175. package/src/tests/react/utils/entityDisplay.test.js +18 -0
  176. package/src/tests/react/utils/googleMapsConfig.test.js +32 -0
  177. package/src/tests/react/utils/importStatus.test.js +85 -0
  178. package/src/tests/react/utils/invoicePresentation.test.mjs +40 -0
  179. package/src/tests/react/utils/managerOrderNotifications.test.js +127 -0
  180. package/src/tests/react/utils/mapNavigation.test.js +58 -0
  181. package/src/tests/react/utils/notificationNavigation.test.js +39 -0
  182. package/src/tests/react/utils/notificationSound.test.js +27 -0
  183. package/src/tests/react/utils/paymentDevices.test.js +88 -0
  184. package/src/tests/react/utils/runtimeFooter.test.js +429 -0
  185. package/src/tests/react/utils/runtimeLanguage.test.js +80 -0
  186. package/src/tests/react/utils/runtimeMenu.test.js +174 -0
  187. package/src/tests/react/utils/shopFranchises.test.js +82 -0
  188. package/src/tests/react/utils/socketRuntimePipeline.test.js +124 -0
  189. package/src/tests/react/utils/spoolAckTracker.test.js +40 -0
  190. package/src/tests/react/utils/storeErrorBridge.test.js +66 -0
  191. package/src/tests/react/utils/systemErrorChannel.test.js +58 -0
  192. package/src/tests/react/utils/systemErrorMessage.test.js +59 -0
  193. package/src/tests/react/utils/toastPresentation.test.js +75 -0
  194. package/src/tests/react/utils/translate.test.mjs +396 -0
  195. package/src/tests/react/utils/websocketSharedRuntime.test.js +245 -0
  196. package/src/utils/apiEntryPoint.js +31 -0
  197. package/src/utils/appDomain.js +50 -0
  198. package/src/utils/config.js +14 -0
  199. package/src/utils/formatter.js +419 -0
  200. package/src/utils/integrationConfigs.js +79 -0
  201. package/src/utils/methods.js +23 -0
  202. package/src/utils/oauth.js +42 -0
  203. package/src/utils/translate.js +620 -0
@@ -0,0 +1,649 @@
1
+ import Formatter from '@controleonline/ui-common/src/utils/formatter';
2
+ import {
3
+ buildAddressOptionSummary,
4
+ formatHumanLabel,
5
+ normalizeText,
6
+ } from '@controleonline/ui-common/src/react/utils/entityDisplay';
7
+ import { resolveFileImageUrl } from '@controleonline/ui-common/src/react/utils/fileUrl';
8
+ import {
9
+ formatStoreColumnLabel,
10
+ formatStoreColumnValue,
11
+ } from '@controleonline/ui-common/src/react/utils/storeColumns';
12
+
13
+ const IGNORED_KEYS = new Set([
14
+ '@context',
15
+ '@type',
16
+ 'extraData',
17
+ 'otherInformations',
18
+ ]);
19
+
20
+ const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2})?)?/;
21
+ const MONEY_FIELD_PATTERN = /(?:^|_)(?:price|total|amount|value|cost|fee|discount)(?:$|_)/i;
22
+
23
+ export const extractEntityId = entity => {
24
+ if (!entity) return null;
25
+
26
+ if (typeof entity === 'number' || typeof entity === 'string') {
27
+ const matches = String(entity).match(/\d+/g);
28
+ return matches ? Number(matches[matches.length - 1]) : null;
29
+ }
30
+
31
+ if (typeof entity === 'object') {
32
+ if (entity.id !== undefined && entity.id !== null && entity.id !== '') {
33
+ return Number(entity.id);
34
+ }
35
+
36
+ if (entity['@id']) {
37
+ const matches = String(entity['@id']).match(/\d+/g);
38
+ return matches ? Number(matches[matches.length - 1]) : null;
39
+ }
40
+ }
41
+
42
+ return null;
43
+ };
44
+
45
+ export const resolveEntityClassFromType = type => {
46
+ const normalizedType = String(type || '').trim().replace(/^#/, '');
47
+ if (!normalizedType) {
48
+ return '';
49
+ }
50
+
51
+ if (normalizedType.includes('\\')) {
52
+ return normalizedType;
53
+ }
54
+
55
+ return `ControleOnline\\Entity\\${normalizedType}`;
56
+ };
57
+
58
+ export const buildEntityIri = ({ entity, iri = '' }) => {
59
+ if (typeof entity?.['@id'] === 'string' && entity['@id'].trim()) {
60
+ return entity['@id'].trim();
61
+ }
62
+
63
+ return typeof iri === 'string' ? iri.trim() : '';
64
+ };
65
+
66
+ const getShortClassName = className => {
67
+ const normalized = String(className || '').replace(/^Entity:/, '').trim();
68
+ if (!normalized) {
69
+ return '';
70
+ }
71
+
72
+ const parts = normalized.split('\\').filter(Boolean);
73
+ return parts[parts.length - 1] || normalized;
74
+ };
75
+
76
+ const normalizeNumericValue = value => {
77
+ if (value === null || value === undefined || value === '') {
78
+ return null;
79
+ }
80
+
81
+ if (typeof value === 'number') {
82
+ return Number.isFinite(value) ? value : null;
83
+ }
84
+
85
+ const normalized = parseFloat(String(value).replace(/\./g, '').replace(',', '.'));
86
+ return Number.isFinite(normalized) ? normalized : null;
87
+ };
88
+
89
+ const isImageFile = fileEntity =>
90
+ /image/i.test(
91
+ String(fileEntity?.fileType || fileEntity?.type || fileEntity?.mimeType || ''),
92
+ );
93
+
94
+ const resolveCoverRelation = (relations = [], relationId = null) => {
95
+ const normalizedRelationId = extractEntityId(relationId);
96
+ const safeRelations = Array.isArray(relations) ? relations.filter(Boolean) : [];
97
+
98
+ if (normalizedRelationId) {
99
+ const matchedRelation = safeRelations.find(
100
+ relation => extractEntityId(relation) === normalizedRelationId,
101
+ );
102
+ if (matchedRelation) {
103
+ return matchedRelation;
104
+ }
105
+ }
106
+
107
+ return safeRelations.find(relation => isImageFile(relation?.file || relation))
108
+ || safeRelations[0]
109
+ || null;
110
+ };
111
+
112
+ const resolveEntityFileCandidate = entity => {
113
+ if (!entity || typeof entity !== 'object') {
114
+ return null;
115
+ }
116
+
117
+ if (isImageFile(entity) || entity?.context || entity?.extension) {
118
+ return entity;
119
+ }
120
+
121
+ if (entity?.file) {
122
+ return entity.file;
123
+ }
124
+
125
+ if (entity?.logo) {
126
+ return entity.logo;
127
+ }
128
+
129
+ if (entity?.avatar) {
130
+ return entity.avatar;
131
+ }
132
+
133
+ if (entity?.background) {
134
+ return entity.background;
135
+ }
136
+
137
+ const fileRelations = [
138
+ ...((Array.isArray(entity?.productFiles) ? entity.productFiles : [])),
139
+ ...((Array.isArray(entity?.categoryFiles) ? entity.categoryFiles : [])),
140
+ ...((Array.isArray(entity?.files) ? entity.files : [])),
141
+ ...((Array.isArray(entity?.attachments) ? entity.attachments : [])),
142
+ ...((Array.isArray(entity?.images) ? entity.images : [])),
143
+ ];
144
+
145
+ return resolveCoverRelation(fileRelations, entity?.extraData?.imageCoverRelationId)?.file
146
+ || null;
147
+ };
148
+
149
+ export const buildEntityKey = (className, id) =>
150
+ className && id ? `${className}#${id}` : '';
151
+
152
+ export const parseEntityReference = value => {
153
+ if (typeof value !== 'string') {
154
+ return null;
155
+ }
156
+
157
+ const normalized = value.trim();
158
+ if (!normalized) {
159
+ return null;
160
+ }
161
+
162
+ const matches = normalized.match(/^(?:Entity:)?([A-Za-z0-9_\\]+)#(\d+)$/);
163
+ if (!matches) {
164
+ return null;
165
+ }
166
+
167
+ return {
168
+ className: matches[1],
169
+ id: Number(matches[2]),
170
+ key: buildEntityKey(matches[1], Number(matches[2])),
171
+ };
172
+ };
173
+
174
+ export const buildEntityLabel = ({
175
+ entity,
176
+ className,
177
+ id,
178
+ fallbackLabel = '',
179
+ }) => {
180
+ const addressSummary =
181
+ entity && typeof entity === 'object' ? buildAddressOptionSummary(entity) : null;
182
+
183
+ const statusText = normalizeText(
184
+ entity?.status?.status ||
185
+ entity?.status?.realStatus ||
186
+ entity?.realStatus ||
187
+ entity?.real_status ||
188
+ entity?.status,
189
+ );
190
+
191
+ const preferredText = normalizeText(
192
+ entity?.alias ||
193
+ entity?.name ||
194
+ entity?.nickname ||
195
+ entity?.label ||
196
+ entity?.title ||
197
+ entity?.display ||
198
+ entity?.document ||
199
+ entity?.username ||
200
+ entity?.device ||
201
+ entity?.email ||
202
+ entity?.city ||
203
+ entity?.district ||
204
+ entity?.street ||
205
+ entity?.measure ||
206
+ entity?.model ||
207
+ entity?.context ||
208
+ statusText ||
209
+ addressSummary?.primary ||
210
+ fallbackLabel,
211
+ );
212
+
213
+ if (preferredText) {
214
+ return preferredText;
215
+ }
216
+
217
+ const shortClassName = formatHumanLabel(getShortClassName(className));
218
+ if (shortClassName && id) {
219
+ return `${shortClassName} #${id}`;
220
+ }
221
+
222
+ return shortClassName || `#${id || '--'}`;
223
+ };
224
+
225
+ const humanizeFieldLabel = fieldName =>
226
+ formatHumanLabel(
227
+ String(fieldName || '')
228
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
229
+ .replace(/[_-]+/g, ' ')
230
+ .trim(),
231
+ ) || 'Campo';
232
+
233
+ const normalizeDisplayValue = value => {
234
+ if (typeof value === 'boolean') {
235
+ return value ? 'Sim' : 'Nao';
236
+ }
237
+
238
+ if (typeof value === 'number') {
239
+ return String(value);
240
+ }
241
+
242
+ if (typeof value === 'object' && value && !Array.isArray(value)) {
243
+ return buildEntityLabel({entity: value});
244
+ }
245
+
246
+ return String(value);
247
+ };
248
+
249
+ export const formatLogFieldLabel = (fieldName, options = {}) =>
250
+ formatStoreColumnLabel({
251
+ columns: options?.columns,
252
+ fallbackLabel: humanizeFieldLabel(fieldName),
253
+ fieldName,
254
+ storeName: options?.storeName,
255
+ }) || humanizeFieldLabel(fieldName);
256
+
257
+ export const formatLogValue = (value, options = {}) => {
258
+ if (value === null || value === undefined || value === '') {
259
+ return '—';
260
+ }
261
+
262
+ if (options?.fieldName && !Array.isArray(value)) {
263
+ const formattedColumnValue = formatStoreColumnValue({
264
+ columns: options?.columns,
265
+ fieldName: options.fieldName,
266
+ row: options?.row,
267
+ storeName: options?.storeName,
268
+ value,
269
+ });
270
+
271
+ if (
272
+ formattedColumnValue !== undefined &&
273
+ formattedColumnValue !== null &&
274
+ formattedColumnValue !== ''
275
+ ) {
276
+ return normalizeDisplayValue(formattedColumnValue);
277
+ }
278
+ }
279
+
280
+ if (typeof value === 'boolean') {
281
+ return value ? 'Sim' : 'Nao';
282
+ }
283
+
284
+ if (typeof value === 'number') {
285
+ return String(value);
286
+ }
287
+
288
+ if (typeof value === 'string') {
289
+ const entityReference = parseEntityReference(value);
290
+ if (entityReference) {
291
+ return `${formatHumanLabel(getShortClassName(entityReference.className))} #${entityReference.id}`;
292
+ }
293
+
294
+ if (DATE_PATTERN.test(value)) {
295
+ return Formatter.formatDateYmdTodmY(value, value.includes(':'));
296
+ }
297
+
298
+ return value;
299
+ }
300
+
301
+ if (Array.isArray(value)) {
302
+ const normalizedValues = value
303
+ .map(item => formatLogValue(item))
304
+ .filter(Boolean);
305
+
306
+ return normalizedValues.length ? normalizedValues.join(', ') : '—';
307
+ }
308
+
309
+ if (typeof value === 'object') {
310
+ return buildEntityLabel({ entity: value });
311
+ }
312
+
313
+ return normalizeDisplayValue(value);
314
+ };
315
+
316
+ const pushSummaryField = (fields, label, formattedValue) => {
317
+ if (formattedValue === null || formattedValue === undefined || formattedValue === '') {
318
+ return;
319
+ }
320
+
321
+ if (!formattedValue || formattedValue === '—') {
322
+ return;
323
+ }
324
+
325
+ if (fields.some(field => field.label === label && field.value === formattedValue)) {
326
+ return;
327
+ }
328
+
329
+ fields.push({
330
+ key: `${label}-${fields.length}`,
331
+ label,
332
+ value: formattedValue,
333
+ });
334
+ };
335
+
336
+ const appendSummaryField = (fields, label, rawValue, options = {}) => {
337
+ if (rawValue === null || rawValue === undefined || rawValue === '') {
338
+ return;
339
+ }
340
+
341
+ const formattedValue = options.money
342
+ ? Formatter.formatMoney(normalizeNumericValue(rawValue) || 0)
343
+ : formatLogValue(rawValue);
344
+
345
+ pushSummaryField(fields, label, formattedValue);
346
+ };
347
+
348
+ const resolveFieldValue = (entity, fieldName) => {
349
+ if (!entity || typeof entity !== 'object' || !fieldName) {
350
+ return undefined;
351
+ }
352
+
353
+ if (Object.prototype.hasOwnProperty.call(entity, fieldName)) {
354
+ return entity[fieldName];
355
+ }
356
+
357
+ if (!String(fieldName).includes('.')) {
358
+ return undefined;
359
+ }
360
+
361
+ return String(fieldName)
362
+ .split('.')
363
+ .filter(Boolean)
364
+ .reduce((currentValue, part) => {
365
+ if (!currentValue || typeof currentValue !== 'object') {
366
+ return undefined;
367
+ }
368
+
369
+ return currentValue[part];
370
+ }, entity);
371
+ };
372
+
373
+ export const buildEntitySummaryFields = ({
374
+ entity,
375
+ className,
376
+ columns = [],
377
+ storeName = '',
378
+ }) => {
379
+ if (!entity || typeof entity !== 'object') {
380
+ return [];
381
+ }
382
+
383
+ const fields = [];
384
+ const shortClassName = getShortClassName(className);
385
+ const safeColumns = Array.isArray(columns) ? columns.filter(Boolean) : [];
386
+
387
+ safeColumns.forEach(column => {
388
+ if (fields.length >= 6) {
389
+ return;
390
+ }
391
+
392
+ const fieldName = column?.key || column?.name;
393
+ if (
394
+ !fieldName ||
395
+ IGNORED_KEYS.has(fieldName) ||
396
+ fieldName === 'id' ||
397
+ fieldName === 'extraData'
398
+ ) {
399
+ return;
400
+ }
401
+
402
+ const rawValue = resolveFieldValue(entity, fieldName);
403
+ if (rawValue === null || rawValue === undefined || rawValue === '') {
404
+ return;
405
+ }
406
+
407
+ const formattedValue = formatLogValue(rawValue, {
408
+ columns: safeColumns,
409
+ fieldName,
410
+ row: entity,
411
+ storeName,
412
+ });
413
+
414
+ pushSummaryField(
415
+ fields,
416
+ formatLogFieldLabel(fieldName, {
417
+ columns: safeColumns,
418
+ storeName,
419
+ }),
420
+ formattedValue,
421
+ );
422
+ });
423
+
424
+ if (fields.length) {
425
+ return fields.slice(0, 6);
426
+ }
427
+
428
+ if (shortClassName === 'Product') {
429
+ appendSummaryField(fields, 'Produto', entity?.product || entity?.name);
430
+ appendSummaryField(fields, 'Preco', entity?.price, { money: true });
431
+ appendSummaryField(fields, 'SKU', entity?.sku);
432
+ appendSummaryField(fields, 'Unidade', entity?.productUnit?.productUnit || entity?.productUnit?.unit);
433
+ appendSummaryField(fields, 'Descricao', entity?.description);
434
+ return fields;
435
+ }
436
+
437
+ if (shortClassName === 'OrderProduct') {
438
+ appendSummaryField(fields, 'Produto', entity?.product?.product || entity?.product?.name);
439
+ appendSummaryField(fields, 'Quantidade', entity?.quantity);
440
+ appendSummaryField(fields, 'Preco unitario', entity?.unitPrice ?? entity?.price, {
441
+ money: true,
442
+ });
443
+ appendSummaryField(fields, 'Total', entity?.total, { money: true });
444
+ appendSummaryField(fields, 'Observacao', entity?.comment || entity?.observation);
445
+ return fields;
446
+ }
447
+
448
+ appendSummaryField(fields, 'Nome', entity?.name || entity?.alias || entity?.product || entity?.title);
449
+ appendSummaryField(fields, 'Status', entity?.status?.status || entity?.status?.realStatus || entity?.status);
450
+ appendSummaryField(fields, 'Documento', entity?.document);
451
+ appendSummaryField(fields, 'E-mail', entity?.email);
452
+ appendSummaryField(fields, 'Telefone', entity?.phone);
453
+ appendSummaryField(fields, 'SKU', entity?.sku);
454
+
455
+ Object.entries(entity).forEach(([fieldName, fieldValue]) => {
456
+ if (fields.length >= 6 || IGNORED_KEYS.has(fieldName)) {
457
+ return;
458
+ }
459
+
460
+ if (typeof fieldValue === 'object' || Array.isArray(fieldValue)) {
461
+ return;
462
+ }
463
+
464
+ if (MONEY_FIELD_PATTERN.test(fieldName)) {
465
+ appendSummaryField(fields, formatLogFieldLabel(fieldName), fieldValue, {
466
+ money: true,
467
+ });
468
+ return;
469
+ }
470
+
471
+ if (DATE_PATTERN.test(String(fieldValue || ''))) {
472
+ appendSummaryField(fields, formatLogFieldLabel(fieldName), fieldValue);
473
+ }
474
+ });
475
+
476
+ appendSummaryField(fields, 'Descricao', entity?.description);
477
+
478
+ return fields.slice(0, 6);
479
+ };
480
+
481
+ export const resolveEntityImageUrl = entity => {
482
+ const fileEntity = resolveEntityFileCandidate(entity);
483
+ return resolveFileImageUrl(fileEntity);
484
+ };
485
+
486
+ const resolveRelationItems = (entity, key, config = {}) => {
487
+ if (typeof config.extractItems === 'function') {
488
+ const extractedItems = config.extractItems(entity);
489
+ return Array.isArray(extractedItems)
490
+ ? extractedItems.filter(Boolean)
491
+ : extractedItems
492
+ ? [extractedItems]
493
+ : [];
494
+ }
495
+
496
+ const value = entity?.[key];
497
+ if (Array.isArray(value)) {
498
+ return value.filter(Boolean);
499
+ }
500
+
501
+ return value ? [value] : [];
502
+ };
503
+
504
+ const buildRelationItemCandidate = ({
505
+ entity,
506
+ className,
507
+ fallbackLabel,
508
+ iri,
509
+ }) => {
510
+ const id = extractEntityId(entity);
511
+ const resolvedClassName =
512
+ className ||
513
+ resolveEntityClassFromType(entity?.['@type']) ||
514
+ '';
515
+
516
+ if (!id || !resolvedClassName) {
517
+ return null;
518
+ }
519
+
520
+ return {
521
+ key: buildEntityKey(resolvedClassName, id),
522
+ id,
523
+ className: resolvedClassName,
524
+ entity,
525
+ iri: buildEntityIri({ entity, iri }),
526
+ label: buildEntityLabel({
527
+ entity,
528
+ className: resolvedClassName,
529
+ id,
530
+ fallbackLabel,
531
+ }),
532
+ };
533
+ };
534
+
535
+ const isEntityLikeObject = value =>
536
+ !!value &&
537
+ typeof value === 'object' &&
538
+ !Array.isArray(value) &&
539
+ extractEntityId(value);
540
+
541
+ export const buildEntityChildren = (
542
+ entity,
543
+ {
544
+ relationConfig = {},
545
+ ancestryKeys = [],
546
+ } = {},
547
+ ) => {
548
+ if (!entity || typeof entity !== 'object') {
549
+ return [];
550
+ }
551
+
552
+ const ancestrySet = new Set(ancestryKeys.filter(Boolean));
553
+ const candidates = [];
554
+ const handledKeys = new Set();
555
+
556
+ Object.entries(relationConfig || {}).forEach(([key, config]) => {
557
+ handledKeys.add(key);
558
+
559
+ const relationItems = resolveRelationItems(entity, key, config);
560
+ if (!relationItems.length) {
561
+ return;
562
+ }
563
+
564
+ const items = relationItems
565
+ .map(item =>
566
+ buildRelationItemCandidate({
567
+ entity: item,
568
+ className: config?.className,
569
+ fallbackLabel: config?.itemLabel || config?.label || formatLogFieldLabel(key),
570
+ iri: typeof config?.buildIri === 'function' ? config.buildIri(item) : config?.iri,
571
+ }),
572
+ )
573
+ .filter(Boolean)
574
+ .filter(item => !ancestrySet.has(item.key));
575
+
576
+ if (!items.length) {
577
+ return;
578
+ }
579
+
580
+ candidates.push({
581
+ key,
582
+ label: config?.label || formatLogFieldLabel(key),
583
+ items,
584
+ isCollection: config?.isCollection || items.length > 1,
585
+ });
586
+ });
587
+
588
+ Object.entries(entity).forEach(([key, value]) => {
589
+ if (
590
+ handledKeys.has(key) ||
591
+ IGNORED_KEYS.has(key) ||
592
+ value === null ||
593
+ value === undefined
594
+ ) {
595
+ return;
596
+ }
597
+
598
+ if (Array.isArray(value)) {
599
+ const items = value
600
+ .filter(isEntityLikeObject)
601
+ .map(item =>
602
+ buildRelationItemCandidate({
603
+ entity: item,
604
+ fallbackLabel: formatLogFieldLabel(key),
605
+ iri: item?.['@id'],
606
+ }),
607
+ )
608
+ .filter(Boolean)
609
+ .filter(item => !ancestrySet.has(item.key));
610
+
611
+ if (!items.length) {
612
+ return;
613
+ }
614
+
615
+ candidates.push({
616
+ key,
617
+ label: formatLogFieldLabel(key),
618
+ items,
619
+ isCollection: true,
620
+ });
621
+ return;
622
+ }
623
+
624
+ if (!isEntityLikeObject(value)) {
625
+ return;
626
+ }
627
+
628
+ const item = buildRelationItemCandidate({
629
+ entity: value,
630
+ fallbackLabel: formatLogFieldLabel(key),
631
+ iri: value?.['@id'],
632
+ });
633
+
634
+ if (!item || ancestrySet.has(item.key)) {
635
+ return;
636
+ }
637
+
638
+ candidates.push({
639
+ key,
640
+ label: formatLogFieldLabel(key),
641
+ items: [item],
642
+ isCollection: false,
643
+ });
644
+ });
645
+
646
+ return candidates.sort((left, right) =>
647
+ String(left.label || '').localeCompare(String(right.label || ''), 'pt-BR'),
648
+ );
649
+ };