@controleonline/ui-common 1.2.80 → 1.2.82

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 +44 -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 +495 -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 +489 -0
  43. package/src/react/pages/Devices/detail/useDeviceDetailStateB.js +98 -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,233 @@
1
+ import React from 'react';
2
+ import { Text, View, Switch, TextInput, TouchableOpacity, Platform } from 'react-native';
3
+ import { Picker } from '@react-native-picker/picker';
4
+ import Icon from 'react-native-vector-icons/Feather';
5
+ import styles from '../../DeviceDetailPage.styles';
6
+ import {
7
+ POS_OPERATION_MODE_OPTIONS,
8
+ POS_OPERATION_MODE_COUNTER,
9
+ POS_PRINT_MODE_ORDER,
10
+ POS_PRINT_MODE_FORM,
11
+ POS_CHECK_ORDER_TYPE_NONE,
12
+ POS_CHECK_ORDER_TYPE_TAB,
13
+ POS_CHECK_ORDER_TYPE_TABLE,
14
+ POS_CHECK_ORDER_TYPE_STAMP,
15
+ POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE,
16
+ POS_CHECK_ORDER_MANAGEMENT_MODE_EXISTING_ONLY,
17
+ POS_CASH_MANAGEMENT_MODE_CASH_REGISTER,
18
+ POS_CASH_MANAGEMENT_MODE_DAILY,
19
+ POS_OPERATION_MODE_CONFIG_KEY,
20
+ DEVICE_ORDER_VISIBILITY_COMPANY,
21
+ DEVICE_ORDER_VISIBILITY_DEVICE,
22
+ resolvePosOperationMode,
23
+ } from '@controleonline/ui-common/src/react/config/deviceConfigBootstrap';
24
+ import { normalizeEntityId } from '@controleonline/ui-common/src/react/utils/paymentDevices';
25
+ import { getProductShowcaseLabel } from './deviceDetailHelpers';
26
+ import { tt } from './deviceDetailConstants';
27
+
28
+ /**
29
+ * Extracted from DeviceDetailScreen for modularization (≤500 lines).
30
+ * Refs: app-community#382
31
+ */
32
+ export default function DeviceDetailAlertsCommandsSection(ctx) {
33
+ const {
34
+ themeColors, brandColors, palette,
35
+ renderHelpButton, renderOptionButtons, renderSwitchRow, renderProduct,
36
+ posOperationMode, setPosOperationMode, savePosOperationMode, savingPosOperationMode,
37
+ productShowcaseId, setProductShowcaseId, saveProductShowcaseConfig,
38
+ productShowcases, loadingProductShowcases, savingProductShowcase, pickerMode,
39
+ counterAutoPrintEnabled, setCounterAutoPrintEnabled,
40
+ counterPrintMode, setCounterPrintMode,
41
+ checkOrderType, setCheckOrderType,
42
+ checkOrderManagementMode, setCheckOrderManagementMode,
43
+ cashManagementMode, setCashManagementMode,
44
+ counterCashManagementMode, setCounterCashManagementMode,
45
+ androidKioskEnabled, setAndroidKioskEnabled,
46
+ androidLauncherEnabled, setAndroidLauncherEnabled,
47
+ saveLauncherMode, savingLauncherMode,
48
+ orderVisibility, setOrderVisibility, saveDeviceOrderVisibility, savingOrderVisibility,
49
+ shouldShowOrderVisibility,
50
+ deliveryEnabled, setDeliveryEnabled, saveDeviceDeliverySettings, savingDeliverySettings,
51
+ deviceAlertSoundEnabled, setDeviceAlertSoundEnabled, deviceAlertSoundUrl, setDeviceAlertSoundUrl,
52
+ saveDeviceAlertSoundConfig, savingAlertSound,
53
+ runtimeDebugInfoEnabled, setRuntimeDebugInfoEnabled, saveDeviceRuntimeDebugInfo, savingRuntimeDebugInfo,
54
+ devicePaymentTarget, setDevicePaymentTarget, saveDevicePaymentTarget, savingPaymentTarget,
55
+ paymentDeviceOptions, displayOptions, printerOptions,
56
+ pdvGateway, setPdvGateway, pdvPrinterEnabled, setPdvPrinterEnabled, savePdvSettings, savingPdvSettings,
57
+ hasLocalPaymentGateway, loyaltyCouponsEnabled,
58
+ sendCatalogRefreshCommand, sendingCatalogRefresh,
59
+ displayAutoPrintProduct, setDisplayAutoPrintProduct,
60
+ displayAllowPrinterChange, setDisplayAllowPrinterChange,
61
+ saveDisplayPrintingConfig, savingDisplayPrinting,
62
+ linkedDisplayId, setLinkedDisplayId,
63
+ isDisplayDevice, isPdvDevice,
64
+ styles: _styles,
65
+ } = ctx;
66
+
67
+ return (
68
+ <>
69
+ <View style={styles.section}>
70
+ <Text style={styles.sectionTitle}>
71
+ <Icon name="volume-2" size={13} /> {' '}Aviso Sonoro
72
+ </Text>
73
+
74
+ <View style={styles.configCard}>
75
+ <View style={styles.sectionTitleRow}>
76
+ <Text style={styles.configTitle}>Alerta via websocket</Text>
77
+ {renderHelpButton(
78
+ 'Alerta via websocket',
79
+ 'Quando habilitado, este device toca o audio configurado ao receber o evento order.created de um novo pedido em preparo.',
80
+ )}
81
+ </View>
82
+
83
+ {renderSwitchRow({
84
+ disabled: savingAlertSound,
85
+ label: 'Aviso sonoro habilitado',
86
+ value: deviceAlertSoundEnabled,
87
+ valueLabel: deviceAlertSoundEnabled ? 'Ativo' : 'Inativo',
88
+ onValueChange: nextValue => {
89
+ setDeviceAlertSoundEnabled(nextValue);
90
+ saveDeviceAlertSoundConfig({
91
+ deviceAlertSoundEnabled: nextValue,
92
+ });
93
+ },
94
+ })}
95
+
96
+ <View style={styles.textInputWrap}>
97
+ <Text style={styles.textInputLabel}>URL do audio</Text>
98
+ <TextInput
99
+ style={styles.textInput}
100
+ value={deviceAlertSoundUrl}
101
+ onChangeText={setDeviceAlertSoundUrl}
102
+ placeholder="https://exemplo.com/alerta.mp3"
103
+ placeholderTextColor="#94A3B8"
104
+ autoCapitalize="none"
105
+ autoCorrect={false}
106
+ keyboardType="url"
107
+ returnKeyType="done"
108
+ onSubmitEditing={saveDeviceAlertSoundConfig}
109
+ onBlur={saveDeviceAlertSoundConfig}
110
+ />
111
+ </View>
112
+ </View>
113
+ </View>
114
+ )}
115
+
116
+ {shouldShowDeviceBehavior && (
117
+ <View style={styles.section}>
118
+ <Text style={styles.sectionTitle}>
119
+ <Icon name="activity" size={13} /> {' '}Rodapé do Sistema
120
+ </Text>
121
+
122
+ <View style={styles.configCard}>
123
+ <View style={styles.sectionTitleRow}>
124
+ <Text style={styles.configTitle}>Debug do socket no rodapé</Text>
125
+ {renderHelpButton(
126
+ 'Debug do socket no rodapé',
127
+ 'Quando habilitado, este device troca a bolinha discreta do socket pelos detalhes de debug publicados pelos serviços do runtime no rodapé global do sistema.',
128
+ )}
129
+ </View>
130
+
131
+ {renderSwitchRow({
132
+ disabled: savingRuntimeDebugInfo,
133
+ label: 'Exibir debug detalhado',
134
+ value: deviceRuntimeDebugInfoEnabled,
135
+ valueLabel: deviceRuntimeDebugInfoEnabled ? 'Ativo' : 'Inativo',
136
+ onValueChange: nextValue => {
137
+ setDeviceRuntimeDebugInfoEnabled(nextValue);
138
+ saveDeviceRuntimeDebugInfo({
139
+ deviceRuntimeDebugInfoEnabled: nextValue,
140
+ });
141
+ },
142
+ })}
143
+ </View>
144
+ </View>
145
+ )}
146
+
147
+ {shouldShowRemotePayment && (
148
+ <View style={styles.section}>
149
+ <Text style={styles.sectionTitle}>
150
+ <Icon name="credit-card" size={13} /> {' '}Pagamento Remoto
151
+ </Text>
152
+
153
+ <View style={styles.configCard}>
154
+ <View style={styles.sectionTitleRow}>
155
+ <Text style={styles.configTitle}>Device preferencial para pagamento</Text>
156
+ {renderHelpButton(
157
+ 'Device preferencial para pagamento',
158
+ 'Esse destino funciona como fallback desta origem quando a empresa não definiu uma ordem padrão no configurador geral. Quando a empresa tiver devices padrão para pagamento remoto, essa ordem global tem prioridade.',
159
+ )}
160
+ </View>
161
+
162
+ <View style={styles.pickerWrap}>
163
+ <Picker
164
+ selectedValue={devicePaymentTarget || ''}
165
+ mode={pickerMode}
166
+ style={styles.picker}
167
+ dropdownIconColor="#64748B"
168
+ onValueChange={value => {
169
+ const nextValue = value || '';
170
+ setDevicePaymentTarget(nextValue);
171
+ saveDevicePaymentTarget({
172
+ devicePaymentTarget: nextValue,
173
+ });
174
+ }}>
175
+ <Picker.Item
176
+ label="Usar devices padrão da empresa"
177
+ value=""
178
+ />
179
+ {paymentDeviceOptions.map(option => (
180
+ <Picker.Item
181
+ key={option.deviceId}
182
+ label={`${option.alias} (${option.gatewayLabel})`}
183
+ value={option.deviceId}
184
+ />
185
+ ))}
186
+ </Picker>
187
+ </View>
188
+ </View>
189
+ </View>
190
+ )}
191
+
192
+ {shouldShowRemoteCommands && (
193
+ <View style={styles.section}>
194
+ <Text style={styles.sectionTitle}>
195
+ <Icon name="refresh-cw" size={13} /> {' '}Comandos Remotos
196
+ </Text>
197
+
198
+ <View style={styles.configCard}>
199
+ <View style={styles.sectionTitleRow}>
200
+ <Text style={styles.configTitle}>Catálogo do PDV</Text>
201
+ {renderHelpButton(
202
+ 'Catálogo do PDV',
203
+ 'Limpa o cache local de produtos e categorias deste device. O recarregamento acontece no próximo uso do PDV.',
204
+ )}
205
+ </View>
206
+
207
+ <TouchableOpacity
208
+ style={[
209
+ styles.configButton,
210
+ {
211
+ backgroundColor: themeColors.buttonBackground,
212
+ borderColor: themeColors.buttonBackground,
213
+ },
214
+ sendingCatalogRefresh && {opacity: 0.6},
215
+ ]}
216
+ activeOpacity={0.85}
217
+ disabled={sendingCatalogRefresh}
218
+ onPress={sendCatalogRefreshCommand}>
219
+ <Icon name="trash-2" size={16} color={themeColors.buttonIcon} />
220
+ <Text
221
+ style={[
222
+ styles.configButtonText,
223
+ {color: themeColors.buttonText},
224
+ ]}>
225
+ {sendingCatalogRefresh ? 'Limpando cache...' : 'Limpar cache de produtos'}
226
+ </Text>
227
+ </TouchableOpacity>
228
+ </View>
229
+ </View>
230
+ )}
231
+ </>
232
+ );
233
+ }
@@ -0,0 +1,138 @@
1
+ import React from 'react';
2
+ import { Text, TextInput, TouchableOpacity, View } from 'react-native';
3
+ import Icon from 'react-native-vector-icons/Feather';
4
+ import { withOpacity } from '@controleonline/../../src/styles/branding';
5
+ import styles from '../../DeviceDetailPage.styles';
6
+
7
+ /**
8
+ * Device detail header: icon + alias edit/save/trash + deviceString.
9
+ * Refs: app-community#382
10
+ */
11
+ export default function DeviceDetailHeader({
12
+ accent,
13
+ alias,
14
+ aliasInput,
15
+ aliasInputRef,
16
+ deviceId,
17
+ deviceString,
18
+ editingAlias,
19
+ removingDevice,
20
+ savingAlias,
21
+ copyingConfig,
22
+ themeColors,
23
+ onAliasChange,
24
+ onConfirmRemove,
25
+ onCopyConfig,
26
+ onSaveAlias,
27
+ onStartEdit,
28
+ }) {
29
+ return (
30
+ <View style={styles.deviceHeader}>
31
+ <View style={styles.deviceHeaderLeft}>
32
+ <View style={[styles.deviceIconBox, { backgroundColor: withOpacity(accent, 0.1) }]}>
33
+ <Icon name="monitor" size={20} color={accent} />
34
+ </View>
35
+
36
+ <View style={styles.aliasBlock}>
37
+ <View style={styles.aliasRow}>
38
+ {editingAlias ? (
39
+ <TextInput
40
+ ref={aliasInputRef}
41
+ style={styles.aliasInput}
42
+ value={aliasInput}
43
+ onChangeText={onAliasChange}
44
+ onSubmitEditing={onSaveAlias}
45
+ returnKeyType="done"
46
+ autoCapitalize="words"
47
+ selectTextOnFocus
48
+ testID="device-alias-input"
49
+ />
50
+ ) : (
51
+ <Text
52
+ style={styles.deviceAlias}
53
+ numberOfLines={1}
54
+ ellipsizeMode="tail"
55
+ testID="device-alias-text"
56
+ >
57
+ {alias}
58
+ </Text>
59
+ )}
60
+
61
+ {!!deviceId && (
62
+ <TouchableOpacity
63
+ style={[
64
+ styles.editAliasBtn,
65
+ {
66
+ backgroundColor: themeColors.buttonBackground,
67
+ borderColor: themeColors.buttonBackground,
68
+ },
69
+ ]}
70
+ onPress={editingAlias ? onSaveAlias : onStartEdit}
71
+ activeOpacity={0.8}
72
+ disabled={savingAlias || removingDevice}
73
+ testID={editingAlias ? 'device-alias-save' : 'device-alias-edit'}
74
+ accessibilityLabel={editingAlias ? 'Salvar alias' : 'Editar alias'}
75
+ >
76
+ <Icon
77
+ name={savingAlias ? 'save' : editingAlias ? 'check' : 'edit-2'}
78
+ size={16}
79
+ color={themeColors.buttonIcon}
80
+ />
81
+ </TouchableOpacity>
82
+ )}
83
+ {!!deviceId && !editingAlias && (
84
+ <TouchableOpacity
85
+ style={[
86
+ styles.editAliasBtn,
87
+ {
88
+ backgroundColor: themeColors.buttonBackground,
89
+ borderColor: themeColors.buttonBackground,
90
+ marginLeft: 8,
91
+ },
92
+ ]}
93
+ onPress={onCopyConfig}
94
+ activeOpacity={0.8}
95
+ disabled={removingDevice || savingAlias || copyingConfig || !deviceString}
96
+ accessibilityLabel="Copiar configurações de outro device"
97
+ testID="device-detail-copy-config-btn"
98
+ >
99
+ <Icon
100
+ name={copyingConfig ? 'loader' : 'copy'}
101
+ size={16}
102
+ color={themeColors.buttonIcon}
103
+ />
104
+ </TouchableOpacity>
105
+ )}
106
+ {!!deviceId && !editingAlias && (
107
+ <TouchableOpacity
108
+ style={[
109
+ styles.editAliasBtn,
110
+ {
111
+ backgroundColor: themeColors.buttonBackground,
112
+ borderColor: themeColors.buttonBackground,
113
+ marginLeft: 8,
114
+ },
115
+ ]}
116
+ onPress={onConfirmRemove}
117
+ activeOpacity={0.8}
118
+ disabled={removingDevice || savingAlias || copyingConfig}
119
+ accessibilityLabel="Excluir device"
120
+ testID="device-detail-delete-btn"
121
+ >
122
+ <Icon
123
+ name={removingDevice ? 'loader' : 'trash-2'}
124
+ size={16}
125
+ color={themeColors.buttonIcon}
126
+ />
127
+ </TouchableOpacity>
128
+ )}
129
+ </View>
130
+
131
+ <Text style={styles.deviceString} numberOfLines={1} ellipsizeMode="middle">
132
+ {deviceString}
133
+ </Text>
134
+ </View>
135
+ </View>
136
+ </View>
137
+ );
138
+ }
@@ -0,0 +1,167 @@
1
+ import React from 'react';
2
+ import { Text, View, FlatList, TextInput } from 'react-native';
3
+ import Icon from 'react-native-vector-icons/Feather';
4
+ import Formatter from '@controleonline/ui-common/src/utils/formatter';
5
+ import styles from '../../DeviceDetailPage.styles';
6
+ import { hex } from './deviceDetailConstants';
7
+
8
+ /**
9
+ * PDV movement tab sections.
10
+ * Refs: app-community#382
11
+ */
12
+ export default function DeviceDetailMovementSections(ctx) {
13
+ const {
14
+ showPdvMovementTab, inflowData, productTotal, wallets, filteredProducts, search, setSearch,
15
+ renderProduct, loadingMovementData,
16
+ } = ctx;
17
+
18
+ return (
19
+ <>
20
+ {showPdvMovementTab && (
21
+ <View style={styles.summaryRow}>
22
+ <View style={styles.summaryCard}>
23
+ <Icon name="dollar-sign" size={14} color={hex.success} style={styles.summaryIcon} />
24
+ <Text style={styles.summaryLabel}>Total Geral</Text>
25
+ <Text style={[styles.summaryValue, { color: hex.success }]}>
26
+ {Formatter.formatMoney(inflowTotal)}
27
+ </Text>
28
+ </View>
29
+ <View style={styles.summaryCard}>
30
+ <Icon name="shopping-bag" size={14} color={hex.info} style={styles.summaryIcon} />
31
+ <Text style={styles.summaryLabel}>Em Produtos</Text>
32
+ <Text style={[styles.summaryValue, { color: hex.info }]}>
33
+ {Formatter.formatMoney(productTotal)}
34
+ </Text>
35
+ </View>
36
+ <View style={styles.summaryCard}>
37
+ <Icon name="package" size={14} color={hex.purple} style={styles.summaryIcon} />
38
+ <Text style={styles.summaryLabel}>Itens</Text>
39
+ <Text style={[styles.summaryValue, { color: hex.purple }]}>
40
+ {products.length}
41
+ </Text>
42
+ </View>
43
+ </View>
44
+ )}
45
+
46
+ {showPdvMovementTab && (
47
+ <View style={styles.section}>
48
+ <View style={styles.sectionHeaderRow}>
49
+ <Text style={styles.sectionTitle}>
50
+ <Icon name="shield" size={13} /> {' '}
51
+ {global.t?.t('manager', 'title', 'pdvMovement') || 'PDV Movement'}
52
+ </Text>
53
+ <TouchableOpacity
54
+ style={[
55
+ styles.toggleBtn,
56
+ {backgroundColor: isOpen ? hex.danger : hex.success},
57
+ actionLoading && {opacity: 0.6},
58
+ ]}
59
+ onPress={handleToggle}
60
+ disabled={actionLoading || loadingConfigData}
61
+ activeOpacity={0.85}>
62
+ <Icon
63
+ name={isOpen ? 'lock' : 'unlock'}
64
+ size={13}
65
+ color="#fff"
66
+ />
67
+ <Text style={styles.toggleBtnText}>
68
+ {actionLoading
69
+ ? isOpen
70
+ ? 'Fechando...'
71
+ : 'Abrindo...'
72
+ : isOpen
73
+ ? global.t?.t('orders', 'button', 'closeCashRegister') || 'Close'
74
+ : global.t?.t('orders', 'button', 'openCashRegister') || 'Open'}
75
+ </Text>
76
+ </TouchableOpacity>
77
+ </View>
78
+ </View>
79
+ )}
80
+
81
+ {showPdvMovementTab && wallets.length > 0 && (
82
+ <View style={styles.section}>
83
+ <Text style={styles.sectionTitle}>
84
+ <Icon name="credit-card" size={13} /> {' '}Recebimentos por Forma de Pagamento
85
+ </Text>
86
+ {wallets.map((wallet, wi) => (
87
+ <View key={wi} style={styles.walletCard}>
88
+ <View style={styles.walletHeader}>
89
+ <Icon name="briefcase" size={13} color="#64748B" />
90
+ <Text style={styles.walletName}>{wallet.wallet || 'Carteira'}</Text>
91
+ <Text style={[styles.walletTotal, { color: brandColors.primary }]}>
92
+ {Formatter.formatMoney(wallet.total)}
93
+ </Text>
94
+ </View>
95
+ {wallet.payments.map((pt, pi) => (
96
+ <View key={pi} style={styles.paymentRow}>
97
+ <View style={[styles.paymentIconBox, { backgroundColor: withOpacity(hex.info, 0.1) }]}>
98
+ <Icon name={paymentIcon(pt.payment)} size={11} color={hex.info} />
99
+ </View>
100
+ <Text style={styles.paymentName}>{pt.payment || '-'}</Text>
101
+ <Text style={styles.paymentValue}>
102
+ {Formatter.formatMoney(pt.inflow)}
103
+ </Text>
104
+ </View>
105
+ ))}
106
+ </View>
107
+ ))}
108
+ </View>
109
+ )}
110
+
111
+ {showPdvMovementTab && (
112
+ <View style={styles.section}>
113
+ <Text style={styles.sectionTitle}>
114
+ <Icon name="shopping-bag" size={13} /> {' '}Produtos Vendidos
115
+ </Text>
116
+
117
+ <View style={styles.searchRow}>
118
+ <Icon name="search" size={14} color="#94A3B8" />
119
+ <TextInput
120
+ style={styles.searchInput}
121
+ value={search}
122
+ onChangeText={setSearch}
123
+ placeholder="Buscar produto ou SKU..."
124
+ placeholderTextColor="#94A3B8"
125
+ />
126
+ {!!search && (
127
+ <TouchableOpacity onPress={() => setSearch('')}>
128
+ <Icon name="x" size={14} color="#94A3B8" />
129
+ </TouchableOpacity>
130
+ )}
131
+ </View>
132
+
133
+ {filteredProducts.length > 0 ? (
134
+ <View style={styles.tableContainer}>
135
+ <View style={styles.tableHeader}>
136
+ <Text style={[styles.tableHead, { flex: 0.5 }]}>Qtd</Text>
137
+ <Text style={[styles.tableHead, { flex: 3 }]}>Produto</Text>
138
+ <Text style={[styles.tableHead, { flex: 1.2, textAlign: 'right' }]}>Unit.</Text>
139
+ <Text style={[styles.tableHead, { flex: 1.3, textAlign: 'right' }]}>Total</Text>
140
+ </View>
141
+ <FlatList
142
+ data={filteredProducts}
143
+ keyExtractor={(item, i) => `${item.product_sku || i}`}
144
+ renderItem={renderProduct}
145
+ scrollEnabled={false}
146
+ />
147
+ <View style={styles.tableFooter}>
148
+ <Text style={styles.tableFooterLabel}>Total em produtos</Text>
149
+ <Text style={[styles.tableFooterValue, { color: brandColors.primary }]}>
150
+ {Formatter.formatMoney(productTotal)}
151
+ </Text>
152
+ </View>
153
+ </View>
154
+ ) : (
155
+ <View style={styles.emptyBox}>
156
+ <Icon name="inbox" size={24} color="#CBD5E1" style={inlineStyle_1301_61} />
157
+ <Text style={styles.emptyText}>
158
+ {search ? 'Nenhum produto encontrado para esta busca' : 'Nenhum produto registrado neste equipamento'}
159
+ </Text>
160
+ </View>
161
+ )}
162
+ </View>
163
+ )}
164
+
165
+ </>
166
+ );
167
+ }