@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,1171 @@
1
+ /*
2
+ * Contract imported from AGENTS.md
3
+ * ## Escopo
4
+ * - Modulo compartilhado de infraestrutura do front.
5
+ * - Reune providers, servicos globais, componentes comuns, rotas compartilhadas, stores e utils.
6
+ *
7
+ * ## Estado
8
+ * - Este modulo tem implementacao ativa em `src/react` e deve constar em novos prompts.
9
+ *
10
+ * ## Quando usar
11
+ * - Prompts sobre helpers compartilhados, `DefaultProvider`, mensageria, impressao, importacao, logs, devices, gateways e utilitarios cross-app.
12
+ * - O helper compartilhado de dinheiro/manual fica aqui e pode ser reutilizado por `ui-orders`, listeners remotos e outros gateways.
13
+ * - A execucao tecnica dos gateways operacionais compartilhados, como `Cielo` e `Infinite Pay`, pode ficar aqui para ser reutilizada pelo checkout principal e pelo listener remoto.
14
+ * - O protocolo de ida e volta do pagamento remoto entre web e device deve ficar centralizado aqui, para que checkout e listener remoto compartilhem a mesma chave de requisicao e o mesmo formato de resposta.
15
+ * - O bridge nativo de kiosk fica aqui e deve apenas ligar/desligar a infraestrutura Android quando `APP_TYPE=POS` e a configuracao `android-kiosk-enabled` estiver ligada. A regra de fluxo do totem continua no modulo dono e nao depende do modo operacional salvo.
16
+ *
17
+ * ## Limites
18
+ * - Nao mover para `ui-common` regra de negocio que pertence claramente a `ui-orders`, `ui-shop`, `ui-manager` ou outro modulo dono.
19
+ * - `ui-common` pode centralizar a parte tecnica de pagamento sem gateway, mas a decisao de quando usar dinheiro continua no modulo dono do fluxo.
20
+ * - `ui-common` nao deve montar uma segunda UI de checkout para gateway. Quando houver listener remoto, ele deve executar o mesmo helper compartilhado do fluxo principal.
21
+ * - Quando a implementacao tecnica do gateway ficar melhor organizada em arquivos separados por provedor no modulo dono, `ui-common` deve apenas orquestrar ou reaproveitar esses arquivos, sem recriar fluxo paralelo.
22
+ *
23
+ * ## Regras de mensageria
24
+ * - Erros visuais transitrios do sistema devem sair por um unico componente compartilhado ligado ao `MessageService`.
25
+ * - `showError` e o ponto canonico para esse feedback. Ele recebe string ou objeto de erro, exibe por alguns segundos e some sozinho.
26
+ * - O card/modal transitorio de erro do `MessageService` e a unica UI permitida para esse feedback. Nao criar segunda tela de erro no modulo.
27
+ * - Stores compartilhadas tambem devem usar esse fluxo. Quando uma store fizer `SET_ERROR`, o erro visual precisa sair pelo `MessageService`, nunca por caixa local do `StateStore`.
28
+ * - `StateStore` e o componente canonico de loading/saving. Ele pode receber `store` e `stores` para configurar quais estados acompanhar, mas nao deve ser substituido por loaders locais nas telas consumidoras.
29
+ * - O `StateStore` deve ler `isLoading` e `isSaving` diretamente dos stores; o erro deve entrar pelo contrato de `SET_ERROR` e ser publicado pelo `storeErrorBridge`, que alimenta o `MessageService`.
30
+ * - Nao criar banners, alerts ou toasts paralelos para erro quando o caso puder usar o `MessageService`.
31
+ * - O contrato canonico de erro HTTP do backend e o envelope do `HydratorService` com `@type: Error`, `hydra:title` e `hydra:description`; o `fetch` e os parsers compartilhados devem ler esse formato como fonte principal de mensagem.
32
+ *
33
+ * ## Regras de runtime em background
34
+ * - O `BackgroundRuntimeBridge` deve manter o registro nativo por package/app, device e empresa para permitir varios APKs instalados no mesmo Android.
35
+ * - O runtime de background pode ser religado pelo Android com `BOOT_COMPLETED` e `MY_PACKAGE_REPLACED`, reutilizando as inscricoes persistidas para seguir notificando mesmo sem a UI aberta.
36
+ * - Em nativo, `WebsocketListener.native.js` deve consumir o stream local exposto pelo `BackgroundRuntimeService`, nao abrir websocket direto no backend.
37
+ * - `WebsocketListener.web.js` e o fluxo web e deve usar runtime compartilhado com owner unico no browser via BroadcastChannel; nao abrir websocket direto do backend nem depender do runtime Android.
38
+ * - Mensagens entregues pelo runtime nativo devem ser marcadas com `source: 'background-runtime'`, mas todos os apps com som configurado devem continuar processando `order.created` para aviso sonoro mesmo que a notificacao do sistema tambem apareca.
39
+ * - O som configurado em `device_config` para `order.created` vale para qualquer `APP_TYPE`, incluindo KDS, Manager e PDV; deve ser enviado ao `BackgroundRuntimeService` como configuracao de device e tocado nativamente para funcionar mesmo com o app fechado.
40
+ * - A configuracao de som do Manager e por usuario e separada da configuracao do device; ela tambem deve ser enviada ao `BackgroundRuntimeService` como configuracao de usuario, sem virar regra global dos demais apps.
41
+ * - Quando a URL personalizada de audio estiver vazia, o runtime deve cair para o asset `src/assets/sound/caixa.m4a` empacotado no app. URL personalizada continua vencendo o fallback.
42
+ * - No `MANAGER` Android, o push FCM humano usa canal nativo com `caixa.m4a`; URL personalizada nao pode tocar quando a notificacao chega com o app fechado.
43
+ * - Alteracoes no protocolo local do runtime precisam manter compatibilidade entre o template do plugin e qualquer arquivo Android gerado.
44
+ *
45
+ * ## Regras de UI compartilhada
46
+ * - Componentes que implementam comportamento default de listagem/filtros pertencem a `ui-default`.
47
+ * - `ui-common` pode fornecer helpers e utilitarios usados por esses componentes, mas nao deve manter uma segunda implementacao de `DateShortcutFilter`, `CompactFilterSelector` ou componentes equivalentes de filtro de listagem.
48
+ * - `DefaultProvider` carrega `menus-people` com `myCompany` e `APP_TYPE` atual e grava o resultado normalizado em `theme.menus`.
49
+ * - Normalizacao de payload de menu runtime deve ficar em helper compartilhado neste modulo.
50
+ * - O `RuntimeInfoFooter` deve montar o texto como `nome (identificador) / versao`, usando em runtime web o IP externo lido de `/runtime/ip` e persistido na metadata do device, com `location.hostname` apenas como fallback. No nativo, deve priorizar a versao persistida na metadata do device salvo no backend e o mesmo identificador mostrado na lista de devices (`device_config.device.device`), com fallback local quando o backend ainda nao tiver esse valor.
51
+ * - O help contextual canônico deve ficar em `ui-common` como componente parametrizado acionado por `?`, com modal proprio e sem reaproveitar `ConfirmModal`. Telas consumidoras nao devem escrever explicacao fixa quando esse padrao estiver disponivel.
52
+ */
53
+
54
+ import React, {useEffect, useState, useCallback, useMemo} from 'react';
55
+
56
+ import {
57
+ Text,
58
+ View,
59
+ ScrollView,
60
+ ActivityIndicator,
61
+ TouchableOpacity,
62
+ Switch,
63
+ Alert,
64
+ Platform,
65
+ TextInput,
66
+ } from 'react-native';
67
+
68
+ import {SafeAreaView} from 'react-native-safe-area-context';
69
+ import css from '@controleonline/ui-orders/src/react/css/orders';
70
+ import {useStore} from '@store';
71
+ import {useFocusEffect, useNavigation} from '@react-navigation/native';
72
+ import {Picker} from '@react-native-picker/picker';
73
+ import Icon from 'react-native-vector-icons/MaterialIcons';
74
+ import StateStore from '@controleonline/ui-layout/src/react/components/StateStore';
75
+ import packageJson from '@package';
76
+ import PaymentTypesByWalletTab from './PaymentTypesByWalletTab';
77
+
78
+ import {
79
+ appendScreenMetrics,
80
+ buildScreenMetrics,
81
+ hasScreenMetricsChanges,
82
+ } from '@controleonline/ui-common/src/react/utils/screenMetrics';
83
+
84
+ import {
85
+ CIELO_DEVICES,
86
+ DEVICE_ALERT_SOUND_ENABLED_KEY,
87
+ DEVICE_ALERT_SOUND_URL_KEY,
88
+ DEVICE_ORDER_VISIBILITY_COMPANY,
89
+ DEVICE_ORDER_VISIBILITY_DEVICE,
90
+ DEVICE_ORDER_VISIBILITY_KEY,
91
+ DEVICE_RUNTIME_DEBUG_INFO_ENABLED_KEY,
92
+ isTruthyValue,
93
+ parseConfigsObject,
94
+ POS_CHECK_ORDER_MANAGEMENT_MODE_CONFIG_KEY,
95
+ POS_CHECK_ORDER_MANAGEMENT_MODE_EXISTING_ONLY,
96
+ POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE,
97
+ POS_CHECK_ORDER_TYPE_CONFIG_KEY,
98
+ POS_CHECK_ORDER_TYPE_NONE,
99
+ POS_CHECK_ORDER_TYPE_TAB,
100
+ POS_CHECK_ORDER_TYPE_TABLE,
101
+ resolveDefaultGateway,
102
+ resolveDeviceOrderVisibility,
103
+ resolvePosCheckOrderManagementMode,
104
+ resolvePosCheckOrderType,
105
+ } from '@controleonline/ui-common/src/react/config/deviceConfigBootstrap';
106
+ import {
107
+ PAYMENT_TYPE_IDS_CONFIG_KEY,
108
+ } from '@controleonline/ui-common/src/react/utils/paymentDevices';
109
+
110
+ import {isWebRuntimeDevice as resolveIsWebRuntimeDevice} from '@controleonline/ui-common/src/react/utils/deviceRuntime';
111
+ import {DEFAULT_NOTIFICATION_SOUND_FILE} from '@controleonline/ui-common/src/react/utils/notificationSound';
112
+
113
+ import {
114
+ inlineStyle_565_16,
115
+ inlineStyle_639_14,
116
+ inlineStyle_650_20,
117
+ inlineStyle_659_16,
118
+ inlineStyle_676_16,
119
+ inlineStyle_690_16,
120
+ inlineStyle_704_12,
121
+ inlineStyle_720_12,
122
+ inlineStyle_736_12,
123
+ inlineStyle_743_14,
124
+ inlineStyle_757_14,
125
+ inlineStyle_783_14,
126
+ inlineStyle_798_12,
127
+ inlineStyle_805_14,
128
+ inlineStyle_818_14,
129
+ inlineStyle_830_16,
130
+ inlineStyle_841_16,
131
+ inlineStyle_852_16,
132
+ inlineStyle_871_18,
133
+ inlineStyle_899_18,
134
+ } from './index.styles';
135
+
136
+ const Settings = () => {
137
+ const navigation = useNavigation();
138
+ const {styles, globalStyles} = css();
139
+ const walletStore = useStore('wallet');
140
+ const walletGetters = walletStore.getters;
141
+ const peopleStore = useStore('people');
142
+ const peopleGetters = peopleStore.getters;
143
+ const configsStore = useStore('configs');
144
+ const configsGetters = configsStore.getters;
145
+ const configActions = configsStore.actions;
146
+ const device_configStore = useStore('device_config');
147
+ const deviceConfigGetters = device_configStore.getters;
148
+ const deviceConfigsActions = device_configStore.actions;
149
+ const {item: device, isSaving: isDeviceConfigSaving} = deviceConfigGetters;
150
+ const {currentCompany} = peopleGetters;
151
+ const {isLoading: walletLoading} = walletGetters;
152
+ const {items: companyConfigs, isSaving} = configsGetters;
153
+ const withdrawWallet =
154
+ companyConfigs?.['pos-withdrawl-wallet'] ||
155
+ companyConfigs?.['pos-withdrawal-wallet'];
156
+
157
+ const [selectedMode, setSelectedMode] = useState(null);
158
+ const [printingMode, setPrintingMode] = useState('order');
159
+ const [selectedGateway, setSelectedGateway] = useState(null);
160
+ const [discovered, setDiscovered] = useState(false);
161
+ const deviceStore = useStore('device');
162
+ const deviceGetters = deviceStore.getters;
163
+ const {item: storagedDevice} = deviceGetters;
164
+ const packageVersion = packageJson?.version || packageJson?.default?.version;
165
+ const appVersion = packageVersion || storagedDevice?.appVersion;
166
+
167
+ const [checkType, setCheckType] = useState('manual');
168
+ const [checkOrderType, setCheckOrderType] = useState(POS_CHECK_ORDER_TYPE_NONE);
169
+ const [checkOrderManagementMode, setCheckOrderManagementMode] = useState(
170
+ POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE,
171
+ );
172
+ const [productInputType, setProductInputType] = useState('manual');
173
+ const [selectionType, setSelectionType] = useState('single');
174
+ const [showSound, setShowSound] = useState(false);
175
+ const [showVibration, setShowVibration] = useState(false);
176
+ const [orderVisibility, setOrderVisibility] = useState(DEVICE_ORDER_VISIBILITY_DEVICE);
177
+ const [alertSoundEnabled, setAlertSoundEnabled] = useState(false);
178
+ const [alertSoundUrl, setAlertSoundUrl] = useState('');
179
+ const [showRuntimeDebugInfo, setShowRuntimeDebugInfo] = useState(false);
180
+ const [configsLoaded, setConfigsLoaded] = useState(false);
181
+ const [deviceConfigsLoaded, setDeviceConfigsLoaded] = useState(false);
182
+ const [activeSettingsTab, setActiveSettingsTab] = useState('general');
183
+ const pickerMode = Platform.OS === 'android' ? 'dropdown' : undefined;
184
+ const settingsTabStyles = {
185
+ tabRow: {
186
+ flexDirection: 'row',
187
+ marginTop: 16,
188
+ marginBottom: 12,
189
+ borderRadius: 12,
190
+ borderColor: '#D6E4F0',
191
+ borderWidth: 1,
192
+ overflow: 'hidden',
193
+ backgroundColor: '#F8FAFC',
194
+ },
195
+ tabButton: {
196
+ alignItems: 'center',
197
+ backgroundColor: '#F8FAFC',
198
+ flex: 1,
199
+ justifyContent: 'center',
200
+ paddingVertical: 12,
201
+ },
202
+ tabButtonActive: {
203
+ backgroundColor: '#1B5587',
204
+ },
205
+ tabLabel: {
206
+ color: '#334155',
207
+ fontSize: 13,
208
+ fontWeight: '700',
209
+ },
210
+ tabLabelActive: {
211
+ color: '#FFFFFF',
212
+ },
213
+ };
214
+
215
+ const defaultGateway = resolveDefaultGateway(storagedDevice);
216
+ const deviceManufacturer = String(storagedDevice?.manufacturer || '').toLowerCase();
217
+ const isEmulatorDevice = isTruthyValue(storagedDevice?.isEmulator);
218
+ const isCieloDevice = CIELO_DEVICES.includes(deviceManufacturer);
219
+ const showGatewayPicker = !isCieloDevice || isEmulatorDevice;
220
+ const allowCieloOption = isCieloDevice || isEmulatorDevice;
221
+ const isWebRuntimeDevice = resolveIsWebRuntimeDevice(storagedDevice);
222
+
223
+ const screenMetrics = useMemo(() => buildScreenMetrics(), []);
224
+ const runtimeCompanyConfigs = useMemo(
225
+ () =>
226
+ parseConfigsObject(
227
+ companyConfigs && Object.keys(companyConfigs).length > 0
228
+ ? companyConfigs
229
+ : currentCompany?.configs,
230
+ ),
231
+ [companyConfigs, currentCompany?.configs],
232
+ );
233
+
234
+ const applyRuntimeCompanyConfigs = useCallback(() => {
235
+ const nextItem = {
236
+ ...(device || {}),
237
+ device: {
238
+ id: storagedDevice?.id,
239
+ device: storagedDevice?.id,
240
+ },
241
+ people: currentCompany?.id ? '/people/' + currentCompany.id : device?.people,
242
+ configs: runtimeCompanyConfigs,
243
+ };
244
+
245
+ deviceConfigsActions.setItem(nextItem);
246
+ return nextItem;
247
+ }, [
248
+ currentCompany?.id,
249
+ device,
250
+ deviceConfigsActions,
251
+ runtimeCompanyConfigs,
252
+ storagedDevice?.id,
253
+ ]);
254
+
255
+ const persistDeviceConfigs = useCallback(
256
+ nextConfigs => {
257
+ if (!currentCompany?.id) {
258
+ return Promise.resolve(null);
259
+ }
260
+
261
+ return deviceConfigsActions.addDeviceConfigs({
262
+ configs: JSON.stringify(nextConfigs),
263
+ people: '/people/' + currentCompany.id,
264
+ });
265
+ },
266
+ [
267
+ currentCompany?.id,
268
+ deviceConfigsActions,
269
+ ],
270
+ );
271
+
272
+ const persistCompanyPaymentConfigs = useCallback(
273
+ nextSelectedPaymentTypeIds => {
274
+ if (!currentCompany?.id) {
275
+ return Promise.resolve(null);
276
+ }
277
+
278
+ return configActions
279
+ .addConfigs({
280
+ configKey: PAYMENT_TYPE_IDS_CONFIG_KEY,
281
+ configValue: JSON.stringify(nextSelectedPaymentTypeIds || []),
282
+ people: '/people/' + currentCompany.id,
283
+ module: 4,
284
+ visibility: 'public',
285
+ })
286
+ .then(async data => {
287
+ await configActions.discoveryMainConfigs({
288
+ people: '/people/' + currentCompany.id,
289
+ });
290
+ return data;
291
+ });
292
+ },
293
+ [configActions, currentCompany?.id],
294
+ );
295
+
296
+ const createDefaultConfigs = useCallback(() => {
297
+
298
+ // ALEMAC //
299
+ if (!currentCompany?.id || configsLoaded || device === undefined || device === null) return;
300
+
301
+ if (isWebRuntimeDevice) {
302
+ applyRuntimeCompanyConfigs();
303
+ setConfigsLoaded(true);
304
+ return;
305
+ }
306
+
307
+ let lc = {...(device?.configs || {})};
308
+ let needsUpdate = false;
309
+
310
+ const metricsConfigs = appendScreenMetrics(lc);
311
+ if (hasScreenMetricsChanges(lc, metricsConfigs)) {
312
+ lc = metricsConfigs;
313
+ needsUpdate = true;
314
+ }
315
+
316
+ // Verifica cada config e cria o padrão se não existir
317
+ if (!lc['pos-type']) {
318
+ lc['pos-type'] = 'full';
319
+ needsUpdate = true;
320
+ }
321
+ if (!lc['print-mode']) {
322
+ lc['print-mode'] = 'order';
323
+ needsUpdate = true;
324
+ }
325
+ if (!lc['check-type']) {
326
+ lc['check-type'] = 'manual';
327
+ needsUpdate = true;
328
+ }
329
+ if (!lc[POS_CHECK_ORDER_TYPE_CONFIG_KEY]) {
330
+ lc[POS_CHECK_ORDER_TYPE_CONFIG_KEY] = POS_CHECK_ORDER_TYPE_NONE;
331
+ needsUpdate = true;
332
+ }
333
+ if (!lc[POS_CHECK_ORDER_MANAGEMENT_MODE_CONFIG_KEY]) {
334
+ lc[POS_CHECK_ORDER_MANAGEMENT_MODE_CONFIG_KEY] =
335
+ POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE;
336
+ needsUpdate = true;
337
+ }
338
+ if (!lc['product-input-type']) {
339
+ lc['product-input-type'] = 'manual';
340
+ needsUpdate = true;
341
+ }
342
+ if (!lc['selection-type']) {
343
+ lc['selection-type'] = 'single';
344
+ needsUpdate = true;
345
+ }
346
+ if (!lc['sound']) {
347
+ lc['sound'] = '0';
348
+ needsUpdate = true;
349
+ }
350
+ if (!lc['vibration']) {
351
+ lc['vibration'] = '0';
352
+ needsUpdate = true;
353
+ }
354
+ if (!lc[DEVICE_ORDER_VISIBILITY_KEY]) {
355
+ lc[DEVICE_ORDER_VISIBILITY_KEY] = DEVICE_ORDER_VISIBILITY_DEVICE;
356
+ needsUpdate = true;
357
+ }
358
+ if (lc[DEVICE_ALERT_SOUND_ENABLED_KEY] === undefined || lc[DEVICE_ALERT_SOUND_ENABLED_KEY] === null) {
359
+ lc[DEVICE_ALERT_SOUND_ENABLED_KEY] = '0';
360
+ needsUpdate = true;
361
+ }
362
+ if (lc[DEVICE_ALERT_SOUND_URL_KEY] === undefined || lc[DEVICE_ALERT_SOUND_URL_KEY] === null) {
363
+ lc[DEVICE_ALERT_SOUND_URL_KEY] = '';
364
+ needsUpdate = true;
365
+ }
366
+ if (lc[DEVICE_RUNTIME_DEBUG_INFO_ENABLED_KEY] === undefined || lc[DEVICE_RUNTIME_DEBUG_INFO_ENABLED_KEY] === null) {
367
+ lc[DEVICE_RUNTIME_DEBUG_INFO_ENABLED_KEY] = '0';
368
+ needsUpdate = true;
369
+ }
370
+ if (!lc['config-version']) {
371
+ // ALEMAC // pega o appVersion ao invés do buildNumber
372
+ lc['config-version'] = appVersion;
373
+ needsUpdate = true;
374
+ }
375
+ if (!lc['pos-gateway']) {
376
+ lc['pos-gateway'] = defaultGateway;
377
+ needsUpdate = true;
378
+ }
379
+
380
+ // Se faltava alguma coisa, grava tudo no banco
381
+ if (needsUpdate && currentCompany?.id) {
382
+ persistDeviceConfigs(lc)
383
+ .then(() => {
384
+ setConfigsLoaded(true);
385
+ })
386
+ .catch(err => {
387
+ console.error('addDeviceConfigs (createDefaultConfigs) failed:', err);
388
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
389
+ });
390
+
391
+ return;
392
+ }
393
+
394
+ setConfigsLoaded(true);
395
+ }, [
396
+ device,
397
+ currentCompany?.id,
398
+ configsLoaded,
399
+ storagedDevice,
400
+ defaultGateway,
401
+ appVersion,
402
+ applyRuntimeCompanyConfigs,
403
+ deviceConfigsActions,
404
+ isWebRuntimeDevice,
405
+ persistDeviceConfigs,
406
+ ]);
407
+
408
+ useFocusEffect(
409
+ useCallback(() => {
410
+ setDeviceConfigsLoaded(false);
411
+ setConfigsLoaded(false);
412
+ setDiscovered(false);
413
+ }, []),
414
+ );
415
+
416
+ useFocusEffect(
417
+ useCallback(() => {
418
+ if (!currentCompany?.id || !storagedDevice?.id) {
419
+ return;
420
+ }
421
+
422
+ if (isWebRuntimeDevice) {
423
+ applyRuntimeCompanyConfigs();
424
+ setDeviceConfigsLoaded(true);
425
+ return;
426
+ }
427
+
428
+ deviceConfigsActions
429
+ .getItems({
430
+ 'device.device': storagedDevice.id,
431
+ people: '/people/' + currentCompany.id,
432
+ })
433
+ .then(data => {
434
+ if (Array.isArray(data) && data.length > 0) {
435
+ let parsedConfigs = {};
436
+
437
+ if (typeof data[0]?.configs === 'string') {
438
+ try {
439
+ parsedConfigs = JSON.parse(data[0].configs);
440
+ } catch {
441
+ parsedConfigs = {};
442
+ }
443
+ } else if (typeof data[0]?.configs === 'object') {
444
+ parsedConfigs = data[0].configs || {};
445
+ }
446
+
447
+ deviceConfigsActions.setItem({
448
+ ...data[0],
449
+ configs: parsedConfigs,
450
+ });
451
+ return;
452
+ }
453
+
454
+ deviceConfigsActions.setItem({});
455
+ })
456
+ .catch(() => {
457
+ deviceConfigsActions.setItem({});
458
+ })
459
+ .finally(() => {
460
+ setDeviceConfigsLoaded(true);
461
+ });
462
+ }, [
463
+ applyRuntimeCompanyConfigs,
464
+ currentCompany?.id,
465
+ deviceConfigsActions,
466
+ isWebRuntimeDevice,
467
+ storagedDevice?.id,
468
+ ]),
469
+ );
470
+
471
+
472
+ useFocusEffect(
473
+ useCallback(() => {
474
+ if (deviceConfigsLoaded) {
475
+ createDefaultConfigs();
476
+ }
477
+ }, [deviceConfigsLoaded, createDefaultConfigs]),
478
+ );
479
+
480
+ useEffect(() => {
481
+ if (isWebRuntimeDevice && deviceConfigsLoaded && currentCompany?.id) {
482
+ applyRuntimeCompanyConfigs();
483
+ }
484
+ }, [
485
+ applyRuntimeCompanyConfigs,
486
+ currentCompany?.id,
487
+ deviceConfigsLoaded,
488
+ isWebRuntimeDevice,
489
+ runtimeCompanyConfigs,
490
+ ]);
491
+
492
+ useFocusEffect(
493
+ useCallback(() => {
494
+ if (device?.configs) {
495
+ setCheckType(device?.configs['check-type'] || 'manual');
496
+ setCheckOrderType(
497
+ resolvePosCheckOrderType(device?.configs),
498
+ );
499
+ setCheckOrderManagementMode(
500
+ resolvePosCheckOrderManagementMode(device?.configs),
501
+ );
502
+ setProductInputType(device?.configs['product-input-type'] || 'manual');
503
+ setSelectionType(device?.configs['selection-type'] || 'single');
504
+ setShowSound(
505
+ device?.configs['sound'] === true ||
506
+ device?.configs['sound'] === '1'
507
+ );
508
+ setShowVibration(
509
+ device?.configs['vibration'] === true ||
510
+ device?.configs['vibration'] === '1'
511
+ );
512
+ setOrderVisibility(resolveDeviceOrderVisibility(device?.configs));
513
+ setAlertSoundEnabled(isTruthyValue(device?.configs[DEVICE_ALERT_SOUND_ENABLED_KEY]));
514
+ setAlertSoundUrl(String(device?.configs[DEVICE_ALERT_SOUND_URL_KEY] || ''));
515
+ setShowRuntimeDebugInfo(
516
+ isTruthyValue(device?.configs[DEVICE_RUNTIME_DEBUG_INFO_ENABLED_KEY]),
517
+ );
518
+ setSelectedMode(device?.configs['pos-type'] || 'full');
519
+ setPrintingMode(device?.configs['print-mode'] || 'order');
520
+ setSelectedGateway(device?.configs['pos-gateway'] || 'infinite-pay');
521
+ } else {
522
+ setCheckType('manual');
523
+ setCheckOrderType(POS_CHECK_ORDER_TYPE_NONE);
524
+ setCheckOrderManagementMode(POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE);
525
+ setProductInputType('manual');
526
+ setSelectionType('single');
527
+ setShowSound(false);
528
+ setShowVibration(false);
529
+ setOrderVisibility(DEVICE_ORDER_VISIBILITY_DEVICE);
530
+ setAlertSoundEnabled(false);
531
+ setAlertSoundUrl('');
532
+ setShowRuntimeDebugInfo(false);
533
+ setSelectedMode('full');
534
+ setPrintingMode('order');
535
+ setSelectedGateway('infinite-pay');
536
+ }
537
+ }, [device]),
538
+ );
539
+
540
+ useFocusEffect(
541
+ useCallback(() => {
542
+ if (!discovered && currentCompany?.id) {
543
+ const params = {
544
+ people: '/people/' + currentCompany.id,
545
+ };
546
+ configActions
547
+ .discoveryMainConfigs(params)
548
+ .then(() => {
549
+ setDiscovered(true);
550
+ })
551
+ .catch(() => {
552
+ setDiscovered(false);
553
+ });
554
+ }
555
+ }, [discovered, currentCompany?.id, configActions]),
556
+ );
557
+
558
+ const handleSelectionTypeChange = (value) => {
559
+ setSelectionType(value);
560
+ let lc = appendScreenMetrics({...(device?.configs || {})});
561
+ lc['selection-type'] = value;
562
+ lc['config-version'] = appVersion;
563
+ persistDeviceConfigs(lc)
564
+ .catch(err => {
565
+ console.error('addDeviceConfigs (selection-type) failed:', err);
566
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
567
+ });
568
+ };
569
+
570
+ const handleCheckTypeChange = (value) => {
571
+ setCheckType(value);
572
+ let lc = appendScreenMetrics({...(device?.configs || {})});
573
+ lc['check-type'] = value;
574
+ // ===== ALTERAÇÃO: ADICIONAR VERSÃO DO APP =====
575
+ lc['config-version'] = appVersion;
576
+ // ===== FIM DA ALTERAÇÃO =====
577
+ persistDeviceConfigs(lc)
578
+ .catch(err => {
579
+ console.error('addDeviceConfigs (check-type) failed:', err);
580
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
581
+ });
582
+ };
583
+
584
+ const handleCheckOrderTypeChange = value => {
585
+ setCheckOrderType(value);
586
+ let lc = appendScreenMetrics({...(device?.configs || {})});
587
+ lc[POS_CHECK_ORDER_TYPE_CONFIG_KEY] = value;
588
+ if (value === POS_CHECK_ORDER_TYPE_NONE) {
589
+ lc[POS_CHECK_ORDER_MANAGEMENT_MODE_CONFIG_KEY] =
590
+ POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE;
591
+ setCheckOrderManagementMode(POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE);
592
+ }
593
+ lc['config-version'] = appVersion;
594
+ persistDeviceConfigs(lc).catch(err => {
595
+ console.error('addDeviceConfigs (check-order-type) failed:', err);
596
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
597
+ });
598
+ };
599
+
600
+ const handleCheckOrderManagementModeChange = value => {
601
+ const nextValue =
602
+ value === POS_CHECK_ORDER_MANAGEMENT_MODE_EXISTING_ONLY
603
+ ? POS_CHECK_ORDER_MANAGEMENT_MODE_EXISTING_ONLY
604
+ : POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE;
605
+
606
+ setCheckOrderManagementMode(nextValue);
607
+ let lc = appendScreenMetrics({...(device?.configs || {})});
608
+ lc[POS_CHECK_ORDER_MANAGEMENT_MODE_CONFIG_KEY] = nextValue;
609
+ lc['config-version'] = appVersion;
610
+ persistDeviceConfigs(lc).catch(err => {
611
+ console.error(
612
+ 'addDeviceConfigs (check-order-management-mode) failed:',
613
+ err,
614
+ );
615
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
616
+ });
617
+ };
618
+
619
+ const handleProductInputTypeChange = (value) => {
620
+ setProductInputType(value);
621
+ let lc = appendScreenMetrics({...(device?.configs || {})});
622
+ lc['product-input-type'] = value;
623
+ // ===== ALTERAÇÃO: ADICIONAR VERSÃO DO APP =====
624
+ lc['config-version'] = appVersion;
625
+ // ===== FIM DA ALTERAÇÃO =====
626
+ persistDeviceConfigs(lc)
627
+ .catch(err => {
628
+ console.error('addDeviceConfigs (product-input-type) failed:', err);
629
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
630
+ });
631
+ };
632
+
633
+ const handleSoundChange = (value) => {
634
+ setShowSound(value);
635
+ localStorage.setItem('sound', String(value));
636
+ let lc = appendScreenMetrics({...(device?.configs || {})});
637
+ lc['sound'] = value ? '1' : '0';
638
+ // ===== ALTERAÇÃO: ADICIONAR VERSÃO DO APP =====
639
+ lc['config-version'] = appVersion;
640
+ // ===== FIM DA ALTERAÇÃO =====
641
+ persistDeviceConfigs(lc)
642
+ .catch(err => {
643
+ console.error('addDeviceConfigs (sound) failed:', err);
644
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
645
+ });
646
+ };
647
+
648
+ const handleVibrationChange = (value) => {
649
+ setShowVibration(value);
650
+ localStorage.setItem('vibration', String(value));
651
+ let lc = appendScreenMetrics({...(device?.configs || {})});
652
+ lc['vibration'] = value ? '1' : '0';
653
+ // ===== ALTERAÇÃO: ADICIONAR VERSÃO DO APP =====
654
+ lc['config-version'] = appVersion;
655
+ // ===== FIM DA ALTERAÇÃO =====
656
+ persistDeviceConfigs(lc)
657
+ .catch(err => {
658
+ console.error('addDeviceConfigs (vibration) failed:', err);
659
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
660
+ });
661
+ };
662
+
663
+ const handleOrderVisibilityChange = value => {
664
+ setOrderVisibility(value);
665
+ let lc = appendScreenMetrics({...(device?.configs || {})});
666
+ lc[DEVICE_ORDER_VISIBILITY_KEY] = value || DEVICE_ORDER_VISIBILITY_DEVICE;
667
+ lc['config-version'] = appVersion;
668
+ persistDeviceConfigs(lc)
669
+ .catch(err => {
670
+ console.error('addDeviceConfigs (order visibility) failed:', err);
671
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
672
+ });
673
+ };
674
+
675
+ const handleAlertSoundEnabledChange = value => {
676
+ setAlertSoundEnabled(value);
677
+ let lc = appendScreenMetrics({...(device?.configs || {})});
678
+ lc[DEVICE_ALERT_SOUND_ENABLED_KEY] = value ? '1' : '0';
679
+ lc[DEVICE_ALERT_SOUND_URL_KEY] = alertSoundUrl.trim();
680
+ lc['config-version'] = appVersion;
681
+ persistDeviceConfigs(lc)
682
+ .catch(err => {
683
+ console.error('addDeviceConfigs (notification sound enabled) failed:', err);
684
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
685
+ });
686
+ };
687
+
688
+ const handleAlertSoundUrlSubmit = () => {
689
+ let lc = appendScreenMetrics({...(device?.configs || {})});
690
+ lc[DEVICE_ALERT_SOUND_ENABLED_KEY] = alertSoundEnabled ? '1' : '0';
691
+ lc[DEVICE_ALERT_SOUND_URL_KEY] = alertSoundUrl.trim();
692
+ lc['config-version'] = appVersion;
693
+ persistDeviceConfigs(lc)
694
+ .catch(err => {
695
+ console.error('addDeviceConfigs (notification sound url) failed:', err);
696
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
697
+ });
698
+ };
699
+
700
+ const handleRuntimeDebugInfoChange = value => {
701
+ setShowRuntimeDebugInfo(value);
702
+ let lc = appendScreenMetrics({...(device?.configs || {})});
703
+ lc[DEVICE_RUNTIME_DEBUG_INFO_ENABLED_KEY] = value ? '1' : '0';
704
+ lc['config-version'] = appVersion;
705
+ persistDeviceConfigs(lc)
706
+ .catch(err => {
707
+ console.error('addDeviceConfigs (runtime debug info) failed:', err);
708
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
709
+ });
710
+ };
711
+
712
+ const handlePosTypeChange = (value) => {
713
+ setSelectedMode(value);
714
+ let lc = appendScreenMetrics({...(device?.configs || {})});
715
+ lc['pos-type'] = value;
716
+ lc['config-version'] = appVersion;
717
+ persistDeviceConfigs(lc)
718
+ .catch(err => {
719
+ console.error('addDeviceConfigs (pos-type) failed:', err);
720
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
721
+ });
722
+ };
723
+
724
+ const handlePrintModeChange = (value) => {
725
+ setPrintingMode(value);
726
+ let lc = appendScreenMetrics({...(device?.configs || {})});
727
+ lc['print-mode'] = value;
728
+ lc['config-version'] = appVersion;
729
+ persistDeviceConfigs(lc)
730
+ .catch(err => {
731
+ console.error('addDeviceConfigs (print-mode) failed:', err);
732
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
733
+ });
734
+ };
735
+
736
+ const handleGatewayChange = (value) => {
737
+ setSelectedGateway(value);
738
+ let lc = appendScreenMetrics({...(device?.configs || {})});
739
+ lc['pos-gateway'] = value;
740
+ lc['config-version'] = appVersion;
741
+ persistDeviceConfigs(lc)
742
+ .catch(err => {
743
+ console.error('addDeviceConfigs (pos-gateway) failed:', err);
744
+ Alert.alert('Erro ao gravar configurações', err.message || JSON.stringify(err));
745
+ });
746
+ };
747
+
748
+ const handleClearTranslate = () => {
749
+ Promise.resolve(global.t?.reload?.())
750
+ .then(() => {
751
+ global.refreshTranslationsUI?.();
752
+
753
+ if (Platform.OS === 'web' && typeof window !== 'undefined') {
754
+ window.location.reload();
755
+ return;
756
+ }
757
+
758
+ navigation.reset({
759
+ index: 0,
760
+ routes: [{name: 'SettingsPage'}],
761
+ });
762
+ })
763
+ .catch(() => {});
764
+ };
765
+
766
+ return (
767
+ <SafeAreaView style={styles.Settings.container}>
768
+ <StateStore store="device_config" />
769
+ <ScrollView contentContainerStyle={styles.Settings.scrollContent}>
770
+ <View style={styles.Settings.mainContainer}>
771
+ <View style={inlineStyle_565_16}>
772
+ <View style={styles.Settings.row}>
773
+ <Text style={styles.Settings.value}>{storagedDevice?.appName}:</Text>
774
+ <Text style={styles.Settings.value}>{appVersion}</Text>
775
+ </View>
776
+ <View style={styles.Settings.row}>
777
+ <Text style={styles.Settings.label}>Resolução do monitor: </Text>
778
+ <Text style={styles.Settings.value}>{screenMetrics?.deviceResolution || '-'}</Text>
779
+ </View>
780
+ <View style={styles.Settings.row}>
781
+ <Text style={styles.Settings.label}>Tamanho atual: </Text>
782
+ <Text style={styles.Settings.value}>{screenMetrics?.actualSize || '-'}</Text>
783
+ </View>
784
+ <View style={styles.Settings.row}>
785
+ <Text style={styles.Settings.label}>Tamanho da janela: </Text>
786
+ <Text style={styles.Settings.value}>{screenMetrics?.windosSize || '-'}</Text>
787
+ </View>
788
+ <View style={styles.Settings.row}>
789
+ <Text style={styles.Settings.label}>{global.t?.t("configs", "label", "machineId")}: </Text>
790
+ <Text style={styles.Settings.value}>{storagedDevice.id}</Text>
791
+ </View>
792
+ <View style={styles.Settings.row}>
793
+ <Text style={styles.Settings.label}>{global.t?.t("configs", "label", "manufacturer")}: </Text>
794
+ <Text style={styles.Settings.value}>
795
+ {storagedDevice?.manufacturer}
796
+ </Text>
797
+ </View>
798
+ <View style={styles.Settings.row}>
799
+ <Text style={styles.Settings.label}>{global.t?.t("configs", "label", "systemVersion")}: </Text>
800
+ <Text style={styles.Settings.value}>
801
+ {storagedDevice?.systemName}
802
+ {Platform.OS !== 'web' &&
803
+ storagedDevice?.systemVersion &&
804
+ String(storagedDevice.systemVersion).toLowerCase() !== 'unknown' &&
805
+ String(storagedDevice.systemVersion).toLowerCase() !== 'unknow'
806
+ ? `, ${storagedDevice.systemVersion}`
807
+ : ''}
808
+ </Text>
809
+ </View>
810
+
811
+ <View style={styles.Settings.walletRow}>
812
+ <Text style={styles.Settings.label}>{global.t?.t("configs", "label", "cashWallet")}: </Text>
813
+ <View style={styles.Settings.walletValueContainer}>
814
+ <Text style={styles.Settings.walletValue}>
815
+ {companyConfigs?.['pos-cash-wallet']}
816
+ </Text>
817
+ {walletLoading || isDeviceConfigSaving ? (
818
+ <ActivityIndicator size={22} color={styles.Settings.label} />
819
+ ) : companyConfigs?.['pos-cash-wallet'] ? (
820
+ <Icon name={'check'} size={22} color="green" />
821
+ ) : (
822
+ <Icon name={'close'} size={22} color="red" />
823
+ )}
824
+ </View>
825
+ </View>
826
+ <View style={styles.Settings.walletRow}>
827
+ <Text style={styles.Settings.label}>{global.t?.t("configs", "label", "withdrawlWallet")}: </Text>
828
+ <View style={styles.Settings.walletValueContainer}>
829
+ <Text style={styles.Settings.walletValue}>
830
+ {withdrawWallet}
831
+ </Text>
832
+ {walletLoading || isDeviceConfigSaving ? (
833
+ <ActivityIndicator size={22} color={styles.Settings.label} />
834
+ ) : withdrawWallet ? (
835
+ <Icon name={'check'} size={22} color="green" />
836
+ ) : (
837
+ <Icon name={'close'} size={22} color="red" />
838
+ )}
839
+ </View>
840
+ </View>
841
+ </View>
842
+
843
+ <View style={settingsTabStyles.tabRow}>
844
+ <TouchableOpacity
845
+ activeOpacity={0.85}
846
+ onPress={() => setActiveSettingsTab('general')}
847
+ style={[
848
+ settingsTabStyles.tabButton,
849
+ activeSettingsTab === 'general' && settingsTabStyles.tabButtonActive,
850
+ ]}>
851
+ <Text
852
+ style={[
853
+ settingsTabStyles.tabLabel,
854
+ activeSettingsTab === 'general' && settingsTabStyles.tabLabelActive,
855
+ ]}>
856
+ Geral
857
+ </Text>
858
+ </TouchableOpacity>
859
+ <TouchableOpacity
860
+ activeOpacity={0.85}
861
+ onPress={() => setActiveSettingsTab('payments')}
862
+ style={[
863
+ settingsTabStyles.tabButton,
864
+ activeSettingsTab === 'payments' && settingsTabStyles.tabButtonActive,
865
+ ]}>
866
+ <Text
867
+ style={[
868
+ settingsTabStyles.tabLabel,
869
+ activeSettingsTab === 'payments' && settingsTabStyles.tabLabelActive,
870
+ ]}>
871
+ Pagamentos
872
+ </Text>
873
+ </TouchableOpacity>
874
+ </View>
875
+
876
+ {activeSettingsTab === 'payments' ? (
877
+ <PaymentTypesByWalletTab
878
+ currentCompanyId={currentCompany?.id}
879
+ configs={runtimeCompanyConfigs}
880
+ isSaving={isSaving}
881
+ onPersistSelectedPaymentTypeIds={persistCompanyPaymentConfigs}
882
+ title="Pagamentos da empresa"
883
+ introText="Os wallets só organizam a lista. O que vale para o shop é a allowlist de meios de pagamento salva nas configurações da empresa."
884
+ helpText="Esse padrão é usado pelo shop e pelos fluxos que consomem as configs da empresa."
885
+ emptyTitle="Nenhuma wallet da empresa encontrada"
886
+ emptyText="Não há wallets cadastradas para a empresa ativa."
887
+ emptyPaymentsText="Nenhum meio de pagamento vinculado a esta wallet."
888
+ unassignedText="Meios de pagamento sem wallet vinculada."
889
+ />
890
+ ) : (
891
+ <>
892
+ {isWebRuntimeDevice && (
893
+ <View style={inlineStyle_639_14}>
894
+ <Text style={[styles.Settings.label, {marginBottom: 4}]}>
895
+ Configuração sincronizada
896
+ </Text>
897
+ <Text style={inlineStyle_650_20}>
898
+ No navegador este runtime usa configurações sincronizadas do
899
+ device web e da empresa. Ajustes compatíveis ficam refletidos no
900
+ rodapé e nos recursos em tempo real.
901
+ </Text>
902
+ </View>
903
+ )}
904
+
905
+ {/* // // // // // LINKED ORDER INPUT */}
906
+ <View style={inlineStyle_659_16}>
907
+ <Text style={styles.Settings.label}>{global.t?.t("configs", "label", "tab type")}</Text>
908
+ <Picker
909
+ selectedValue={checkType}
910
+ onValueChange={handleCheckTypeChange}
911
+ enabled={!isWebRuntimeDevice}
912
+ mode={pickerMode}
913
+ style={styles.Settings.picker}>
914
+
915
+ <Picker.Item label={global.t?.t("configs", "option", "manual")} value="manual" />
916
+ <Picker.Item label={global.t?.t("configs", "option", "barcode")} value="barcode" />
917
+ <Picker.Item label={global.t?.t("configs", "option", "rfid")} value="rfid" />
918
+
919
+ </Picker>
920
+ </View>
921
+
922
+ <View style={inlineStyle_676_16}>
923
+ <Text style={styles.Settings.label}>
924
+ {global.t?.t('configs', 'label', 'linkedOrderType') ||
925
+ 'Service base order'}
926
+ </Text>
927
+ <Picker
928
+ selectedValue={checkOrderType}
929
+ onValueChange={handleCheckOrderTypeChange}
930
+ enabled={!isWebRuntimeDevice}
931
+ mode={pickerMode}
932
+ style={styles.Settings.picker}>
933
+ <Picker.Item
934
+ label={global.t?.t('configs', 'option', 'none') || 'None'}
935
+ value={POS_CHECK_ORDER_TYPE_NONE}
936
+ />
937
+ <Picker.Item
938
+ label={global.t?.t('orders', 'title', 'tab') || 'Tab'}
939
+ value={POS_CHECK_ORDER_TYPE_TAB}
940
+ />
941
+ <Picker.Item
942
+ label={global.t?.t('orders', 'title', 'table') || 'Table'}
943
+ value={POS_CHECK_ORDER_TYPE_TABLE}
944
+ />
945
+ </Picker>
946
+ </View>
947
+
948
+ {checkOrderType !== POS_CHECK_ORDER_TYPE_NONE && (
949
+ <View style={inlineStyle_676_16}>
950
+ <Text style={styles.Settings.label}>
951
+ {global.t?.t('configs', 'label', 'linkedOrderManagementMode') ||
952
+ 'Tab and table access'}
953
+ </Text>
954
+ <Picker
955
+ selectedValue={checkOrderManagementMode}
956
+ onValueChange={handleCheckOrderManagementModeChange}
957
+ enabled={!isWebRuntimeDevice}
958
+ mode={pickerMode}
959
+ style={styles.Settings.picker}>
960
+ <Picker.Item
961
+ label={
962
+ global.t?.t('configs', 'option', 'manageLinkedOrders') ||
963
+ 'Open and close tabs/tables'
964
+ }
965
+ value={POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE}
966
+ />
967
+ <Picker.Item
968
+ label={
969
+ global.t?.t(
970
+ 'configs',
971
+ 'option',
972
+ 'existingLinkedOrdersOnly',
973
+ ) || 'Use open tabs/tables only'
974
+ }
975
+ value={POS_CHECK_ORDER_MANAGEMENT_MODE_EXISTING_ONLY}
976
+ />
977
+ </Picker>
978
+ </View>
979
+ )}
980
+
981
+ {/* // // // // // TIPO DE LEITURA DE PRODUTO */}
982
+ <View style={inlineStyle_676_16}>
983
+ <Text style={styles.Settings.label}>{global.t?.t("configs", "label", "product read method")}</Text>
984
+ <Picker
985
+ selectedValue={productInputType}
986
+ onValueChange={handleProductInputTypeChange}
987
+ enabled={!isWebRuntimeDevice}
988
+ mode={pickerMode}
989
+ style={styles.Settings.picker}>
990
+ <Picker.Item label={global.t?.t("configs", "option", "manual")} value="manual" />
991
+ <Picker.Item label={global.t?.t("configs", "option", "barcode")} value="barcode" />
992
+ <Picker.Item label={global.t?.t("configs", "option", "rfid")} value="rfid" />
993
+ </Picker>
994
+ </View>
995
+
996
+ <View style={inlineStyle_690_16}>
997
+ <Text style={styles.Settings.label}>{global.t?.t("configs", "label", "selection type")}</Text>
998
+ <Picker
999
+ selectedValue={selectionType}
1000
+ onValueChange={handleSelectionTypeChange}
1001
+ enabled={!isWebRuntimeDevice}
1002
+ mode={pickerMode}
1003
+ style={styles.Settings.picker}>
1004
+ <Picker.Item label={global.t?.t("configs", "option", "single")} value="single" />
1005
+ <Picker.Item label={global.t?.t("configs", "option", "multiple")} value="multiple" />
1006
+ </Picker>
1007
+ </View>
1008
+
1009
+ <View
1010
+ style={inlineStyle_704_12}>
1011
+ <Text style={styles.Settings.label}>{global.t?.t("configs", "label", "sound")}</Text>
1012
+
1013
+ <Switch
1014
+ value={showSound}
1015
+ disabled={isWebRuntimeDevice}
1016
+ onValueChange={handleSoundChange}
1017
+ />
1018
+ </View>
1019
+
1020
+ <View
1021
+ style={inlineStyle_720_12}>
1022
+ <Text style={styles.Settings.label}>{global.t?.t("configs", "label", "vibration")}</Text>
1023
+
1024
+ <Switch
1025
+ value={showVibration}
1026
+ disabled={isWebRuntimeDevice}
1027
+ onValueChange={handleVibrationChange}
1028
+ />
1029
+ </View>
1030
+
1031
+ <View
1032
+ style={inlineStyle_736_12}>
1033
+ <View
1034
+ style={inlineStyle_743_14}>
1035
+ <Text style={styles.Settings.label}>Aviso sonoro via websocket</Text>
1036
+ <Switch
1037
+ value={alertSoundEnabled}
1038
+ disabled={isWebRuntimeDevice}
1039
+ onValueChange={handleAlertSoundEnabledChange}
1040
+ />
1041
+ </View>
1042
+
1043
+ <Text
1044
+ style={inlineStyle_757_14}>
1045
+ Toca quando este device recebe o evento
1046
+ order.created de um novo pedido em preparo via
1047
+ websocket.
1048
+ </Text>
1049
+
1050
+ <Text style={[styles.Settings.label, {marginTop: 12, marginBottom: 6, flex: 0}]}>
1051
+ URL do aviso sonoro
1052
+ </Text>
1053
+ <TextInput
1054
+ value={alertSoundUrl}
1055
+ onChangeText={setAlertSoundUrl}
1056
+ onBlur={handleAlertSoundUrlSubmit}
1057
+ onSubmitEditing={handleAlertSoundUrlSubmit}
1058
+ editable={!isWebRuntimeDevice}
1059
+ placeholder="https://exemplo.com/alerta.mp3"
1060
+ placeholderTextColor="#94A3B8"
1061
+ autoCapitalize="none"
1062
+ autoCorrect={false}
1063
+ keyboardType="url"
1064
+ returnKeyType="done"
1065
+ style={inlineStyle_783_14}
1066
+ />
1067
+ <Text style={inlineStyle_818_14}>
1068
+ Se a URL ficar vazia, o runtime usa o som padrão do app
1069
+ ({DEFAULT_NOTIFICATION_SOUND_FILE}).
1070
+ </Text>
1071
+ </View>
1072
+
1073
+ <View
1074
+ style={inlineStyle_798_12}>
1075
+ <View
1076
+ style={inlineStyle_805_14}>
1077
+ <Text style={styles.Settings.label}>Debug do socket no rodapé</Text>
1078
+ <Switch
1079
+ value={showRuntimeDebugInfo}
1080
+ onValueChange={handleRuntimeDebugInfoChange}
1081
+ />
1082
+ </View>
1083
+
1084
+ <Text
1085
+ style={inlineStyle_818_14}>
1086
+ Quando desligado, o rodapé mostra só um indicador discreto do
1087
+ socket. Quando ligado, exibe os detalhes completos de realtime e
1088
+ refresh em qualquer tela do sistema.
1089
+ </Text>
1090
+ </View>
1091
+
1092
+ <View style={inlineStyle_830_16}>
1093
+ <Picker
1094
+ selectedValue={selectedMode}
1095
+ onValueChange={handlePosTypeChange}
1096
+ enabled={!isWebRuntimeDevice}
1097
+ mode={pickerMode}
1098
+ style={styles.Settings.picker}>
1099
+ <Picker.Item label={global.t?.t("configs", "option", "simple order")} value="simple" />
1100
+ <Picker.Item label={global.t?.t("configs", "option", "full order")} value="full" />
1101
+ </Picker>
1102
+ </View>
1103
+ <View style={inlineStyle_841_16}>
1104
+ <Picker
1105
+ selectedValue={printingMode}
1106
+ onValueChange={handlePrintModeChange}
1107
+ enabled={!isWebRuntimeDevice}
1108
+ mode={pickerMode}
1109
+ style={styles.Settings.picker}>
1110
+ <Picker.Item label={global.t?.t("configs", "option", "printSingleOrder")} value="order" />
1111
+ <Picker.Item label={global.t?.t("configs", "option", "printFullOrder")} value="form" />
1112
+ </Picker>
1113
+ </View>
1114
+ <View style={inlineStyle_852_16}>
1115
+ <Text style={styles.Settings.label}>Pedidos visíveis no PDV</Text>
1116
+ <Picker
1117
+ selectedValue={orderVisibility}
1118
+ onValueChange={handleOrderVisibilityChange}
1119
+ enabled={!isWebRuntimeDevice}
1120
+ mode={pickerMode}
1121
+ style={styles.Settings.picker}>
1122
+ <Picker.Item
1123
+ label="Somente pedidos deste device"
1124
+ value={DEVICE_ORDER_VISIBILITY_DEVICE}
1125
+ />
1126
+ <Picker.Item
1127
+ label="Todos os pedidos da empresa"
1128
+ value={DEVICE_ORDER_VISIBILITY_COMPANY}
1129
+ />
1130
+ </Picker>
1131
+ </View>
1132
+ {showGatewayPicker && (
1133
+ <View style={inlineStyle_871_18}>
1134
+ <Picker
1135
+ selectedValue={selectedGateway}
1136
+ onValueChange={handleGatewayChange}
1137
+ enabled={!isWebRuntimeDevice}
1138
+ mode={pickerMode}
1139
+ style={styles.Settings.picker}>
1140
+ <Picker.Item label="Infinite Pay" value="infinite-pay" />
1141
+ {allowCieloOption && (
1142
+ <Picker.Item label="Cielo" value="cielo" />
1143
+ )}
1144
+ </Picker>
1145
+ </View>
1146
+ )}
1147
+
1148
+ <TouchableOpacity
1149
+ onPress={handleClearTranslate}
1150
+ style={[
1151
+ globalStyles.button,
1152
+ {
1153
+ flexDirection: 'row',
1154
+ justifyContent: 'center',
1155
+ alignItems: 'center',
1156
+ height: 50,
1157
+ marginTop: 6,
1158
+ },
1159
+ ]}>
1160
+ <Icon name="add-circle" size={24} color="#fff" />
1161
+ <Text style={inlineStyle_899_18}>{global.t?.t("configs", "label", "resync translations")}</Text>
1162
+ </TouchableOpacity>
1163
+ </>
1164
+ )}
1165
+ </View>
1166
+ </ScrollView>
1167
+ </SafeAreaView>
1168
+ );
1169
+ };
1170
+
1171
+ export default Settings;