@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,157 @@
1
+ import React, { useState } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ TouchableOpacity,
6
+ ScrollView,
7
+ ActivityIndicator,
8
+ Platform,
9
+ } from 'react-native';
10
+
11
+ import Icon from 'react-native-vector-icons/MaterialIcons';
12
+ import * as DocumentPicker from 'expo-document-picker';
13
+ import { useStore } from '@store';
14
+ import AnimatedModal from '@controleonline/ui-crm/src/react/components/AnimatedModal';
15
+ import { api } from '@controleonline/ui-common/src/api';
16
+ import { useMessage } from '@controleonline/ui-common/src/react/components/MessageService';
17
+ import styles from './AddImportModal.styles';
18
+
19
+ const AddImportModal = ({ visible, onClose, onSuccess, context = {} }) => {
20
+ const { showError, showSuccess } = useMessage();
21
+ const peopleStore = useStore('people');
22
+ const getters = peopleStore.getters;
23
+ const { currentCompany } = getters;
24
+
25
+ const [file, setFile] = useState(null);
26
+ const [loading, setLoading] = useState(false);
27
+ const modalTitle = global.t?.t('imports', 'title', 'new_import');
28
+ const csvLabel = global.t?.t('imports', 'label', 'csv_file');
29
+ const selectFileLabel = global.t?.t('imports', 'button', 'select_file');
30
+ const cancelLabel = global.t?.t('imports', 'button', 'cancel');
31
+ const importLabel = global.t?.t('imports', 'button', 'import');
32
+ const csvOnlyLabel = global.t?.t('imports', 'message', 'only_csv_files_are_allowed');
33
+ const pickFileErrorLabel = global.t?.t('imports', 'error', 'error_selecting_file');
34
+ const importSuccessLabel = global.t?.t('imports', 'success', 'import_sent_successfully');
35
+ const importErrorLabel = global.t?.t('imports', 'error', 'error_sending_import');
36
+ const missingFileLabel = global.t?.t('imports', 'error', 'select_a_csv_file');
37
+
38
+ const handlePickFile = async () => {
39
+ try {
40
+ const result = await DocumentPicker.getDocumentAsync({
41
+ type: 'text/csv',
42
+ copyToCacheDirectory: true,
43
+ multiple: false,
44
+ });
45
+
46
+ if (result.canceled) return;
47
+
48
+ const pickedFile = result.assets[0];
49
+
50
+ if (!pickedFile.name?.toLowerCase().endsWith('.csv')) {
51
+ showError(csvOnlyLabel);
52
+ return;
53
+ }
54
+
55
+ setFile(pickedFile);
56
+ } catch {
57
+ showError(pickFileErrorLabel);
58
+ }
59
+ };
60
+
61
+ const handleSave = async () => {
62
+ if (!file) {
63
+ showError(missingFileLabel);
64
+ return;
65
+ }
66
+
67
+ setLoading(true);
68
+
69
+ try {
70
+ const formData = new FormData();
71
+ formData.append('importType', context.context);
72
+
73
+
74
+ formData.append('people', String(currentCompany.id));
75
+
76
+
77
+ if (Platform.OS === 'web') {
78
+ const response = await fetch(file.uri);
79
+ const blob = await response.blob();
80
+ formData.append('file', blob, file.name || 'import.csv');
81
+ } else {
82
+ formData.append('file', {
83
+ uri: file.uri,
84
+ name: file.name || 'import.csv',
85
+ type: file.mimeType || 'text/csv',
86
+ });
87
+ }
88
+
89
+ await api.upload('/imports/upload', formData);
90
+ showSuccess(importSuccessLabel);
91
+ if (onSuccess) onSuccess();
92
+ handleClose();
93
+ } catch {
94
+ showError(importErrorLabel);
95
+ } finally {
96
+ setLoading(false);
97
+ }
98
+ };
99
+
100
+ const handleClose = () => {
101
+ setFile(null);
102
+ onClose();
103
+ };
104
+
105
+ return (
106
+ <AnimatedModal
107
+ visible={visible}
108
+ onRequestClose={handleClose}
109
+ style={styles.modalAlignEnd}
110
+ >
111
+ <View style={styles.sheet}>
112
+ <View style={styles.header}>
113
+ <Text style={styles.title}>{modalTitle}</Text>
114
+ <TouchableOpacity onPress={handleClose}>
115
+ <Icon name="close" size={24} />
116
+ </TouchableOpacity>
117
+ </View>
118
+
119
+ <ScrollView style={styles.content}>
120
+ <Text style={styles.label}>{csvLabel}</Text>
121
+ <TouchableOpacity
122
+ onPress={handlePickFile}
123
+ style={styles.filePicker}
124
+ >
125
+ <Text numberOfLines={1} style={styles.fileName}>
126
+ {file ? file.name : selectFileLabel}
127
+ </Text>
128
+ <Icon name="upload-file" size={22} color={file ? "#007bff" : "#666"} />
129
+ </TouchableOpacity>
130
+ <Text style={styles.helperText}>
131
+ {csvOnlyLabel}
132
+ </Text>
133
+ </ScrollView>
134
+
135
+ <View style={styles.footer}>
136
+ <TouchableOpacity onPress={handleClose} style={styles.secondaryButton}>
137
+ <Text>{cancelLabel}</Text>
138
+ </TouchableOpacity>
139
+
140
+ <TouchableOpacity
141
+ onPress={handleSave}
142
+ disabled={loading}
143
+ style={[
144
+ styles.primaryButton,
145
+ loading && styles.primaryButtonDisabled,
146
+ ]}
147
+ >
148
+ {loading ? <ActivityIndicator color="#fff" /> : <Text style={styles.primaryButtonText}>{importLabel}</Text>}
149
+ </TouchableOpacity>
150
+ </View>
151
+ </View>
152
+ </AnimatedModal>
153
+ );
154
+ };
155
+
156
+ export default AddImportModal;
157
+ // TODO(store-first): quando este arquivo for mexido, mover a leitura para stores e evitar chamadas HTTP diretas quando o store ja resolver isso.
@@ -0,0 +1,81 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ const styles = StyleSheet.create({
4
+ modalAlignEnd: {
5
+ justifyContent: 'flex-end',
6
+ },
7
+ sheet: {
8
+ backgroundColor: '#fff',
9
+ borderTopLeftRadius: 24,
10
+ borderTopRightRadius: 24,
11
+ maxHeight: '90%',
12
+ width: '100%',
13
+ },
14
+ header: {
15
+ flexDirection: 'row',
16
+ justifyContent: 'space-between',
17
+ padding: 20,
18
+ borderBottomWidth: 1,
19
+ borderColor: '#eee',
20
+ },
21
+ title: {
22
+ fontSize: 20,
23
+ fontWeight: '700',
24
+ },
25
+ content: {
26
+ padding: 20,
27
+ },
28
+ label: {
29
+ marginBottom: 6,
30
+ fontWeight: '600',
31
+ },
32
+ filePicker: {
33
+ borderWidth: 1,
34
+ borderColor: '#ddd',
35
+ borderRadius: 10,
36
+ padding: 16,
37
+ flexDirection: 'row',
38
+ alignItems: 'center',
39
+ justifyContent: 'space-between',
40
+ },
41
+ fileName: {
42
+ flex: 1,
43
+ marginRight: 10,
44
+ },
45
+ helperText: {
46
+ marginTop: 10,
47
+ color: '#6c757d',
48
+ fontSize: 12,
49
+ },
50
+ footer: {
51
+ flexDirection: 'row',
52
+ padding: 20,
53
+ gap: 10,
54
+ borderTopWidth: 1,
55
+ borderColor: '#eee',
56
+ },
57
+ secondaryButton: {
58
+ flex: 1,
59
+ padding: 14,
60
+ borderRadius: 10,
61
+ borderWidth: 1,
62
+ borderColor: '#aaa',
63
+ alignItems: 'center',
64
+ },
65
+ primaryButton: {
66
+ flex: 1,
67
+ padding: 14,
68
+ borderRadius: 10,
69
+ backgroundColor: '#007bff',
70
+ alignItems: 'center',
71
+ },
72
+ primaryButtonDisabled: {
73
+ backgroundColor: '#ccc',
74
+ },
75
+ primaryButtonText: {
76
+ color: '#fff',
77
+ fontWeight: '600',
78
+ },
79
+ });
80
+
81
+ export default styles;
@@ -0,0 +1,219 @@
1
+ import React, {useEffect, useMemo, useRef} from 'react';
2
+ import {NativeModules} from 'react-native';
3
+ import {env as APP_ENV} from '@env';
4
+ import {useStore} from '@store';
5
+ import DeviceInfo from 'react-native-device-info';
6
+ import {resolveAppDomain} from '@controleonline/ui-common/src/utils/appDomain';
7
+ import {resolveApiEntryPoint} from '@controleonline/ui-common/src/utils/apiEntryPoint';
8
+ import {isWebRuntimeDevice} from '@controleonline/ui-common/src/react/utils/deviceRuntime';
9
+ import {normalizeDeviceId} from '@controleonline/ui-common/src/react/utils/paymentDevices';
10
+ import {
11
+ DEVICE_ALERT_SOUND_ENABLED_KEY,
12
+ DEVICE_ALERT_SOUND_URL_KEY,
13
+ DEVICE_RUNTIME_DEBUG_INFO_ENABLED_KEY,
14
+ isTruthyValue,
15
+ parseConfigsObject,
16
+ } from '@controleonline/ui-common/src/react/config/deviceConfigBootstrap';
17
+ import {
18
+ isManagerAppType,
19
+ resolveManagerOrderNotificationPreferences,
20
+ } from '@controleonline/ui-common/src/react/utils/managerOrderNotifications';
21
+ import {syncBackgroundRuntimeRegistration} from '@controleonline/ui-common/src/react/utils/backgroundRuntimeRegistration';
22
+ import {
23
+ DEFAULT_NETWORK_PRINTER_PORT,
24
+ DISPLAY_DEVICE_TYPE,
25
+ NETWORK_PRINTER_PORT_CONFIG_KEY,
26
+ PDV_DEVICE_TYPE,
27
+ getManagedPrinterDevices,
28
+ getPrinterHost,
29
+ normalizeDeviceType,
30
+ normalizePrinterPort,
31
+ } from '@controleonline/ui-common/src/react/utils/printerDevices';
32
+
33
+ const backgroundRuntimeModule = NativeModules?.BackgroundRuntime;
34
+
35
+ const safeTrim = value => String(value || '').trim();
36
+ const readSessionToken = () => {
37
+ try {
38
+ const session = JSON.parse(localStorage.getItem('session') || '{}');
39
+ return safeTrim(session?.token || session?.api_key);
40
+ } catch {
41
+ return '';
42
+ }
43
+ };
44
+
45
+ const BackgroundRuntimeBridge = () => {
46
+ const authStore = useStore('auth');
47
+ const peopleStore = useStore('people');
48
+ const deviceStore = useStore('device');
49
+ const deviceConfigStore = useStore('device_config');
50
+
51
+ const {isLogged, user} = authStore.getters;
52
+ const {currentCompany} = peopleStore.getters;
53
+ const {item: runtimeDevice} = deviceStore.getters;
54
+ const {
55
+ item: runtimeDeviceConfig,
56
+ items: companyDeviceConfigs = [],
57
+ } = deviceConfigStore.getters;
58
+
59
+ const lastRegistrationIdRef = useRef('');
60
+ const lastRegistrationPayloadRef = useRef('');
61
+
62
+ const bundleId = safeTrim(DeviceInfo.getBundleId());
63
+ const runtimeAppKey =
64
+ bundleId || safeTrim(APP_ENV.APP_TYPE).toUpperCase() || 'app';
65
+ const runtimeDeviceId = normalizeDeviceId(runtimeDevice?.id);
66
+ const runtimeDeviceType = normalizeDeviceType(
67
+ runtimeDeviceConfig?.type ||
68
+ runtimeDeviceConfig?.device?.type ||
69
+ runtimeDevice?.type,
70
+ );
71
+ const currentCompanyId = safeTrim(currentCompany?.id);
72
+ const sessionToken = readSessionToken();
73
+ const isManagerRuntime = isManagerAppType(APP_ENV?.APP_TYPE);
74
+ const managerOrderNotificationPreferences =
75
+ resolveManagerOrderNotificationPreferences(user);
76
+ const userAlertSoundUrl = safeTrim(
77
+ managerOrderNotificationPreferences.soundUrl,
78
+ );
79
+ const userAlertSoundEnabled =
80
+ isManagerRuntime &&
81
+ managerOrderNotificationPreferences.pushEnabled &&
82
+ managerOrderNotificationPreferences.soundEnabled;
83
+ const runtimeDeviceConfigs = parseConfigsObject(runtimeDeviceConfig?.configs);
84
+ const deviceAlertSoundEnabled = isTruthyValue(
85
+ runtimeDeviceConfigs?.[DEVICE_ALERT_SOUND_ENABLED_KEY],
86
+ );
87
+ const deviceAlertSoundUrl = safeTrim(
88
+ runtimeDeviceConfigs?.[DEVICE_ALERT_SOUND_URL_KEY],
89
+ );
90
+ const runtimeDebugInfoEnabled = isTruthyValue(
91
+ runtimeDeviceConfigs?.[DEVICE_RUNTIME_DEBUG_INFO_ENABLED_KEY],
92
+ );
93
+ const alertSoundEnabled = userAlertSoundEnabled || deviceAlertSoundEnabled;
94
+ const alertSoundUrl = userAlertSoundEnabled
95
+ ? userAlertSoundUrl
96
+ : deviceAlertSoundUrl;
97
+
98
+ const managedPrinters = useMemo(
99
+ () =>
100
+ getManagedPrinterDevices({
101
+ deviceConfigs: companyDeviceConfigs,
102
+ companyId: currentCompanyId,
103
+ managerDeviceId: runtimeDeviceId,
104
+ })
105
+ .map(printer => {
106
+ const host = safeTrim(getPrinterHost(printer));
107
+ const port = normalizePrinterPort(
108
+ printer?.configs?.[NETWORK_PRINTER_PORT_CONFIG_KEY] ||
109
+ DEFAULT_NETWORK_PRINTER_PORT,
110
+ );
111
+ const deviceId = normalizeDeviceId(printer?.device);
112
+
113
+ if (!host || !deviceId) {
114
+ return null;
115
+ }
116
+
117
+ return {
118
+ deviceId,
119
+ host,
120
+ port,
121
+ };
122
+ })
123
+ .filter(Boolean),
124
+ [companyDeviceConfigs, currentCompanyId, runtimeDeviceId],
125
+ );
126
+
127
+ const spoolDeviceIds = useMemo(() => {
128
+ if (!runtimeDeviceId || isWebRuntimeDevice(runtimeDevice)) {
129
+ return [];
130
+ }
131
+
132
+ if (runtimeDeviceType === PDV_DEVICE_TYPE) {
133
+ return Array.from(
134
+ new Set([
135
+ runtimeDeviceId,
136
+ ...managedPrinters.map(printer => normalizeDeviceId(printer?.deviceId)),
137
+ ].filter(Boolean)),
138
+ );
139
+ }
140
+
141
+ if (runtimeDeviceType === DISPLAY_DEVICE_TYPE) {
142
+ return Array.from(
143
+ new Set(
144
+ managedPrinters
145
+ .map(printer => normalizeDeviceId(printer?.deviceId))
146
+ .filter(Boolean),
147
+ ),
148
+ );
149
+ }
150
+
151
+ return [];
152
+ }, [managedPrinters, runtimeDevice, runtimeDeviceId, runtimeDeviceType]);
153
+
154
+ const registration = useMemo(() => {
155
+ if (!isLogged || !sessionToken || !runtimeDeviceId || !currentCompanyId) {
156
+ return null;
157
+ }
158
+
159
+ return {
160
+ registrationId: `${runtimeAppKey}::${runtimeDeviceId}::${currentCompanyId}`,
161
+ packageName: runtimeAppKey,
162
+ companyAlias: safeTrim(currentCompany?.alias || currentCompany?.name),
163
+ companyId: currentCompanyId,
164
+ appDomain: safeTrim(resolveAppDomain(APP_ENV.DOMAIN)),
165
+ appType: safeTrim(APP_ENV.APP_TYPE).toUpperCase(),
166
+ backgroundEnabled: true,
167
+ deviceId: runtimeDeviceId,
168
+ deviceType: runtimeDeviceType,
169
+ notificationEnabled: !isManagerRuntime,
170
+ alertSoundEnabled,
171
+ alertSoundUrl,
172
+ deviceAlertSoundEnabled,
173
+ deviceAlertSoundUrl,
174
+ userAlertSoundEnabled,
175
+ userAlertSoundUrl,
176
+ runtimeDebugInfoEnabled,
177
+ printEnabled: spoolDeviceIds.length > 0 && managedPrinters.length > 0,
178
+ managedPrinters,
179
+ socketUrl: safeTrim(APP_ENV.SOCKET),
180
+ spoolDeviceIds,
181
+ token: sessionToken,
182
+ apiBaseUrl: safeTrim(resolveApiEntryPoint(APP_ENV.API_ENTRYPOINT)),
183
+ };
184
+ }, [
185
+ currentCompany?.alias,
186
+ currentCompany?.name,
187
+ currentCompanyId,
188
+ alertSoundEnabled,
189
+ alertSoundUrl,
190
+ deviceAlertSoundEnabled,
191
+ deviceAlertSoundUrl,
192
+ runtimeAppKey,
193
+ isLogged,
194
+ isManagerRuntime,
195
+ managedPrinters,
196
+ runtimeDeviceId,
197
+ runtimeDeviceType,
198
+ sessionToken,
199
+ spoolDeviceIds,
200
+ userAlertSoundEnabled,
201
+ userAlertSoundUrl,
202
+ runtimeDebugInfoEnabled,
203
+ ]);
204
+
205
+ useEffect(() => {
206
+ syncBackgroundRuntimeRegistration({
207
+ backgroundRuntimeModule,
208
+ registration,
209
+ lastRegistrationIdRef,
210
+ lastRegistrationPayloadRef,
211
+ });
212
+ // Keep the registration alive after this component unmounts.
213
+ // Explicit logout/device changes are handled by the branches above.
214
+ }, [registration]);
215
+
216
+ return null;
217
+ };
218
+
219
+ export default BackgroundRuntimeBridge;
@@ -0,0 +1,238 @@
1
+ const translateLabel = (translate, descriptor) => {
2
+ if (!descriptor || typeof descriptor === 'string') {
3
+ return descriptor || '';
4
+ }
5
+
6
+ const translated = translate?.(
7
+ descriptor.store,
8
+ descriptor.type,
9
+ descriptor.key,
10
+ );
11
+
12
+ return translated || descriptor.fallback || descriptor.key || '';
13
+ };
14
+
15
+ export const resolveBottomNavigationItems = (items = [], translate) =>
16
+ (Array.isArray(items) ? items : []).map(item => ({
17
+ ...item,
18
+ label: translateLabel(translate, item?.label),
19
+ }));
20
+
21
+ export const resolveBottomNavigationRoute = (routeAliases = {}, routeName) =>
22
+ routeAliases?.[routeName] || routeName || '';
23
+
24
+ export const BOTTOM_NAVIGATION_PRESETS = {
25
+ crmToolbar: {
26
+ items: [
27
+ {
28
+ route: 'HomePage',
29
+ icon: 'home',
30
+ label: {
31
+ store: 'users',
32
+ type: 'label',
33
+ key: 'home',
34
+ fallback: 'Home',
35
+ },
36
+ },
37
+ {
38
+ route: 'CrmIndex',
39
+ icon: 'target',
40
+ label: {
41
+ store: 'users',
42
+ type: 'label',
43
+ key: 'crm',
44
+ fallback: 'CRM',
45
+ },
46
+ },
47
+ {
48
+ route: 'ClientsIndex',
49
+ icon: 'users',
50
+ label: {
51
+ store: 'users',
52
+ type: 'label',
53
+ key: 'clients',
54
+ fallback: 'Clients',
55
+ },
56
+ },
57
+ {
58
+ route: 'ProfilePage',
59
+ icon: 'user',
60
+ label: {
61
+ store: 'users',
62
+ type: 'label',
63
+ key: 'profile',
64
+ fallback: 'Profile',
65
+ },
66
+ },
67
+ ],
68
+ routeAliases: {
69
+ HomePage: 'HomePage',
70
+ CrmIndex: 'CrmIndex',
71
+ ContractsIndex: 'CrmIndex',
72
+ ProposalsIndex: 'CrmIndex',
73
+ ComissionsPage: 'CrmIndex',
74
+ CrmConversation: 'CrmIndex',
75
+ ContractDetails: 'CrmIndex',
76
+ ClientsIndex: 'ClientsIndex',
77
+ ClientDetails: 'ClientsIndex',
78
+ FranchiseesIndex: 'ClientsIndex',
79
+ ProfilePage: 'ProfilePage',
80
+ SettingsPage: 'ProfilePage',
81
+ },
82
+ },
83
+ managerToolbar: {
84
+ items: [
85
+ {
86
+ route: 'HomePage',
87
+ icon: 'home',
88
+ label: {
89
+ store: 'configs',
90
+ type: 'toolbar',
91
+ key: 'home',
92
+ fallback: 'Home',
93
+ },
94
+ },
95
+ {
96
+ route: 'CrmIndex',
97
+ icon: 'dollar-sign',
98
+ label: {
99
+ store: 'configs',
100
+ type: 'toolbar',
101
+ key: 'opportunities',
102
+ fallback: 'Oportunidades',
103
+ },
104
+ },
105
+ {
106
+ route: 'ClientsIndex',
107
+ icon: 'users',
108
+ label: {
109
+ store: 'configs',
110
+ type: 'toolbar',
111
+ key: 'customers',
112
+ fallback: 'Clientes',
113
+ },
114
+ },
115
+ {
116
+ route: 'ProfilePage',
117
+ icon: 'user',
118
+ label: {
119
+ store: 'configs',
120
+ type: 'toolbar',
121
+ key: 'profile',
122
+ fallback: 'Profile',
123
+ },
124
+ },
125
+ ],
126
+ routeAliases: {
127
+ HomePage: 'HomePage',
128
+ CrmIndex: 'CrmIndex',
129
+ ContractsIndex: 'CrmIndex',
130
+ ProposalsIndex: 'CrmIndex',
131
+ ComissionsPage: 'CrmIndex',
132
+ CrmConversation: 'CrmIndex',
133
+ ContractDetails: 'CrmIndex',
134
+ ClientsIndex: 'ClientsIndex',
135
+ ClientDetails: 'ClientsIndex',
136
+ FranchiseesIndex: 'ClientsIndex',
137
+ ProfilePage: 'ProfilePage',
138
+ SettingsPage: 'ProfilePage',
139
+ OrderHistoryPage: 'CrmIndex',
140
+ InventoriesPage: 'CrmIndex',
141
+ ProductsPage: 'CrmIndex',
142
+ },
143
+ },
144
+ managerDock: {
145
+ items: [
146
+ {route: 'HomePage', icon: 'home', label: 'Home'},
147
+ {route: 'CrmIndex', icon: 'dollar-sign', label: 'Oportunidades'},
148
+ {route: 'ClientsIndex', icon: 'users', label: 'Clientes'},
149
+ {route: 'ProfilePage', icon: 'user', label: 'Perfil'},
150
+ ],
151
+ routeAliases: {
152
+ ClientDetails: 'ClientsIndex',
153
+ EmployeesIndex: 'ClientsIndex',
154
+ FranchiseesIndex: 'ClientsIndex',
155
+ },
156
+ },
157
+ deliveryDock: {
158
+ items: [
159
+ {route: 'HomePage', icon: 'home', label: 'Home'},
160
+ {route: 'DeliveryOrdersPage', icon: 'shopping-bag', label: 'Pedidos'},
161
+ {
162
+ route: 'DeliveryReceivablesPage',
163
+ icon: 'dollar-sign',
164
+ label: 'Recebiveis',
165
+ },
166
+ {route: 'DeliveryCompaniesPage', icon: 'users', label: 'Empresas'},
167
+ {route: 'DeliveryRateTablesPage', icon: 'list', label: 'Tabelas'},
168
+ ],
169
+ routeAliases: {
170
+ OrderDetails: 'DeliveryOrdersPage',
171
+ OrderLogisticsPage: 'DeliveryOrdersPage',
172
+ DeliveryRunPage: 'DeliveryOrdersPage',
173
+ DeliveryVehicleSetupPage: 'DeliveryRateTablesPage',
174
+ DeliveryRateTableFormPage: 'DeliveryRateTablesPage',
175
+ DeliveryRateTableCompaniesPage: 'DeliveryRateTablesPage',
176
+ DeliveryCourierSchedulesPage: 'DeliveryCompaniesPage',
177
+ DeliveryCourierScheduleFormPage: 'DeliveryCompaniesPage',
178
+ DeliveryCourierPresencePage: 'DeliveryCompaniesPage',
179
+ DeliveryCourierPresenceHistoryPage: 'DeliveryCompaniesPage',
180
+ },
181
+ },
182
+ ppcDock: {
183
+ items: [
184
+ {route: 'HomePage', icon: 'home', label: 'Home'},
185
+ {route: 'DisplayList', icon: 'monitor', label: 'Displays'},
186
+ {route: 'ProfilePage', icon: 'user', label: 'Profile'},
187
+ ],
188
+ routeAliases: {},
189
+ },
190
+ posToolbar: {
191
+ items: [
192
+ {
193
+ route: 'HomePage',
194
+ icon: 'home',
195
+ label: {
196
+ store: 'orders',
197
+ type: 'label',
198
+ key: 'home',
199
+ fallback: 'Home',
200
+ },
201
+ },
202
+ {
203
+ route: 'OrderHistoryPage',
204
+ icon: 'shopping-bag',
205
+ label: {
206
+ store: 'orders',
207
+ type: 'label',
208
+ key: 'orders',
209
+ fallback: 'Orders',
210
+ },
211
+ },
212
+ {
213
+ route: 'CashRegisterIndex',
214
+ icon: 'credit-card',
215
+ label: {
216
+ store: 'orders',
217
+ type: 'title',
218
+ key: 'cashRegister',
219
+ fallback: 'Cash register',
220
+ },
221
+ },
222
+ {
223
+ route: 'ProfilePage',
224
+ icon: 'user',
225
+ label: {
226
+ store: 'orders',
227
+ type: 'label',
228
+ key: 'profile',
229
+ fallback: 'Profile',
230
+ },
231
+ },
232
+ ],
233
+ routeAliases: {},
234
+ },
235
+ };
236
+
237
+ export const getBottomNavigationPreset = variant =>
238
+ BOTTOM_NAVIGATION_PRESETS[variant] || BOTTOM_NAVIGATION_PRESETS.managerDock;