@controleonline/ui-common 1.2.80 → 1.2.81

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 (90) hide show
  1. package/AGENTS.md +8 -0
  2. package/package.json +9 -3
  3. package/src/api/fetch.js +7 -7
  4. package/src/react/components/AddImportModal.js +6 -2
  5. package/src/react/components/BackgroundRuntimeBridge.js +201 -215
  6. package/src/react/components/ConfirmModal.js +44 -9
  7. package/src/react/components/ConfirmModal.styles.js +55 -41
  8. package/src/react/components/CopyDeviceConfigModal.js +193 -0
  9. package/src/react/components/CopyDeviceConfigModal.styles.js +123 -0
  10. package/src/react/components/DefaultProvider.native.js +22 -0
  11. package/src/react/components/DefaultProvider.web.js +25 -1
  12. package/src/react/components/EntityLogBranch.js +482 -0
  13. package/src/react/components/EntityLogCards.js +369 -0
  14. package/src/react/components/EntityLogContent.js +113 -794
  15. package/src/react/components/LinkedOrderProductsTab.js +3 -3
  16. package/src/react/components/PrintService.js +130 -409
  17. package/src/react/components/RemoteCheckoutService.js +2 -2
  18. package/src/react/components/RuntimeBottomNavigationBar.js +141 -146
  19. package/src/react/components/RuntimeFooterMarqueeText.js +137 -0
  20. package/src/react/components/RuntimeInfoFooter.js +12 -15
  21. package/src/react/components/SystemErrorToast.js +49 -29
  22. package/src/react/components/SystemErrorToast.styles.js +43 -38
  23. package/src/react/components/UserAvatar.js +120 -28
  24. package/src/react/config/deviceConfigBootstrap.js +330 -304
  25. package/src/react/hooks/usePrintSpoolEffects.js +161 -0
  26. package/src/react/pages/DeviceDetailPage.js +6 -2882
  27. package/src/react/pages/Devices/detail/DeviceDetailAlertsCommandsSection.js +233 -0
  28. package/src/react/pages/Devices/detail/DeviceDetailHeader.js +138 -0
  29. package/src/react/pages/Devices/detail/DeviceDetailMovementSections.js +167 -0
  30. package/src/react/pages/Devices/detail/DeviceDetailOrdersPrintSection.js +216 -0
  31. package/src/react/pages/Devices/detail/DeviceDetailPaymentSection.js +40 -0
  32. package/src/react/pages/Devices/detail/DeviceDetailPdvConfigSection.js +390 -0
  33. package/src/react/pages/Devices/detail/DeviceDetailRenderers.js +135 -0
  34. package/src/react/pages/Devices/detail/DeviceDetailScreen.js +493 -0
  35. package/src/react/pages/Devices/detail/OptionButtonChip.js +65 -0
  36. package/src/react/pages/Devices/detail/deviceDetailConstants.js +36 -0
  37. package/src/react/pages/Devices/detail/deviceDetailHelpers.js +70 -0
  38. package/src/react/pages/Devices/detail/useDeviceDetailActions.js +483 -0
  39. package/src/react/pages/Devices/detail/useDeviceDetailCopyConfig.js +87 -0
  40. package/src/react/pages/Devices/detail/useDeviceDetailLoaders.js +327 -0
  41. package/src/react/pages/Devices/detail/useDeviceDetailSaves.js +429 -0
  42. package/src/react/pages/Devices/detail/useDeviceDetailStateA.js +484 -0
  43. package/src/react/pages/Devices/detail/useDeviceDetailStateB.js +71 -0
  44. package/src/react/pages/Devices/deviceTypes/index.js +2 -0
  45. package/src/react/pages/Devices/deviceTypes/pdv.js +11 -0
  46. package/src/react/pages/Imports.js +3 -2
  47. package/src/react/pages/IntegrationConfigFields.js +126 -0
  48. package/src/react/pages/IntegrationConfigPage.js +122 -591
  49. package/src/react/pages/IntegrationConfigPage.utils.js +95 -0
  50. package/src/react/print/usePrintButtonController.js +9 -1
  51. package/src/react/services/Getnet/Checkout.js +81 -0
  52. package/src/react/services/Getnet/Getnet.js +77 -0
  53. package/src/react/services/paymentGatewayExecution.js +20 -0
  54. package/src/react/utils/bottomNavigationToolbar.js +161 -0
  55. package/src/react/utils/commercialDocumentOrders.js +18 -6
  56. package/src/react/utils/copyDeviceConfigs.js +198 -0
  57. package/src/react/utils/fileUrl.js +43 -0
  58. package/src/react/utils/importStatus.js +19 -10
  59. package/src/react/utils/logSettings.js +7 -7
  60. package/src/react/utils/maintenanceSettings.js +16 -0
  61. package/src/react/utils/paymentDevices.js +6 -0
  62. package/src/react/utils/printManagedPrinter.js +45 -0
  63. package/src/react/utils/printRouting.js +63 -0
  64. package/src/react/utils/printSpoolUtils.js +119 -0
  65. package/src/react/utils/printerDevices.js +368 -368
  66. package/src/react/utils/runtimeFooter.js +10 -19
  67. package/src/react/utils/shopFranchises.js +186 -65
  68. package/src/react/utils/systemErrorMessage.js +71 -67
  69. package/src/store/device_config/customActions.js +119 -30
  70. package/src/tests/browser/manager/bottom-navigation-role-toolbar.spec.js +353 -0
  71. package/src/tests/browser/manager/device-detail-alias-save.spec.js +345 -0
  72. package/src/tests/browser/manager/device-detail-copy-config.spec.js +365 -0
  73. package/src/tests/browser/manager/runtime-footer-marquee.spec.js +262 -0
  74. package/src/tests/browser/manager/theme-confirm-toast.spec.js +236 -0
  75. package/src/tests/react/components/RuntimeFooterMarqueeText.test.js +171 -0
  76. package/src/tests/react/components/UserAvatar.test.js +6 -2
  77. package/src/tests/react/components/themeAwareModals.test.mjs +60 -0
  78. package/src/tests/react/config/deviceConfigBootstrap.test.js +104 -66
  79. package/src/tests/react/pages/deviceDetailDelete.test.js +78 -0
  80. package/src/tests/react/pages/receitaFederalIntegrationContract.test.mjs +48 -0
  81. package/src/tests/react/utils/bottomNavigationToolbar.test.js +222 -0
  82. package/src/tests/react/utils/copyDeviceConfigs.test.mjs +127 -0
  83. package/src/tests/react/utils/fileUrl.test.js +38 -0
  84. package/src/tests/react/utils/importStatus.test.js +26 -9
  85. package/src/tests/react/utils/passwordPolicy.test.js +27 -0
  86. package/src/tests/react/utils/printRouting.test.mjs +86 -0
  87. package/src/tests/react/utils/printSpoolUtils.test.mjs +52 -0
  88. package/src/tests/react/utils/runtimeFooter.test.js +7 -0
  89. package/src/tests/react/utils/shopFranchises.test.js +121 -115
  90. package/src/tests/react/utils/systemErrorMessage.test.js +32 -59
