@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.
- package/AGENTS.md +8 -0
- package/package.json +9 -3
- package/src/api/fetch.js +7 -7
- package/src/react/components/AddImportModal.js +6 -2
- package/src/react/components/BackgroundRuntimeBridge.js +201 -215
- package/src/react/components/ConfirmModal.js +44 -9
- package/src/react/components/ConfirmModal.styles.js +55 -41
- package/src/react/components/CopyDeviceConfigModal.js +193 -0
- package/src/react/components/CopyDeviceConfigModal.styles.js +123 -0
- package/src/react/components/DefaultProvider.native.js +22 -0
- package/src/react/components/DefaultProvider.web.js +25 -1
- package/src/react/components/EntityLogBranch.js +482 -0
- package/src/react/components/EntityLogCards.js +369 -0
- package/src/react/components/EntityLogContent.js +113 -794
- package/src/react/components/LinkedOrderProductsTab.js +3 -3
- package/src/react/components/PrintService.js +130 -409
- package/src/react/components/RemoteCheckoutService.js +2 -2
- package/src/react/components/RuntimeBottomNavigationBar.js +141 -146
- package/src/react/components/RuntimeFooterMarqueeText.js +137 -0
- package/src/react/components/RuntimeInfoFooter.js +12 -15
- package/src/react/components/SystemErrorToast.js +49 -29
- package/src/react/components/SystemErrorToast.styles.js +43 -38
- package/src/react/components/UserAvatar.js +120 -28
- package/src/react/config/deviceConfigBootstrap.js +330 -304
- package/src/react/hooks/usePrintSpoolEffects.js +161 -0
- package/src/react/pages/DeviceDetailPage.js +6 -2882
- package/src/react/pages/Devices/detail/DeviceDetailAlertsCommandsSection.js +233 -0
- package/src/react/pages/Devices/detail/DeviceDetailHeader.js +138 -0
- package/src/react/pages/Devices/detail/DeviceDetailMovementSections.js +167 -0
- package/src/react/pages/Devices/detail/DeviceDetailOrdersPrintSection.js +216 -0
- package/src/react/pages/Devices/detail/DeviceDetailPaymentSection.js +44 -0
- package/src/react/pages/Devices/detail/DeviceDetailPdvConfigSection.js +390 -0
- package/src/react/pages/Devices/detail/DeviceDetailRenderers.js +135 -0
- package/src/react/pages/Devices/detail/DeviceDetailScreen.js +495 -0
- package/src/react/pages/Devices/detail/OptionButtonChip.js +65 -0
- package/src/react/pages/Devices/detail/deviceDetailConstants.js +36 -0
- package/src/react/pages/Devices/detail/deviceDetailHelpers.js +70 -0
- package/src/react/pages/Devices/detail/useDeviceDetailActions.js +483 -0
- package/src/react/pages/Devices/detail/useDeviceDetailCopyConfig.js +87 -0
- package/src/react/pages/Devices/detail/useDeviceDetailLoaders.js +327 -0
- package/src/react/pages/Devices/detail/useDeviceDetailSaves.js +429 -0
- package/src/react/pages/Devices/detail/useDeviceDetailStateA.js +489 -0
- package/src/react/pages/Devices/detail/useDeviceDetailStateB.js +98 -0
- package/src/react/pages/Devices/deviceTypes/index.js +2 -0
- package/src/react/pages/Devices/deviceTypes/pdv.js +11 -0
- package/src/react/pages/Imports.js +3 -2
- package/src/react/pages/IntegrationConfigFields.js +126 -0
- package/src/react/pages/IntegrationConfigPage.js +122 -591
- package/src/react/pages/IntegrationConfigPage.utils.js +95 -0
- package/src/react/print/usePrintButtonController.js +9 -1
- package/src/react/services/Getnet/Checkout.js +81 -0
- package/src/react/services/Getnet/Getnet.js +77 -0
- package/src/react/services/paymentGatewayExecution.js +20 -0
- package/src/react/utils/bottomNavigationToolbar.js +161 -0
- package/src/react/utils/commercialDocumentOrders.js +18 -6
- package/src/react/utils/copyDeviceConfigs.js +198 -0
- package/src/react/utils/fileUrl.js +43 -0
- package/src/react/utils/importStatus.js +19 -10
- package/src/react/utils/logSettings.js +7 -7
- package/src/react/utils/maintenanceSettings.js +16 -0
- package/src/react/utils/paymentDevices.js +6 -0
- package/src/react/utils/printManagedPrinter.js +45 -0
- package/src/react/utils/printRouting.js +63 -0
- package/src/react/utils/printSpoolUtils.js +119 -0
- package/src/react/utils/printerDevices.js +368 -368
- package/src/react/utils/runtimeFooter.js +10 -19
- package/src/react/utils/shopFranchises.js +186 -65
- package/src/react/utils/systemErrorMessage.js +71 -67
- package/src/store/device_config/customActions.js +119 -30
- package/src/tests/browser/manager/bottom-navigation-role-toolbar.spec.js +353 -0
- package/src/tests/browser/manager/device-detail-alias-save.spec.js +345 -0
- package/src/tests/browser/manager/device-detail-copy-config.spec.js +365 -0
- package/src/tests/browser/manager/runtime-footer-marquee.spec.js +262 -0
- package/src/tests/browser/manager/theme-confirm-toast.spec.js +236 -0
- package/src/tests/react/components/RuntimeFooterMarqueeText.test.js +171 -0
- package/src/tests/react/components/UserAvatar.test.js +6 -2
- package/src/tests/react/components/themeAwareModals.test.mjs +60 -0
- package/src/tests/react/config/deviceConfigBootstrap.test.js +104 -66
- package/src/tests/react/pages/deviceDetailDelete.test.js +78 -0
- package/src/tests/react/pages/receitaFederalIntegrationContract.test.mjs +48 -0
- package/src/tests/react/utils/bottomNavigationToolbar.test.js +222 -0
- package/src/tests/react/utils/copyDeviceConfigs.test.mjs +127 -0
- package/src/tests/react/utils/fileUrl.test.js +38 -0
- package/src/tests/react/utils/importStatus.test.js +26 -9
- package/src/tests/react/utils/passwordPolicy.test.js +27 -0
- package/src/tests/react/utils/printRouting.test.mjs +86 -0
- package/src/tests/react/utils/printSpoolUtils.test.mjs +52 -0
- package/src/tests/react/utils/runtimeFooter.test.js +7 -0
- package/src/tests/react/utils/shopFranchises.test.js +121 -115
- 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
|
+
}
|