@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,427 @@
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
+ const normalizeFormattedValue = value => {
116
+ if (value === null || value === undefined) {
117
+ return value;
118
+ }
119
+
120
+ if (typeof value === 'object' && !Array.isArray(value)) {
121
+ return value?.label ?? value?.value ?? value?.name ?? value;
122
+ }
123
+
124
+ return value;
125
+ };
126
+
127
+ const DEFAULT_TRANSLATABLE_FIELDS = new Set([
128
+ 'active',
129
+ 'app',
130
+ 'channel',
131
+ 'displaytype',
132
+ 'featured',
133
+ 'frequency',
134
+ 'invoicetype',
135
+ 'installments',
136
+ 'ordertype',
137
+ 'peopletype',
138
+ 'pricecalculation',
139
+ 'productcondition',
140
+ 'realstatus',
141
+ 'status',
142
+ ]);
143
+
144
+ const shouldTranslateColumnValue = ({column, storeName, value}) => {
145
+ if (column?.translate === false || typeof value !== 'string' || !normalizeText(storeName)) {
146
+ return false;
147
+ }
148
+
149
+ if (column?.translate === true || Array.isArray(column?.list)) {
150
+ return true;
151
+ }
152
+
153
+ const candidates = [column?.key, column?.name, column?.label]
154
+ .map(normalizeKey)
155
+ .filter(Boolean);
156
+
157
+ return candidates.some(candidate => DEFAULT_TRANSLATABLE_FIELDS.has(candidate));
158
+ };
159
+
160
+ const translateColumnValue = ({column, storeName, value}) => {
161
+ if (!shouldTranslateColumnValue({column, storeName, value})) {
162
+ return value;
163
+ }
164
+
165
+ const translateFn =
166
+ typeof globalThis !== 'undefined' && typeof globalThis?.t?.t === 'function'
167
+ ? globalThis.t.t.bind(globalThis.t)
168
+ : null;
169
+
170
+ return translateFn ? translateFn(storeName, 'span', value) : value;
171
+ };
172
+
173
+ const translateColumnLabel = ({column, fieldName, storeName, fallbackLabel = ''}) => {
174
+ if (!normalizeText(storeName)) {
175
+ return fallbackLabel;
176
+ }
177
+
178
+ const translateFn =
179
+ typeof globalThis !== 'undefined' && typeof globalThis?.t?.t === 'function'
180
+ ? globalThis.t.t.bind(globalThis.t)
181
+ : null;
182
+ if (!translateFn) {
183
+ return fallbackLabel;
184
+ }
185
+
186
+ const candidates = [
187
+ normalizeText(fieldName),
188
+ normalizeText(column?.name),
189
+ normalizeText(column?.key),
190
+ normalizeText(column?.label),
191
+ ].filter(Boolean);
192
+
193
+ for (const candidate of candidates) {
194
+ const translated = translateFn(storeName, 'label', candidate);
195
+ if (normalizeText(translated) && normalizeText(translated) !== formatHumanLabel(candidate)) {
196
+ return translated;
197
+ }
198
+ }
199
+
200
+ return fallbackLabel;
201
+ };
202
+
203
+ export const resolveStoreConfigByName = storeName => {
204
+ const normalizedStoreName = normalizeText(storeName);
205
+ const stores = getAllStores();
206
+ const store = stores?.[normalizedStoreName];
207
+ const resourceEndpoint = getStoreEndpoint(store);
208
+
209
+ return {
210
+ columns: getStoreColumns(store),
211
+ resourceEndpoint,
212
+ resourceName: resourceEndpoint,
213
+ store,
214
+ storeName: normalizedStoreName,
215
+ };
216
+ };
217
+
218
+ export const resolveStoreConfigByClassName = className => {
219
+ const normalizedClassKey = normalizeKey(
220
+ String(className || '').split('\\').filter(Boolean).pop(),
221
+ );
222
+ if (!normalizedClassKey) {
223
+ return {
224
+ columns: [],
225
+ resourceEndpoint: '',
226
+ resourceName: '',
227
+ store: null,
228
+ storeName: '',
229
+ };
230
+ }
231
+
232
+ const stores = getAllStores();
233
+ const candidates = Object.entries(stores || {})
234
+ .map(([storeName, store]) => {
235
+ const resourceEndpoint = getStoreEndpoint(store);
236
+ const normalizedEndpoint = singularizeResourceName(resourceEndpoint);
237
+ const columns = getStoreColumns(store);
238
+ const endpointKey = normalizeKey(normalizedEndpoint);
239
+ const resourceKey = normalizeKey(resourceEndpoint);
240
+ const storeKey = normalizeKey(storeName);
241
+
242
+ let score = 0;
243
+ if (endpointKey === normalizedClassKey) score += 4;
244
+ if (storeKey === normalizedClassKey) score += 3;
245
+ if (resourceKey === normalizedClassKey) score += 2;
246
+ if (!score) {
247
+ return null;
248
+ }
249
+
250
+ if (columns.length) score += 1;
251
+
252
+ return {
253
+ columns,
254
+ resourceEndpoint,
255
+ resourceName: resourceEndpoint,
256
+ score,
257
+ store,
258
+ storeName,
259
+ };
260
+ })
261
+ .filter(Boolean)
262
+ .sort((left, right) => {
263
+ if (right.score !== left.score) {
264
+ return right.score - left.score;
265
+ }
266
+
267
+ return right.columns.length - left.columns.length;
268
+ });
269
+
270
+ return candidates[0] || {
271
+ columns: [],
272
+ resourceEndpoint: '',
273
+ resourceName: '',
274
+ store: null,
275
+ storeName: '',
276
+ };
277
+ };
278
+
279
+ export const resolveStoreConfigByEntity = ({entity = null, entityIri = ''} = {}) => {
280
+ const resolvedEntityIri =
281
+ normalizeText(entity?.['@id']) || normalizeText(entityIri);
282
+ const resourceName = extractEntityResource(resolvedEntityIri);
283
+ if (!resourceName) {
284
+ return {
285
+ columns: [],
286
+ resourceEndpoint: '',
287
+ resourceName: '',
288
+ store: null,
289
+ storeName: '',
290
+ };
291
+ }
292
+
293
+ const normalizedResourceName = singularizeResourceName(resourceName);
294
+ const stores = getAllStores();
295
+
296
+ const candidates = Object.entries(stores || {})
297
+ .map(([storeName, store]) => {
298
+ const resourceEndpoint = getStoreEndpoint(store);
299
+ const normalizedEndpoint = singularizeResourceName(resourceEndpoint);
300
+ const columns = getStoreColumns(store);
301
+
302
+ if (
303
+ !resourceEndpoint ||
304
+ (resourceEndpoint !== resourceName && normalizedEndpoint !== normalizedResourceName)
305
+ ) {
306
+ return null;
307
+ }
308
+
309
+ let score = 0;
310
+ if (resourceEndpoint === resourceName) score += 4;
311
+ if (storeName === resourceName) score += 3;
312
+ if (storeName === normalizedResourceName) score += 2;
313
+ if (resourceEndpoint === storeName) score += 1;
314
+ if (columns.length) score += 1;
315
+
316
+ return {
317
+ columns,
318
+ resourceEndpoint,
319
+ resourceName,
320
+ score,
321
+ store,
322
+ storeName,
323
+ };
324
+ })
325
+ .filter(Boolean)
326
+ .sort((left, right) => {
327
+ if (right.score !== left.score) {
328
+ return right.score - left.score;
329
+ }
330
+
331
+ return right.columns.length - left.columns.length;
332
+ });
333
+
334
+ return candidates[0] || {
335
+ columns: [],
336
+ resourceEndpoint: '',
337
+ resourceName,
338
+ store: null,
339
+ storeName: '',
340
+ };
341
+ };
342
+
343
+ export const resolveStoreColumn = (columns = [], fieldName = '') => {
344
+ const normalizedFieldName = normalizeKey(fieldName);
345
+ if (!normalizedFieldName) {
346
+ return null;
347
+ }
348
+
349
+ return columns.find(column => {
350
+ const candidateNames = [
351
+ column?.key,
352
+ column?.name,
353
+ column?.label,
354
+ ]
355
+ .map(normalizeKey)
356
+ .filter(Boolean);
357
+
358
+ return candidateNames.includes(normalizedFieldName);
359
+ }) || null;
360
+ };
361
+
362
+ export const formatStoreColumnLabel = ({
363
+ columns = [],
364
+ fieldName = '',
365
+ fallbackLabel = '',
366
+ storeName = '',
367
+ } = {}) => {
368
+ const column = resolveStoreColumn(columns, fieldName);
369
+ if (!column?.label) {
370
+ return fallbackLabel;
371
+ }
372
+
373
+ const normalizedLabel = normalizeText(column.label);
374
+ if (!normalizedLabel) {
375
+ return fallbackLabel;
376
+ }
377
+
378
+ const directTranslatedLabel = translateColumnLabel({
379
+ column,
380
+ fallbackLabel,
381
+ fieldName,
382
+ storeName,
383
+ });
384
+ if (directTranslatedLabel) {
385
+ return formatHumanLabel(directTranslatedLabel) || directTranslatedLabel;
386
+ }
387
+
388
+ const resolvedLabel = translateColumnValue({
389
+ column,
390
+ storeName,
391
+ value: normalizedLabel,
392
+ });
393
+
394
+ return formatHumanLabel(resolvedLabel) || resolvedLabel;
395
+ };
396
+
397
+ // Replica a logica central de columns do store para usos fora das listagens padrao.
398
+ export const formatStoreColumnValue = ({
399
+ columns = [],
400
+ fieldName = '',
401
+ row = null,
402
+ storeName = '',
403
+ value,
404
+ } = {}) => {
405
+ const column = resolveStoreColumn(columns, fieldName);
406
+ if (!column) {
407
+ return undefined;
408
+ }
409
+
410
+ const rowContext = buildRowContext(row, fieldName, value);
411
+ const resolvedValue = resolveListValue({
412
+ column,
413
+ row: rowContext,
414
+ value,
415
+ });
416
+
417
+ let formattedValue = resolvedValue;
418
+ if (typeof column.format === 'function') {
419
+ formattedValue = column.format(resolvedValue, column, rowContext, false);
420
+ }
421
+
422
+ return translateColumnValue({
423
+ column,
424
+ storeName,
425
+ value: normalizeFormattedValue(formattedValue),
426
+ });
427
+ };
@@ -0,0 +1,109 @@
1
+ import {resolveSystemErrorMessage} from './systemErrorMessage'
2
+
3
+ export const DEFAULT_SYSTEM_ERROR_DEDUPE_WINDOW_MS = 1200
4
+
5
+ const listeners = new Set()
6
+ const pendingPayloads = []
7
+
8
+ let lastFingerprint = ''
9
+ let lastPublishedAt = 0
10
+
11
+ const resolveDedupeWindow = options => {
12
+ const value = Number(options?.dedupeWindowMs)
13
+ if (Number.isFinite(value) && value >= 0) {
14
+ return value
15
+ }
16
+
17
+ return DEFAULT_SYSTEM_ERROR_DEDUPE_WINDOW_MS
18
+ }
19
+
20
+ export const getSystemErrorFingerprint = (error, options = {}) => {
21
+ const message = resolveSystemErrorMessage(error)
22
+
23
+ if (!message) {
24
+ return ''
25
+ }
26
+
27
+ return JSON.stringify({
28
+ message,
29
+ position: String(options?.position || ''),
30
+ providerKey: String(options?.providerKey || ''),
31
+ })
32
+ }
33
+
34
+ const shouldSkipDuplicate = (fingerprint, options = {}) => {
35
+ if (!fingerprint || options?.dedupe === false) {
36
+ return false
37
+ }
38
+
39
+ const now = Date.now()
40
+ const dedupeWindowMs = resolveDedupeWindow(options)
41
+
42
+ if (
43
+ lastFingerprint === fingerprint &&
44
+ now - lastPublishedAt < dedupeWindowMs
45
+ ) {
46
+ return true
47
+ }
48
+
49
+ lastFingerprint = fingerprint
50
+ lastPublishedAt = now
51
+ return false
52
+ }
53
+
54
+ const emitPayload = payload => {
55
+ if (listeners.size === 0) {
56
+ pendingPayloads.push(payload)
57
+ return true
58
+ }
59
+
60
+ listeners.forEach(listener => listener(payload))
61
+ return true
62
+ }
63
+
64
+ export const publishSystemError = (error, options = {}) => {
65
+ const message = resolveSystemErrorMessage(error)
66
+
67
+ if (!message) {
68
+ return false
69
+ }
70
+
71
+ const fingerprint =
72
+ options?.dedupeKey || getSystemErrorFingerprint(message, options)
73
+
74
+ if (shouldSkipDuplicate(fingerprint, options)) {
75
+ return false
76
+ }
77
+
78
+ return emitPayload({
79
+ error,
80
+ message,
81
+ options,
82
+ })
83
+ }
84
+
85
+ export const subscribeSystemErrors = listener => {
86
+ if (typeof listener !== 'function') {
87
+ return () => {}
88
+ }
89
+
90
+ listeners.add(listener)
91
+
92
+ if (pendingPayloads.length > 0) {
93
+ const queuedPayloads = pendingPayloads.splice(0, pendingPayloads.length)
94
+ queuedPayloads.forEach(payload => listener(payload))
95
+ }
96
+
97
+ return () => {
98
+ listeners.delete(listener)
99
+ }
100
+ }
101
+
102
+ export const resetSystemErrorChannel = () => {
103
+ listeners.clear()
104
+ pendingPayloads.length = 0
105
+ lastFingerprint = ''
106
+ lastPublishedAt = 0
107
+ }
108
+
109
+ export default publishSystemError
@@ -0,0 +1,67 @@
1
+ const normalizeText = value => String(value || '').trim()
2
+
3
+ const resolveMessageList = items =>
4
+ (Array.isArray(items) ? items : [])
5
+ .map(item =>
6
+ normalizeText(
7
+ item?.['hydra:description'] ||
8
+ item?.['hydra:title'] ||
9
+ item?.message ||
10
+ item?.title ||
11
+ item?.detail ||
12
+ item?.description ||
13
+ item,
14
+ ),
15
+ )
16
+ .filter(Boolean)
17
+ .join('\n')
18
+ .trim()
19
+
20
+ export const resolveSystemErrorMessage = error => {
21
+ if (error === undefined || error === null) {
22
+ return ''
23
+ }
24
+
25
+ if (typeof error === 'string') {
26
+ return error.trim()
27
+ }
28
+
29
+ if (Array.isArray(error)) {
30
+ return resolveMessageList(error)
31
+ }
32
+
33
+ if (Array.isArray(error?.message)) {
34
+ return resolveMessageList(error.message)
35
+ }
36
+
37
+ if (Array.isArray(error?.violations)) {
38
+ return resolveMessageList(error.violations)
39
+ }
40
+
41
+ if (error?.response?.data) {
42
+ const responseMessage = resolveSystemErrorMessage(error.response.data)
43
+ if (responseMessage) {
44
+ return responseMessage
45
+ }
46
+ }
47
+
48
+ if (error?.body) {
49
+ const bodyMessage = resolveSystemErrorMessage(error.body)
50
+ if (bodyMessage) {
51
+ return bodyMessage
52
+ }
53
+ }
54
+
55
+ return normalizeText(
56
+ error?.['hydra:description'] ||
57
+ error?.['hydra:title'] ||
58
+ error?.detail ||
59
+ error?.description ||
60
+ error?.errmsg ||
61
+ error?.error ||
62
+ error?.message ||
63
+ error?.title,
64
+ )
65
+ }
66
+
67
+ export default resolveSystemErrorMessage
@@ -0,0 +1,53 @@
1
+ const TOAST_STYLE_KEYS = ['pressable', 'view', 'text', 'indicator']
2
+
3
+ const isPlainObject = value =>
4
+ value && typeof value === 'object' && !Array.isArray(value)
5
+
6
+ export const TOAST_ERROR_STYLES = {
7
+ pressable: {
8
+ backgroundColor: '#FFF7F7',
9
+ borderColor: '#FECACA',
10
+ borderWidth: 1,
11
+ },
12
+ indicator: {
13
+ backgroundColor: '#DC2626',
14
+ },
15
+ }
16
+
17
+ export const TOAST_WARNING_STYLES = {
18
+ indicator: {
19
+ backgroundColor: '#e67e22',
20
+ },
21
+ }
22
+
23
+ export const mergeToastStyles = (...styleSets) =>
24
+ styleSets.reduce((mergedStyles, currentStyleSet) => {
25
+ if (!isPlainObject(currentStyleSet)) {
26
+ return mergedStyles
27
+ }
28
+
29
+ TOAST_STYLE_KEYS.forEach(styleKey => {
30
+ if (!isPlainObject(currentStyleSet[styleKey])) {
31
+ return
32
+ }
33
+
34
+ mergedStyles[styleKey] = {
35
+ ...(mergedStyles[styleKey] || {}),
36
+ ...currentStyleSet[styleKey],
37
+ }
38
+ })
39
+
40
+ return mergedStyles
41
+ }, {})
42
+
43
+ export const withErrorToastStyles = (options = {}) => ({
44
+ ...options,
45
+ styles: mergeToastStyles(TOAST_ERROR_STYLES, options?.styles),
46
+ })
47
+
48
+ export const withWarningToastStyles = (options = {}) => ({
49
+ ...options,
50
+ styles: mergeToastStyles(TOAST_WARNING_STYLES, options?.styles),
51
+ })
52
+
53
+ export default withErrorToastStyles