@@ -0,0 +1,36 @@
1
+ export const hex = {
2
+ success: '#10b981',
3
+ danger: '#c10015',
4
+ warning: '#e67e22',
5
+ info: '#0EA5E9',
6
+ purple: '#8B5CF6',
7
+ };
8
+
9
+ export const PAYMENT_ICONS = {
10
+ dinheiro: 'dollar-sign',
11
+ pix: 'zap',
12
+ debito: 'credit-card',
13
+ credito: 'credit-card',
14
+ default: 'hash',
15
+ };
16
+
17
+ export const DISPLAY_DEVICE_TYPE = 'DISPLAY';
18
+ export const PDV_DEVICE_TYPE = 'PDV';
19
+ export const DISPLAY_DEVICE_LINK_CONFIG_KEY = 'display-id';
20
+ export const DISPLAY_DEVICE_PRINTER_CONFIG_KEY = 'printer';
21
+
22
+ export const PDV_TAB_OPERATION = 'operation';
23
+ export const PDV_TAB_ORDERS = 'orders';
24
+ export const PDV_TAB_DEVICE = 'device';
25
+ export const PDV_TAB_PAYMENT_TYPES = 'payment-types';
26
+ export const PDV_TAB_MOVEMENT = 'movement';
27
+
28
+ export const PDV_DETAIL_TABS = [
29
+ {key: PDV_TAB_OPERATION, icon: 'sliders', labelKey: 'pdvOperation'},
30
+ {key: PDV_TAB_ORDERS, icon: 'list', labelKey: 'pdvOrders'},
31
+ {key: PDV_TAB_DEVICE, icon: 'cpu', labelKey: 'pdvDevice'},
32
+ {key: PDV_TAB_PAYMENT_TYPES, icon: 'credit-card', labelKey: 'pdvPayments'},
33
+ {key: PDV_TAB_MOVEMENT, icon: 'bar-chart-2', labelKey: 'pdvMovement'},
34
+ ];
35
+
36
+ export const tt = (type, key) => global.t?.t('configs', type, key);
@@ -0,0 +1,70 @@
1
+ import {normalizeEntityId} from '@controleonline/ui-common/src/react/utils/paymentDevices';
2
+ import {isPosCashRegisterOpen} from '@controleonline/ui-common/src/react/config/deviceConfigBootstrap';
3
+ import {Alert, Platform} from 'react-native';
4
+ import {PAYMENT_ICONS} from './deviceDetailConstants';
5
+
6
+ export const paymentIcon = label => {
7
+ const l = String(label || '').toLowerCase();
8
+ if (l.includes('pix')) return PAYMENT_ICONS.pix;
9
+ if (l.includes('debit')) return PAYMENT_ICONS.debito;
10
+ if (l.includes('crédit') || l.includes('credit')) return PAYMENT_ICONS.credito;
11
+ if (l.includes('dinh')) return PAYMENT_ICONS.dinheiro;
12
+ return PAYMENT_ICONS.default;
13
+ };
14
+
15
+ export const formatApiError = (error, fallback) => {
16
+ if (typeof error === 'string') {
17
+ return error.trim() || fallback;
18
+ }
19
+ if (Array.isArray(error?.message)) {
20
+ return (
21
+ error.message
22
+ .map(item => item?.message || item?.title || String(item || '').trim())
23
+ .filter(Boolean)
24
+ .join('\n') || fallback
25
+ );
26
+ }
27
+ return error?.message || error?.description || fallback;
28
+ };
29
+
30
+ export const getDisplayLabel = display => {
31
+ const name = String(display?.display || '').trim();
32
+ const type = String(display?.displayType || '').trim().toUpperCase();
33
+ if (name && type) return `${name} (${type})`;
34
+ if (name) return name;
35
+ return `Display #${normalizeEntityId(display) || '--'}`;
36
+ };
37
+
38
+ export const getProductShowcaseLabel = showcase => {
39
+ const name = String(showcase?.name || '').trim();
40
+ const externalCode = String(showcase?.externalStoreCode || '').trim();
41
+ if (name && externalCode) return `${name} (${externalCode})`;
42
+ return name || externalCode || `Showcase #${normalizeEntityId(showcase) || '--'}`;
43
+ };
44
+
45
+ export const getIsOpen = configs => isPosCashRegisterOpen(configs);
46
+
47
+ export const confirm = (msg, cb) => {
48
+ if (Platform.OS === 'web' && typeof window !== 'undefined' && window.confirm) {
49
+ if (window.confirm(msg)) cb();
50
+ } else {
51
+ Alert.alert('Confirmação', msg, [
52
+ {text: 'Cancelar', style: 'cancel'},
53
+ {text: 'Confirmar', onPress: cb},
54
+ ]);
55
+ }
56
+ };
57
+
58
+ export const getDeviceSwitchProps = ({disabled = false, palette, value = false}) => {
59
+ const offTrackColor = disabled ? palette.switchDisabledTrack : palette.switchOffTrack;
60
+ const onTrackColor = disabled ? palette.switchDisabledTrack : palette.switchOnTrack;
61
+ return {
62
+ ios_backgroundColor: offTrackColor,
63
+ thumbColor: disabled
64
+ ? palette.switchDisabledThumb
65
+ : value
66
+ ? palette.switchOnThumb
67
+ : palette.switchOffThumb,
68
+ trackColor: {false: offTrackColor, true: onTrackColor},
69
+ };
70
+ };
@@ -0,0 +1,483 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useNavigation, useRoute } from '@react-navigation/native';
3
+ import { useStore } from '@store';
4
+ import { useMessage } from '@controleonline/ui-common/src/react/components/MessageService';
5
+ import { resolveThemePalette } from '@controleonline/../../src/styles/branding';
6
+ import { colors } from '@controleonline/../../src/styles/colors';
7
+ import packageJson from '@package';
8
+ import {
9
+ canDisplayChangePrinter, DEVICE_ANDROID_KIOSK_ENABLED_CONFIG_KEY, DEVICE_ANDROID_LAUNCHER_ENABLED_CONFIG_KEY,
10
+ DISPLAY_AUTO_PRINT_PRODUCT_CONFIG_KEY, DISPLAY_ALLOW_PRINTER_CHANGE_CONFIG_KEY, DEVICE_ALERT_SOUND_ENABLED_KEY,
11
+ DEVICE_ALERT_SOUND_URL_KEY, DEVICE_ORDER_VISIBILITY_COMPANY, DEVICE_ORDER_VISIBILITY_DEVICE, DEVICE_ORDER_VISIBILITY_KEY,
12
+ DEVICE_RUNTIME_DEBUG_INFO_ENABLED_KEY, isPosAutoPrintEnabled, isPosCashRegisterOpen, isTruthyValue, parseConfigsObject,
13
+ POS_AUTO_PRINT_ENABLED_CONFIG_KEY, POS_CASH_MANAGEMENT_MODE_CASH_REGISTER, POS_CASH_MANAGEMENT_MODE_CONFIG_KEY,
14
+ POS_CASH_MANAGEMENT_MODE_DAILY, POS_CHECK_ORDER_MANAGEMENT_MODE_CONFIG_KEY, POS_CHECK_ORDER_MANAGEMENT_MODE_EXISTING_ONLY,
15
+ POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE, POS_CHECK_ORDER_TYPE_CONFIG_KEY, POS_CHECK_ORDER_TYPE_NONE, POS_CHECK_ORDER_TYPE_TAB,
16
+ POS_CHECK_ORDER_TYPE_TABLE, POS_CHECK_ORDER_TYPE_STAMP, POS_DELIVERY_ENABLED_CONFIG_KEY, POS_OPERATION_MODE_COUNTER,
17
+ POS_OPERATION_MODE_CONFIG_KEY, POS_OPERATION_MODE_OPTIONS, POS_PRODUCT_SHOWCASE_CONFIG_KEY, POS_PRINT_MODE_FORM,
18
+ POS_PRINT_MODE_ORDER, getPosOperationModeOption, isAndroidKioskEnabled, isAndroidLauncherEnabled, isPosDeliveryEnabled,
19
+ resolvePosCheckOrderManagementMode, resolvePosCheckOrderType, resolvePosCheckOrderTypeForShop, resolveDeviceOrderVisibility,
20
+ resolvePosCashManagementMode, resolvePosOperationMode, resolvePosPrintMode,
21
+ } from '@controleonline/ui-common/src/react/config/deviceConfigBootstrap';
22
+ import {
23
+ filterDeviceConfigsByCompany, getCompanyPaymentDeviceOptions, getPaymentGatewayFromConfigs, getPaymentGatewayLabel,
24
+ isPaymentCapableDeviceConfig, isPdvPrinterEnabled, normalizeDeviceId, normalizeEntityId, PAYMENT_TYPE_IDS_CONFIG_KEY,
25
+ PAYMENT_GATEWAY_CIELO, PDV_PRINTER_ENABLED_CONFIG_KEY, ORDER_PAYMENT_DEVICE_CONFIG_KEY, POS_GATEWAY_CONFIG_KEY,
26
+ } from '@controleonline/ui-common/src/react/utils/paymentDevices';
27
+ import { normalizeBooleanConfig, SHOP_LOYALTY_COUPONS_ENABLED_CONFIG_KEY } from '@controleonline/ui-common/src/react/utils/shopConfig';
28
+ import { getPrinterOptionValue, getDeviceTypeLabel, getPrinterLabel, getPrinterOptions } from '@controleonline/ui-common/src/react/utils/printerDevices';
29
+ import { hex, DISPLAY_DEVICE_TYPE, PDV_DEVICE_TYPE, DISPLAY_DEVICE_LINK_CONFIG_KEY, DISPLAY_DEVICE_PRINTER_CONFIG_KEY,
30
+ PDV_TAB_OPERATION, PDV_TAB_ORDERS, PDV_TAB_DEVICE, PDV_TAB_PAYMENT_TYPES, PDV_TAB_MOVEMENT, PDV_DETAIL_TABS, tt,
31
+ } from './deviceDetailConstants';
32
+ import { paymentIcon, formatApiError, getDisplayLabel, getProductShowcaseLabel, getIsOpen, confirm } from './deviceDetailHelpers';
33
+ import { api } from '@controleonline/ui-common/src/api';
34
+ import { buildDeviceAliasStoreUpdates } from '@controleonline/ui-common/src/react/utils/deviceAliasSync';
35
+ import { createDeviceDetailRenderers } from './DeviceDetailRenderers';
36
+
37
+
38
+ export default function useDeviceDetailActions(deps) {
39
+ const {
40
+ navigation, deviceId, deviceConfigStore, messageApi, websocketActions, runtimeCompanyConfigs, showSystemError, loyaltyCouponsEnabled, currentDevice, currentDeviceConfig, deviceString, deviceType,
41
+ normalizedInitialConfigs, initialAlias, isDisplayDevice, isPdvDevice, actionsRef, stampAutoDisableSignatureRef, brandColors, products, setProducts, productShowcases, setProductShowcases, companyDeviceConfigs,
42
+ setCompanyDeviceConfigs, inflowData, setInflowData, configs, setConfigs, loadingConfigData, setLoadingConfigData, loadingCompanyDeviceConfigs, setLoadingCompanyDeviceConfigs, loadingMovementData, setLoadingMovementData, actionLoading,
43
+ setActionLoading, activePdvTab, setActivePdvTab, savingPaymentTarget, setSavingPaymentTarget, savingPdvSettings, setSavingPdvSettings, savingPaymentTypes, setSavingPaymentTypes, savingPosOperationMode, setSavingPosOperationMode, savingProductShowcase,
44
+ setSavingProductShowcase, savingLauncherMode, setSavingLauncherMode, savingAlertSound, setSavingAlertSound, savingOrderVisibility, setSavingOrderVisibility, savingDeviceDeliverySettings, setSavingDeviceDeliverySettings, savingRuntimeDebugInfo, setSavingRuntimeDebugInfo, sendingCatalogRefresh,
45
+ setSendingCatalogRefresh, loadingProductShowcases, setLoadingProductShowcases, search, setSearch, devicePaymentTarget, setDevicePaymentTarget, pdvGateway, setPdvGateway, pdvPrinterEnabled, setPdvPrinterEnabled, posOperationMode,
46
+ setPosOperationMode, productShowcaseId, setProductShowcaseId, androidKioskEnabled, setAndroidKioskEnabled, androidLauncherEnabled, setAndroidLauncherEnabled, counterAutoPrintEnabled, setCounterAutoPrintEnabled, counterPrintMode, setCounterPrintMode, counterCashManagementMode,
47
+ setCounterCashManagementMode, checkOrderType, setCheckOrderType, checkOrderManagementMode, setCheckOrderManagementMode, deviceOrderVisibility, setDeviceOrderVisibility, deviceDeliveryEnabled, setDeviceDeliveryEnabled, deviceAlertSoundEnabled, setDeviceAlertSoundEnabled, deviceAlertSoundUrl,
48
+ setDeviceAlertSoundUrl, deviceRuntimeDebugInfoEnabled, setDeviceRuntimeDebugInfoEnabled, linkedDisplayId, setLinkedDisplayId, displayPrinterId, setDisplayPrinterId, displayAllowPrinterChange, setDisplayAllowPrinterChange, displayAutoPrintProductEnabled, setDisplayAutoPrintProductEnabled, savingDisplayPrintingConfig,
49
+ setSavingDisplayPrintingConfig, hasLoadedCurrentConfig, setHasLoadedCurrentConfig, hasLoadedCompanyConfigs, setHasLoadedCompanyConfigs, hasLoadedMovementData, setHasLoadedMovementData, hasLoadedCurrentConfigRef, hasLoadedCompanyConfigsRef, hasLoadedMovementDataRef, hasInitializedPdvTabRef, alias,
50
+ setAlias, editingAlias, setEditingAlias, aliasInput, setAliasInput, savingAlias, setSavingAlias, removingDevice, setRemovingDevice, aliasInputRef, skipAliasSyncFromStoreRef, isOpen,
51
+ hasLocalPaymentGateway, paymentDeviceOptions, displayOptions, printerOptions, selectedPosOperationModeOption, pickerMode, packageVersion, appVersion, runtimeDeviceId, runtimeDeviceType, isEditingRuntimeDevice, resolveDeviceContext,
52
+ loadMovementData, refreshCurrentConfig, loadCompanyConfigs, loadProductShowcases, ensureActiveTabData, ensureActiveTabDataRef,
53
+ } = deps;
54
+ const handleToggle = useCallback(() => {
55
+ if (!isPdvDevice) {
56
+ return;
57
+ }
58
+
59
+ const msg = isOpen ? 'Deseja fechar o caixa?' : 'Deseja abrir o caixa?';
60
+ confirm(msg, async () => {
61
+ setActionLoading(true);
62
+ try {
63
+ const action = isOpen
64
+ ? actionsRef.current.invoiceActions.closeCashRegister
65
+ : actionsRef.current.invoiceActions.openCashRegister;
66
+ await action({ device: deviceString, provider: currentCompany.id });
67
+ await refreshCurrentConfig();
68
+ if (activePdvTab === PDV_TAB_MOVEMENT || hasLoadedMovementData) {
69
+ await loadMovementData();
70
+ }
71
+ } catch (error) {
72
+ showSystemError(
73
+ error,
74
+ 'Nao foi possivel atualizar o caixa deste device.',
75
+ );
76
+ } finally {
77
+ setActionLoading(false);
78
+ }
79
+ });
80
+ }, [
81
+ activePdvTab,
82
+ currentCompany?.id,
83
+ deviceString,
84
+ hasLoadedMovementData,
85
+ isOpen,
86
+ isPdvDevice,
87
+ loadMovementData,
88
+ refreshCurrentConfig,
89
+ showSystemError,
90
+ ]);
91
+
92
+ const startEditAlias = useCallback(() => {
93
+ setAliasInput(alias);
94
+ setEditingAlias(true);
95
+ setTimeout(() => aliasInputRef.current?.focus(), 80);
96
+ }, [alias]);
97
+
98
+ const cancelEditAlias = useCallback(() => {
99
+ setEditingAlias(false);
100
+ setAliasInput(alias);
101
+ }, [alias]);
102
+
103
+ const saveAlias = useCallback(async () => {
104
+ const trimmed = aliasInput.trim();
105
+ if (!trimmed || trimmed === alias || !deviceId) {
106
+ cancelEditAlias();
107
+ return;
108
+ }
109
+ setSavingAlias(true);
110
+ try {
111
+ const savedDevice = await actionsRef.current.deviceActions.save({
112
+ id: deviceId,
113
+ alias: trimmed,
114
+ });
115
+ const nextAlias = String(savedDevice?.alias || trimmed).trim();
116
+
117
+ const { mergedDevice, nextDeviceConfig } = buildDeviceAliasStoreUpdates({
118
+ deviceId,
119
+ nextAlias,
120
+ runtimeDevice,
121
+ runtimeDeviceConfig,
122
+ savedDevice,
123
+ normalizeEntityId,
124
+ });
125
+ actionsRef.current.deviceActions.setItem?.(mergedDevice);
126
+ if (nextDeviceConfig && actionsRef.current.deviceConfigActions?.setItem) {
127
+ actionsRef.current.deviceConfigActions.setItem(nextDeviceConfig);
128
+ }
129
+
130
+ skipAliasSyncFromStoreRef.current = true;
131
+ setAlias(nextAlias);
132
+ setAliasInput(nextAlias);
133
+ setEditingAlias(false);
134
+ } catch (error) {
135
+ showSystemError(error, 'Nao foi possivel salvar o nome do device.');
136
+ cancelEditAlias();
137
+ } finally {
138
+ setSavingAlias(false);
139
+ }
140
+ }, [aliasInput, alias, deviceId, cancelEditAlias, showSystemError]);
141
+
142
+ const confirmRemoveDevice = useCallback(() => {
143
+ if (!deviceId || removingDevice) {
144
+ return;
145
+ }
146
+ const label = String(alias || deviceString || deviceId).trim();
147
+ const message = `Tem certeza que deseja excluir o device "${label}"? Esta ação não pode ser desfeita.`;
148
+ // Use web-safe confirm() (window.confirm on web; Alert.alert on native).
149
+ // Alert.alert alone is a no-op on Manager web — root cause of "click does nothing".
150
+ confirm(message, async () => {
151
+ setRemovingDevice(true);
152
+ try {
153
+ await actionsRef.current.deviceActions.remove(deviceId);
154
+ if (navigation?.canGoBack?.()) {
155
+ navigation.goBack();
156
+ } else {
157
+ navigation.navigate('DevicesIndex');
158
+ }
159
+ } catch (error) {
160
+ showSystemError(error, 'Não foi possível excluir o device.');
161
+ } finally {
162
+ setRemovingDevice(false);
163
+ }
164
+ });
165
+ }, [deviceId, removingDevice, alias, deviceString, navigation, showSystemError]);
166
+
167
+ const saveDevicePaymentTarget = useCallback(async (override = {}) => {
168
+ const nextDevicePaymentTarget =
169
+ override.devicePaymentTarget ?? devicePaymentTarget;
170
+
171
+ if (!currentCompany?.id || !deviceString) {
172
+ return;
173
+ }
174
+
175
+ setSavingPaymentTarget(true);
176
+ try {
177
+ await actionsRef.current.deviceConfigActions.addDeviceConfigs({
178
+ device: deviceString,
179
+ configs: JSON.stringify({
180
+ [ORDER_PAYMENT_DEVICE_CONFIG_KEY]: nextDevicePaymentTarget || '',
181
+ }),
182
+ people: '/people/' + currentCompany.id,
183
+ type: deviceType,
184
+ });
185
+ await refreshCurrentConfig();
186
+ } catch (error) {
187
+ showSystemError(
188
+ error,
189
+ 'Nao foi possivel salvar o device de pagamento.',
190
+ );
191
+ } finally {
192
+ setSavingPaymentTarget(false);
193
+ }
194
+ }, [currentCompany?.id, devicePaymentTarget, deviceString, deviceType, refreshCurrentConfig, showSystemError]);
195
+
196
+ const savePdvSettings = useCallback(async (override = {}) => {
197
+ const nextPdvGateway = override.pdvGateway ?? pdvGateway;
198
+ const nextPdvPrinterEnabled =
199
+ override.pdvPrinterEnabled ?? pdvPrinterEnabled;
200
+
201
+ if (
202
+ !isPdvDevice ||
203
+ !currentCompany?.id ||
204
+ !deviceString ||
205
+ savingPdvSettings
206
+ ) {
207
+ return;
208
+ }
209
+
210
+ setSavingPdvSettings(true);
211
+ try {
212
+ await actionsRef.current.deviceConfigActions.addDeviceConfigs({
213
+ device: deviceString,
214
+ configs: JSON.stringify({
215
+ [POS_GATEWAY_CONFIG_KEY]: nextPdvGateway || '',
216
+ [PDV_PRINTER_ENABLED_CONFIG_KEY]: nextPdvPrinterEnabled ? '1' : '0',
217
+ }),
218
+ people: '/people/' + currentCompany.id,
219
+ type: deviceType,
220
+ });
221
+ await refreshCurrentConfig();
222
+ } catch (error) {
223
+ showSystemError(
224
+ error,
225
+ 'Nao foi possivel salvar as configuracoes de pagamento do PDV.',
226
+ );
227
+ } finally {
228
+ setSavingPdvSettings(false);
229
+ }
230
+ }, [
231
+ currentCompany?.id,
232
+ deviceString,
233
+ deviceType,
234
+ isPdvDevice,
235
+ pdvGateway,
236
+ pdvPrinterEnabled,
237
+ refreshCurrentConfig,
238
+ savingPdvSettings,
239
+ showSystemError,
240
+ ]);
241
+
242
+ const saveProductShowcaseConfig = useCallback(async (override = {}) => {
243
+ const nextProductShowcaseId =
244
+ override.productShowcaseId ?? productShowcaseId;
245
+
246
+ if (!currentCompany?.id || !deviceString || savingProductShowcase) {
247
+ return;
248
+ }
249
+
250
+ setSavingProductShowcase(true);
251
+ try {
252
+ await actionsRef.current.deviceConfigActions.addDeviceConfigs({
253
+ device: deviceString,
254
+ configs: JSON.stringify({
255
+ [POS_PRODUCT_SHOWCASE_CONFIG_KEY]: nextProductShowcaseId || '',
256
+ }),
257
+ people: '/people/' + currentCompany.id,
258
+ type: deviceType,
259
+ });
260
+ await refreshCurrentConfig();
261
+ } catch (error) {
262
+ showSystemError(
263
+ error,
264
+ 'Nao foi possivel salvar a vitrine deste PDV.',
265
+ );
266
+ } finally {
267
+ setSavingProductShowcase(false);
268
+ }
269
+ }, [
270
+ currentCompany?.id,
271
+ deviceString,
272
+ deviceType,
273
+ productShowcaseId,
274
+ refreshCurrentConfig,
275
+ savingProductShowcase,
276
+ showSystemError,
277
+ ]);
278
+
279
+ const savePaymentTypeConfigs = useCallback(
280
+ async nextSelectedPaymentTypeIds => {
281
+ if (
282
+ !currentCompany?.id ||
283
+ !deviceString ||
284
+ savingPaymentTypes
285
+ ) {
286
+ return;
287
+ }
288
+
289
+ setSavingPaymentTypes(true);
290
+ try {
291
+ const nextConfigs = appendScreenMetrics({
292
+ ...(configs || {}),
293
+ [PAYMENT_TYPE_IDS_CONFIG_KEY]: nextSelectedPaymentTypeIds,
294
+ 'config-version': appVersion,
295
+ });
296
+
297
+ await actionsRef.current.deviceConfigActions.addDeviceConfigs({
298
+ device: deviceString,
299
+ configs: JSON.stringify(nextConfigs),
300
+ people: '/people/' + currentCompany.id,
301
+ type: deviceType,
302
+ });
303
+
304
+ actionsRef.current.deviceConfigActions.setItem({
305
+ ...(currentDeviceConfig || {}),
306
+ configs: nextConfigs,
307
+ device:
308
+ currentDeviceConfig?.device ||
309
+ currentDevice ||
310
+ {device: deviceString, type: deviceType},
311
+ people:
312
+ currentDeviceConfig?.people ||
313
+ `/people/${currentCompany.id}`,
314
+ type:
315
+ currentDeviceConfig?.type ||
316
+ deviceType,
317
+ });
318
+ setConfigs(nextConfigs);
319
+ } catch (error) {
320
+ showSystemError(
321
+ error,
322
+ 'Nao foi possivel salvar os tipos de pagamento do device.',
323
+ );
324
+ } finally {
325
+ setSavingPaymentTypes(false);
326
+ }
327
+ },
328
+ [
329
+ currentCompany?.id,
330
+ appVersion,
331
+ configs,
332
+ currentDevice,
333
+ currentDeviceConfig,
334
+ deviceString,
335
+ deviceType,
336
+ savingPaymentTypes,
337
+ showSystemError,
338
+ ],
339
+ );
340
+
341
+ const savePosOperationMode = useCallback(async (override = {}) => {
342
+ const nextPosOperationMode =
343
+ override.posOperationMode ?? posOperationMode;
344
+ const nextAndroidKioskEnabled =
345
+ override.androidKioskEnabled ?? androidKioskEnabled;
346
+ const nextCheckOrderType =
347
+ override.checkOrderType ?? checkOrderType;
348
+ const nextCheckOrderManagementMode =
349
+ override.checkOrderManagementMode ?? checkOrderManagementMode;
350
+ const nextCounterAutoPrintEnabled =
351
+ override.counterAutoPrintEnabled ?? counterAutoPrintEnabled;
352
+ const nextCounterPrintMode =
353
+ override.counterPrintMode ?? counterPrintMode;
354
+ const nextCounterCashManagementMode =
355
+ override.counterCashManagementMode ?? counterCashManagementMode;
356
+
357
+ if (
358
+ !isPdvDevice ||
359
+ !currentCompany?.id ||
360
+ !deviceString ||
361
+ savingPosOperationMode
362
+ ) {
363
+ return;
364
+ }
365
+
366
+ setSavingPosOperationMode(true);
367
+ try {
368
+ const nextOperationConfigs = {
369
+ [POS_OPERATION_MODE_CONFIG_KEY]: nextPosOperationMode,
370
+ [DEVICE_ANDROID_KIOSK_ENABLED_CONFIG_KEY]: nextAndroidKioskEnabled
371
+ ? '1'
372
+ : '0',
373
+ [POS_CHECK_ORDER_TYPE_CONFIG_KEY]: nextCheckOrderType,
374
+ [POS_CHECK_ORDER_MANAGEMENT_MODE_CONFIG_KEY]:
375
+ nextCheckOrderType === POS_CHECK_ORDER_TYPE_NONE
376
+ ? POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE
377
+ : nextCheckOrderManagementMode,
378
+ };
379
+
380
+ if (nextPosOperationMode === POS_OPERATION_MODE_COUNTER) {
381
+ nextOperationConfigs[POS_AUTO_PRINT_ENABLED_CONFIG_KEY] =
382
+ nextCounterAutoPrintEnabled ? '1' : '0';
383
+ nextOperationConfigs['print-mode'] = nextCounterPrintMode;
384
+ nextOperationConfigs[POS_CASH_MANAGEMENT_MODE_CONFIG_KEY] =
385
+ nextCounterCashManagementMode;
386
+ }
387
+
388
+ await actionsRef.current.deviceConfigActions.addDeviceConfigs({
389
+ device: deviceString,
390
+ configs: JSON.stringify(nextOperationConfigs),
391
+ people: '/people/' + currentCompany.id,
392
+ type: deviceType,
393
+ });
394
+ await refreshCurrentConfig();
395
+ } catch (error) {
396
+ showSystemError(
397
+ error,
398
+ 'Nao foi possivel salvar a operacao do PDV.',
399
+ );
400
+ } finally {
401
+ setSavingPosOperationMode(false);
402
+ }
403
+ }, [
404
+ currentCompany?.id,
405
+ checkOrderManagementMode,
406
+ checkOrderType,
407
+ counterAutoPrintEnabled,
408
+ counterCashManagementMode,
409
+ counterPrintMode,
410
+ deviceString,
411
+ deviceType,
412
+ androidKioskEnabled,
413
+ isPdvDevice,
414
+ posOperationMode,
415
+ refreshCurrentConfig,
416
+ savingPosOperationMode,
417
+ showSystemError,
418
+ ]);
419
+
420
+ useEffect(() => {
421
+ if (!isPdvDevice || !currentCompany?.id || !deviceString) {
422
+ return;
423
+ }
424
+
425
+ const rawCheckOrderType = resolvePosCheckOrderType(configs);
426
+ const nextCheckOrderType = resolvePosCheckOrderTypeForShop(
427
+ configs,
428
+ runtimeCompanyConfigs,
429
+ );
430
+
431
+ if (
432
+ rawCheckOrderType !== POS_CHECK_ORDER_TYPE_STAMP ||
433
+ nextCheckOrderType !== POS_CHECK_ORDER_TYPE_NONE ||
434
+ savingPosOperationMode
435
+ ) {
436
+ stampAutoDisableSignatureRef.current = '';
437
+ return;
438
+ }
439
+
440
+ const signature = [
441
+ currentCompany?.id,
442
+ deviceString,
443
+ rawCheckOrderType,
444
+ loyaltyCouponsEnabled ? '1' : '0',
445
+ String(configs?.['config-version'] || ''),
446
+ ].join(':');
447
+
448
+ if (stampAutoDisableSignatureRef.current === signature) {
449
+ return;
450
+ }
451
+
452
+ stampAutoDisableSignatureRef.current = signature;
453
+
454
+ setCheckOrderType(POS_CHECK_ORDER_TYPE_NONE);
455
+ setCheckOrderManagementMode(POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE);
456
+ savePosOperationMode({
457
+ checkOrderType: POS_CHECK_ORDER_TYPE_NONE,
458
+ checkOrderManagementMode: POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE,
459
+ });
460
+ }, [
461
+ configs,
462
+ currentCompany?.id,
463
+ deviceString,
464
+ isPdvDevice,
465
+ loyaltyCouponsEnabled,
466
+ savePosOperationMode,
467
+ savingPosOperationMode,
468
+ runtimeCompanyConfigs,
469
+ ]);
470
+
471
+ return {
472
+ handleToggle,
473
+ startEditAlias,
474
+ cancelEditAlias,
475
+ saveAlias,
476
+ confirmRemoveDevice,
477
+ saveDevicePaymentTarget,
478
+ savePdvSettings,
479
+ saveProductShowcaseConfig,
480
+ savePaymentTypeConfigs,
481
+ savePosOperationMode,
482
+ };
483
+ }
@@ -0,0 +1,87 @@
1
+ import {useCallback, useState} from 'react';
2
+ import {executeCopyDeviceConfigs} from '@controleonline/ui-common/src/react/utils/copyDeviceConfigs';
3
+
4
+ /**
5
+ * Copy device_config from another company device onto the current detail target.
6
+ * Refs: app-community#629
7
+ */
8
+ export default function useDeviceDetailCopyConfig({
9
+ actionsRef,
10
+ alias,
11
+ currentCompanyId,
12
+ deviceString,
13
+ loadCompanyConfigs,
14
+ messageApi,
15
+ refreshCurrentConfig,
16
+ showSystemError,
17
+ }) {
18
+ const [copyModalVisible, setCopyModalVisible] = useState(false);
19
+ const [copyingConfig, setCopyingConfig] = useState(false);
20
+
21
+ const openCopyConfigModal = useCallback(() => {
22
+ if (!deviceString || copyingConfig) {
23
+ return;
24
+ }
25
+ setCopyModalVisible(true);
26
+ loadCompanyConfigs?.();
27
+ }, [deviceString, copyingConfig, loadCompanyConfigs]);
28
+
29
+ const closeCopyConfigModal = useCallback(() => {
30
+ if (copyingConfig) {
31
+ return;
32
+ }
33
+ setCopyModalVisible(false);
34
+ }, [copyingConfig]);
35
+
36
+ const handleCopyConfigConfirm = useCallback(
37
+ async sourceOption => {
38
+ if (!sourceOption || !currentCompanyId || !deviceString || copyingConfig) {
39
+ return;
40
+ }
41
+ setCopyingConfig(true);
42
+ try {
43
+ const summary = await executeCopyDeviceConfigs({
44
+ addDeviceConfigs: params =>
45
+ actionsRef.current.deviceConfigActions.addDeviceConfigs(params),
46
+ sourceConfigs: sourceOption.configs || [],
47
+ destinationDeviceString: deviceString,
48
+ peopleIri: `/people/${currentCompanyId}`,
49
+ });
50
+ setCopyModalVisible(false);
51
+ await refreshCurrentConfig?.();
52
+ const keysLabel =
53
+ summary.keys && summary.keys.length
54
+ ? summary.keys.join(', ')
55
+ : '(nenhuma chave)';
56
+ messageApi?.showSuccess?.(
57
+ `Configurações copiadas (${(summary.types || []).join(', ') || '—'}). Chaves: ${keysLabel}`,
58
+ );
59
+ } catch (error) {
60
+ showSystemError?.(
61
+ error,
62
+ 'Não foi possível copiar as configurações do device.',
63
+ );
64
+ } finally {
65
+ setCopyingConfig(false);
66
+ }
67
+ },
68
+ [
69
+ actionsRef,
70
+ copyingConfig,
71
+ currentCompanyId,
72
+ deviceString,
73
+ messageApi,
74
+ refreshCurrentConfig,
75
+ showSystemError,
76
+ ],
77
+ );
78
+
79
+ return {
80
+ alias,
81
+ copyModalVisible,
82
+ copyingConfig,
83
+ openCopyConfigModal,
84
+ closeCopyConfigModal,
85
+ handleCopyConfigConfirm,
86
+ };
87
+ }