@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,475 @@
1
+ import { getDeviceTypeLabel } from '@controleonline/ui-common/src/react/utils/printerDevices';
2
+
3
+ const DEVICE_RUNTIME_FOOTER_TEXT_CONFIG_KEY = 'device-runtime-footer-text';
4
+
5
+ const safeTrim = value => String(value || '').trim();
6
+
7
+ const POS_OPERATION_MODE_TRANSLATION_KEYS = {
8
+ counter: 'counterService',
9
+ waiter: 'waiterService',
10
+ totem: 'selfServiceKiosk',
11
+ 'single-item': 'singleItemSale',
12
+ cashier: 'cashierPOS',
13
+ };
14
+
15
+ const normalizeOperationModeKey = value =>
16
+ safeTrim(value)
17
+ .toLowerCase()
18
+ .normalize('NFD')
19
+ .replace(/[\u0300-\u036f]/g, '')
20
+ .replace(/[\s_]/g, '-');
21
+
22
+ const normalizeComparableText = value =>
23
+ safeTrim(value)
24
+ .toLowerCase()
25
+ .normalize('NFD')
26
+ .replace(/[\u0300-\u036f]/g, '');
27
+
28
+ const getRuntimeFooterTranslation = (store, type, key) =>
29
+ safeTrim(globalThis?.t?.getMessageFromBuckets?.(store, type, key));
30
+
31
+ const buildRuntimeFooterDisplayName = ({
32
+ deviceName,
33
+ operationalTypeLabel,
34
+ operationModeLabel,
35
+ runtimeDetail,
36
+ }) => {
37
+ const parts = [];
38
+ const seen = new Set();
39
+
40
+ [deviceName, operationalTypeLabel].forEach(value => {
41
+ const normalizedValue = normalizeComparableText(value);
42
+
43
+ if (!normalizedValue || seen.has(normalizedValue)) {
44
+ return;
45
+ }
46
+
47
+ seen.add(normalizedValue);
48
+ parts.push(safeTrim(value));
49
+ });
50
+
51
+ if (operationModeLabel) {
52
+ const normalizedModeLabel = normalizeComparableText(operationModeLabel);
53
+
54
+ if (normalizedModeLabel && !seen.has(normalizedModeLabel)) {
55
+ seen.add(normalizedModeLabel);
56
+ parts.push(safeTrim(operationModeLabel));
57
+ }
58
+
59
+ return parts.join(' • ');
60
+ }
61
+
62
+ if (runtimeDetail) {
63
+ return `${parts.join(' • ')} (${safeTrim(runtimeDetail)})`;
64
+ }
65
+
66
+ return parts.join(' • ');
67
+ };
68
+
69
+ const parseJsonStringValue = value => {
70
+ try {
71
+ return JSON.parse(value);
72
+ } catch {
73
+ return value;
74
+ }
75
+ };
76
+
77
+ const parseObjectValue = value => {
78
+ if (!value) {
79
+ return {};
80
+ }
81
+
82
+ if (typeof value === 'string') {
83
+ const parsed = parseJsonStringValue(value);
84
+ return parsed && typeof parsed === 'object' ? parsed : {};
85
+ }
86
+
87
+ return typeof value === 'object' ? value : {};
88
+ };
89
+
90
+ const parseDeviceMetadata = metadata => parseObjectValue(metadata);
91
+ const parseDeviceConfigs = configs => parseObjectValue(configs);
92
+
93
+ const normalizeRuntimeFooterText = value => {
94
+ if (value === null || value === undefined) {
95
+ return '';
96
+ }
97
+
98
+ const parsedValue =
99
+ typeof value === 'string' ? parseJsonStringValue(value) : value;
100
+
101
+ if (parsedValue && typeof parsedValue === 'object') {
102
+ return '';
103
+ }
104
+
105
+ const normalized = safeTrim(parsedValue);
106
+
107
+ return normalized.replace(/\s+/g, ' ');
108
+ };
109
+
110
+ const getRuntimeFooterText = company =>
111
+ normalizeRuntimeFooterText(
112
+ company?.configs?.[DEVICE_RUNTIME_FOOTER_TEXT_CONFIG_KEY],
113
+ );
114
+
115
+ const isWebRuntimeDevice = device => {
116
+ const metadata = parseDeviceMetadata(device?.metadata);
117
+ const runtime = safeTrim(metadata?.runtime).toLowerCase();
118
+ const systemName = safeTrim(
119
+ device?.systemName || metadata?.system?.name,
120
+ ).toLowerCase();
121
+ const hardwareType = safeTrim(
122
+ device?.deviceType || metadata?.system?.hardwareType,
123
+ ).toLowerCase();
124
+ const identifier = safeTrim(device?.device || device?.id).toLowerCase();
125
+
126
+ return (
127
+ runtime === 'web' ||
128
+ systemName === 'web' ||
129
+ hardwareType === 'web' ||
130
+ identifier.startsWith('web-')
131
+ );
132
+ };
133
+
134
+ const getRuntimeFooterDeviceName = device => {
135
+ if (isWebRuntimeDevice(device)) {
136
+ return 'web';
137
+ }
138
+
139
+ const metadata = parseDeviceMetadata(device?.metadata);
140
+
141
+ return (
142
+ safeTrim(device?.alias) ||
143
+ safeTrim(device?.name) ||
144
+ safeTrim(metadata?.system?.name) ||
145
+ safeTrim(metadata?.system?.model) ||
146
+ safeTrim(device?.appName) ||
147
+ safeTrim(device?.deviceType) ||
148
+ safeTrim(device?.id)
149
+ );
150
+ };
151
+
152
+ const formatRuntimeFooterVersion = version => {
153
+ const normalizedVersion = safeTrim(version).replace(/^v/i, '');
154
+ return normalizedVersion ? `v${normalizedVersion}` : '';
155
+ };
156
+
157
+ const getRuntimeFooterWebHost = () => {
158
+ const hostname = safeTrim(globalThis?.location?.hostname);
159
+
160
+ if (!hostname) {
161
+ return '';
162
+ }
163
+
164
+ if (['localhost', '::1', '[::1]'].includes(hostname)) {
165
+ return '127.0.0.1';
166
+ }
167
+
168
+ return hostname;
169
+ };
170
+
171
+ const getRuntimeFooterWebIdentifierCandidates = ({device, deviceConfig}) => {
172
+ const deviceMetadata = parseDeviceMetadata(device?.metadata);
173
+ const storedDeviceMetadata = parseDeviceMetadata(deviceConfig?.device?.metadata);
174
+ const candidates = [
175
+ {
176
+ value: safeTrim(storedDeviceMetadata?.network?.publicIp),
177
+ source: 'device_config.device.metadata.network.publicIp',
178
+ },
179
+ {
180
+ value: safeTrim(deviceMetadata?.network?.publicIp),
181
+ source: 'device.metadata.network.publicIp',
182
+ },
183
+ {
184
+ value: safeTrim(device?.externalIp),
185
+ source: 'device.externalIp',
186
+ },
187
+ {
188
+ value: getRuntimeFooterWebHost(),
189
+ source: 'location.hostname',
190
+ },
191
+ ];
192
+ const seenValues = new Set();
193
+
194
+ return candidates.filter(candidate => {
195
+ if (!candidate.value || seenValues.has(candidate.value)) {
196
+ return false;
197
+ }
198
+
199
+ seenValues.add(candidate.value);
200
+ return true;
201
+ });
202
+ };
203
+
204
+ const getRuntimeFooterStoredVersion = deviceConfig => {
205
+ const configs = parseDeviceConfigs(deviceConfig?.configs);
206
+ return formatRuntimeFooterVersion(configs?.['config-version']);
207
+ };
208
+
209
+ const getRuntimeFooterOperationalTypeInfo = ({device, deviceConfig} = {}) => {
210
+ const runtimeType = safeTrim(device?.type);
211
+
212
+ if (runtimeType) {
213
+ return {
214
+ source: 'device.type',
215
+ value: runtimeType.toUpperCase(),
216
+ };
217
+ }
218
+
219
+ const storedType = safeTrim(deviceConfig?.device?.type);
220
+
221
+ if (storedType) {
222
+ return {
223
+ source: 'device_config.device.type',
224
+ value: storedType.toUpperCase(),
225
+ };
226
+ }
227
+
228
+ return {
229
+ source: '',
230
+ value: '',
231
+ };
232
+ };
233
+
234
+ const getRuntimeFooterOperationalTypeLabel = ({device, deviceConfig} = {}) => {
235
+ const operationalTypeInfo = getRuntimeFooterOperationalTypeInfo({
236
+ device,
237
+ deviceConfig,
238
+ });
239
+
240
+ return operationalTypeInfo.value
241
+ ? getDeviceTypeLabel(operationalTypeInfo.value)
242
+ : '';
243
+ };
244
+
245
+ const getRuntimeFooterOperationModeLabel = ({deviceConfig} = {}) => {
246
+ const configs = parseDeviceConfigs(deviceConfig?.configs);
247
+ const normalizedMode = normalizeOperationModeKey(
248
+ configs?.['pos-operation-mode'],
249
+ );
250
+ const translationKey = POS_OPERATION_MODE_TRANSLATION_KEYS[normalizedMode];
251
+
252
+ return translationKey
253
+ ? getRuntimeFooterTranslation('common', 'option', translationKey)
254
+ : '';
255
+ };
256
+
257
+ const getRuntimeFooterOperationalSummary = ({device, deviceConfig} = {}) => {
258
+ const operationalTypeLabel = getRuntimeFooterOperationalTypeLabel({
259
+ device,
260
+ deviceConfig,
261
+ });
262
+
263
+ if (!operationalTypeLabel) {
264
+ return '';
265
+ }
266
+
267
+ if (operationalTypeLabel !== 'PDV') {
268
+ return operationalTypeLabel;
269
+ }
270
+
271
+ const operationModeLabel = getRuntimeFooterOperationModeLabel({
272
+ deviceConfig,
273
+ });
274
+
275
+ if (
276
+ !operationModeLabel ||
277
+ operationModeLabel.toUpperCase() === operationalTypeLabel.toUpperCase()
278
+ ) {
279
+ return operationalTypeLabel;
280
+ }
281
+
282
+ return `${operationalTypeLabel} • ${operationModeLabel}`;
283
+ };
284
+
285
+ const getRuntimeFooterVersionCandidates = ({device, appVersion, deviceConfig}) => {
286
+ const deviceMetadata = parseDeviceMetadata(device?.metadata);
287
+ const storedDeviceMetadata = parseDeviceMetadata(deviceConfig?.device?.metadata);
288
+ const configs = parseDeviceConfigs(deviceConfig?.configs);
289
+ const candidates = [
290
+ {
291
+ label: formatRuntimeFooterVersion(storedDeviceMetadata?.app?.version),
292
+ rawValue: safeTrim(storedDeviceMetadata?.app?.version),
293
+ source: 'device_config.device.metadata.app.version',
294
+ },
295
+ {
296
+ label: formatRuntimeFooterVersion(deviceMetadata?.app?.version),
297
+ rawValue: safeTrim(deviceMetadata?.app?.version),
298
+ source: 'device.metadata.app.version',
299
+ },
300
+ {
301
+ label: formatRuntimeFooterVersion(device?.appVersion),
302
+ rawValue: safeTrim(device?.appVersion),
303
+ source: 'device.appVersion',
304
+ },
305
+ {
306
+ label: formatRuntimeFooterVersion(appVersion),
307
+ rawValue: safeTrim(appVersion),
308
+ source: 'appVersion',
309
+ },
310
+ {
311
+ label: formatRuntimeFooterVersion(configs?.['config-version']),
312
+ rawValue: safeTrim(configs?.['config-version']),
313
+ source: 'device_config.configs.config-version',
314
+ },
315
+ ];
316
+ const seenLabels = new Set();
317
+
318
+ return candidates.filter(candidate => {
319
+ if (!candidate.label || seenLabels.has(candidate.label)) {
320
+ return false;
321
+ }
322
+
323
+ seenLabels.add(candidate.label);
324
+ return true;
325
+ });
326
+ };
327
+
328
+ const getRuntimeFooterNativeIdentifierCandidates = ({device, deviceConfig}) => {
329
+ const candidates = [
330
+ {
331
+ value: safeTrim(deviceConfig?.device?.device),
332
+ source: 'device_config.device.device',
333
+ },
334
+ {
335
+ value: safeTrim(device?.id),
336
+ source: 'device.id',
337
+ },
338
+ {
339
+ value: safeTrim(deviceConfig?.device?.id),
340
+ source: 'device_config.device.id',
341
+ },
342
+ ];
343
+ const seenValues = new Set();
344
+
345
+ return candidates.filter(candidate => {
346
+ if (!candidate.value || seenValues.has(candidate.value)) {
347
+ return false;
348
+ }
349
+
350
+ seenValues.add(candidate.value);
351
+ return true;
352
+ });
353
+ };
354
+
355
+ const getRuntimeFooterDebugInfo = ({device, appVersion, deviceConfig}) => {
356
+ const deviceName = getRuntimeFooterDeviceName(device);
357
+ const versionCandidates = getRuntimeFooterVersionCandidates({
358
+ device,
359
+ appVersion,
360
+ deviceConfig,
361
+ });
362
+ const versionLabel = versionCandidates[0]?.label || '';
363
+ const isWebRuntime = isWebRuntimeDevice(device);
364
+ const operationalTypeInfo = getRuntimeFooterOperationalTypeInfo({
365
+ device,
366
+ deviceConfig,
367
+ });
368
+ const operationalTypeLabel = operationalTypeInfo.value
369
+ ? getDeviceTypeLabel(operationalTypeInfo.value)
370
+ : '';
371
+ const operationModeLabel =
372
+ operationalTypeLabel === 'PDV'
373
+ ? getRuntimeFooterOperationModeLabel({deviceConfig})
374
+ : '';
375
+ const operationalSummary = getRuntimeFooterOperationalSummary({
376
+ device,
377
+ deviceConfig,
378
+ });
379
+ const webIdentifierCandidates = isWebRuntime
380
+ ? getRuntimeFooterWebIdentifierCandidates({
381
+ device,
382
+ deviceConfig,
383
+ })
384
+ : [];
385
+ const webIdentifier = webIdentifierCandidates[0]?.value || '';
386
+ const nativeIdentifierCandidates = getRuntimeFooterNativeIdentifierCandidates({
387
+ device,
388
+ deviceConfig,
389
+ });
390
+ const nativeIdentifier = nativeIdentifierCandidates[0]?.value || '';
391
+ const runtimeDetail = operationModeLabel
392
+ ? operationModeLabel
393
+ : isWebRuntime
394
+ ? webIdentifier
395
+ : nativeIdentifier;
396
+ const runtimeDetailSource = operationModeLabel
397
+ ? 'device_config.configs.pos-operation-mode'
398
+ : isWebRuntime
399
+ ? webIdentifierCandidates[0]?.source || ''
400
+ : nativeIdentifierCandidates[0]?.source || '';
401
+ const displayName = buildRuntimeFooterDisplayName({
402
+ deviceName,
403
+ operationalTypeLabel,
404
+ operationModeLabel,
405
+ runtimeDetail,
406
+ });
407
+ const primaryText = [displayName, versionLabel]
408
+ .filter(Boolean)
409
+ .join(' / ');
410
+
411
+ return {
412
+ deviceName,
413
+ displayName,
414
+ operationalSummary,
415
+ operationalTypeLabel,
416
+ operationModeLabel,
417
+ versionLabel,
418
+ runtimeDetail,
419
+ runtimeDetailSource,
420
+ primaryText,
421
+ isWebRuntime,
422
+ versionCandidates,
423
+ webIdentifierCandidates,
424
+ nativeIdentifierCandidates,
425
+ rawValues: {
426
+ appVersionProp: safeTrim(appVersion),
427
+ deviceId: safeTrim(device?.id),
428
+ deviceType: safeTrim(device?.type),
429
+ deviceExternalIp: safeTrim(device?.externalIp),
430
+ deviceAppVersion: safeTrim(device?.appVersion),
431
+ deviceMetadataAppVersion: safeTrim(
432
+ parseDeviceMetadata(device?.metadata)?.app?.version,
433
+ ),
434
+ deviceMetadataPublicIp: safeTrim(
435
+ parseDeviceMetadata(device?.metadata)?.network?.publicIp,
436
+ ),
437
+ storedDeviceRecordId: safeTrim(deviceConfig?.device?.id),
438
+ storedDeviceType: safeTrim(deviceConfig?.device?.type),
439
+ storedDeviceIdentifier: safeTrim(deviceConfig?.device?.device),
440
+ storedDeviceMetadataAppVersion: safeTrim(
441
+ parseDeviceMetadata(deviceConfig?.device?.metadata)?.app?.version,
442
+ ),
443
+ storedDeviceMetadataPublicIp: safeTrim(
444
+ parseDeviceMetadata(deviceConfig?.device?.metadata)?.network?.publicIp,
445
+ ),
446
+ storedConfigVersion: safeTrim(
447
+ parseDeviceConfigs(deviceConfig?.configs)?.['config-version'],
448
+ ),
449
+ operationMode: normalizeOperationModeKey(
450
+ parseDeviceConfigs(deviceConfig?.configs)?.['pos-operation-mode'],
451
+ ),
452
+ operationModeLabel,
453
+ operationalType: safeTrim(operationalTypeInfo.value),
454
+ webHost: getRuntimeFooterWebHost(),
455
+ },
456
+ };
457
+ };
458
+
459
+ const getRuntimeFooterPrimaryText = params =>
460
+ getRuntimeFooterDebugInfo(params).primaryText;
461
+
462
+ module.exports = {
463
+ DEVICE_RUNTIME_FOOTER_TEXT_CONFIG_KEY,
464
+ formatRuntimeFooterVersion,
465
+ getRuntimeFooterDebugInfo,
466
+ getRuntimeFooterDeviceName,
467
+ getRuntimeFooterNativeIdentifierCandidates,
468
+ getRuntimeFooterPrimaryText,
469
+ getRuntimeFooterStoredVersion,
470
+ getRuntimeFooterVersionCandidates,
471
+ getRuntimeFooterWebIdentifierCandidates,
472
+ getRuntimeFooterText,
473
+ getRuntimeFooterWebHost,
474
+ normalizeRuntimeFooterText,
475
+ };
@@ -0,0 +1,50 @@
1
+ function normalizeLanguageCode(value) {
2
+ if (typeof value !== 'string') {
3
+ return ''
4
+ }
5
+
6
+ return value.trim().replace(/_/g, '-').toLowerCase()
7
+ }
8
+
9
+ function resolveCompanyLanguageCode(company) {
10
+ const candidates = [
11
+ company?.language?.language,
12
+ company?.language?.locale,
13
+ company?.language?.code,
14
+ company?.locale,
15
+ company?.languageCode,
16
+ typeof company?.language === 'string' ? company.language : '',
17
+ company?.configs?.language,
18
+ ]
19
+
20
+ for (const candidate of candidates) {
21
+ const normalized = normalizeLanguageCode(candidate)
22
+ if (normalized) {
23
+ return normalized
24
+ }
25
+ }
26
+
27
+ return ''
28
+ }
29
+
30
+ function resolveConfiguredLanguage({
31
+ currentCompany,
32
+ defaultCompany,
33
+ currentConfig,
34
+ sessionData,
35
+ fallback = 'pt-br',
36
+ } = {}) {
37
+ return (
38
+ resolveCompanyLanguageCode(currentCompany) ||
39
+ resolveCompanyLanguageCode(defaultCompany) ||
40
+ normalizeLanguageCode(currentConfig?.language) ||
41
+ normalizeLanguageCode(sessionData?.language) ||
42
+ fallback
43
+ )
44
+ }
45
+
46
+ module.exports = {
47
+ normalizeLanguageCode,
48
+ resolveCompanyLanguageCode,
49
+ resolveConfiguredLanguage,
50
+ }