@controleonline/ui-common 1.2.71 → 1.2.76

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 (150) hide show
  1. package/.github/actions/workers/manager/action.yml +147 -0
  2. package/.github/actions/workers/qa/action.yml +44 -0
  3. package/.github/actions/workers/security/action.yml +44 -0
  4. package/.github/actions/workers/technical-documenter/action.yml +51 -0
  5. package/.github/workflows/manager-worker.yml +168 -0
  6. package/.github/workflows/publish-npm.yml +45 -0
  7. package/.gitmodules +3 -0
  8. package/AGENTS.md +6 -0
  9. package/README.md +14 -0
  10. package/package.json +3 -2
  11. package/src/api/fetch.js +1 -1
  12. package/src/api/index.js +92 -22
  13. package/src/react/components/AddImportModal.js +94 -110
  14. package/src/react/components/BottomNavigationBar.config.js +211 -231
  15. package/src/react/components/CategoryForm.js +52 -35
  16. package/src/react/components/DefaultProvider.native.js +131 -95
  17. package/src/react/components/DefaultProvider.web.js +148 -74
  18. package/src/react/components/NetworkPrinterPreviewModal.js +249 -0
  19. package/src/react/components/NetworkPrinterPreviewModal.styles.js +159 -0
  20. package/src/react/components/RuntimeInfoFooter.js +164 -104
  21. package/src/react/components/RuntimeInfoFooter.styles.js +3 -0
  22. package/src/react/components/UnifiedPaymentBar.js +7 -4
  23. package/src/react/components/UserAvatar.js +160 -0
  24. package/src/react/components/integrations/IntegrationHero/index.js +48 -0
  25. package/src/react/components/integrations/IntegrationHero/styles.js +57 -0
  26. package/src/react/components/integrations/IntegrationTabs/index.js +89 -0
  27. package/src/react/components/integrations/IntegrationTabs/styles.js +44 -0
  28. package/src/react/config/deviceConfigBootstrap.js +5 -3
  29. package/src/react/pages/Connections.js +229 -0
  30. package/src/react/pages/Connections.styles.js +222 -0
  31. package/src/react/pages/DeviceDetailPage.js +2865 -0
  32. package/src/react/pages/DeviceDetailPage.styles.js +308 -0
  33. package/src/react/pages/Devices/currentDevice.js +194 -0
  34. package/src/react/pages/Devices/deviceTypes/DeviceGroupCard.js +257 -0
  35. package/src/react/pages/Devices/deviceTypes/all.js +23 -0
  36. package/src/react/pages/Devices/deviceTypes/device.js +25 -0
  37. package/src/react/pages/Devices/deviceTypes/deviceListHelpers.js +178 -0
  38. package/src/react/pages/Devices/deviceTypes/display.js +26 -0
  39. package/src/react/pages/Devices/deviceTypes/index.js +145 -0
  40. package/src/react/pages/Devices/deviceTypes/ipCamera.js +35 -0
  41. package/src/react/pages/Devices/deviceTypes/pdv.js +88 -0
  42. package/src/react/pages/Devices/deviceTypes/printer.js +37 -0
  43. package/src/react/pages/Devices/deviceTypes/shared.js +453 -0
  44. package/src/react/pages/Devices/deviceTypes/useDeviceNetworkConnectivity.js +145 -0
  45. package/src/react/pages/Devices.js +183 -0
  46. package/src/react/pages/Devices.styles.js +353 -0
  47. package/src/react/pages/Food99IntegrationPage/components/Food99CatalogTab.js +219 -0
  48. package/src/react/pages/Food99IntegrationPage/components/Food99OverviewTab.js +134 -0
  49. package/src/react/pages/Food99IntegrationPage/components/Food99PreviewModal.js +173 -0
  50. package/src/react/pages/Food99IntegrationPage/components/Food99ProductCard.js +1 -0
  51. package/src/react/pages/Food99IntegrationPage/components/Food99QuickWalletModal.js +181 -0
  52. package/src/react/pages/Food99IntegrationPage/components/Food99SettingsTab.js +306 -0
  53. package/src/react/pages/Food99IntegrationPage/components/Food99StoreTab.js +417 -0
  54. package/src/react/pages/Food99IntegrationPage/index.js +1530 -0
  55. package/src/react/pages/Food99IntegrationPage/styles.js +792 -0
  56. package/src/react/pages/Food99IntegrationPage/utils.js +155 -0
  57. package/src/react/pages/Food99IntegrationPage.js +1 -0
  58. package/src/react/pages/Food99IntegrationPage.styles.js +1 -0
  59. package/src/react/pages/GenericLogPage.js +42 -167
  60. package/src/react/pages/IFoodIntegrationPage/components/IFoodCatalogTab.js +8 -0
  61. package/src/react/pages/IFoodIntegrationPage/components/IFoodOperationsTab.js +420 -0
  62. package/src/react/pages/IFoodIntegrationPage/components/IFoodOverviewTab.js +118 -0
  63. package/src/react/pages/IFoodIntegrationPage/components/IFoodPreviewModal.js +103 -0
  64. package/src/react/pages/IFoodIntegrationPage/components/IFoodProductCard.js +1 -0
  65. package/src/react/pages/IFoodIntegrationPage/components/IFoodStoreTab.js +150 -0
  66. package/src/react/pages/IFoodIntegrationPage/index.js +1298 -0
  67. package/src/react/pages/IFoodIntegrationPage/styles.js +764 -0
  68. package/src/react/pages/IFoodIntegrationPage/utils.js +91 -0
  69. package/src/react/pages/IFoodIntegrationPage.js +1 -0
  70. package/src/react/pages/IFoodIntegrationPage.styles.js +1 -0
  71. package/src/react/pages/Imports.js +214 -166
  72. package/src/react/pages/IntegrationConfigPage.js +744 -0
  73. package/src/react/pages/IntegrationConfigPage.styles.js +269 -0
  74. package/src/react/pages/Integrations.js +299 -0
  75. package/src/react/pages/Integrations.styles.js +130 -0
  76. package/src/react/pages/ManagerOrderNotificationsPage.js +642 -0
  77. package/src/react/pages/MarketplaceIntegrationPage/components/MarketplaceProductCard.js +205 -0
  78. package/src/react/pages/MarketplaceIntegrationPage/components/RemoteMarketplaceMenuTab.js +335 -0
  79. package/src/react/pages/MarketplaceIntegrationPage/index.js +60 -0
  80. package/src/react/pages/MarketplaceIntegrationPage/utils.js +137 -0
  81. package/src/react/pages/MercadoLivreIntegrationPage.js +538 -0
  82. package/src/react/pages/PrinterDeviceDetailPage.js +1916 -0
  83. package/src/react/pages/PrinterDeviceDetailPage.styles.js +223 -0
  84. package/src/react/pages/PrinterDeviceFormPage.js +521 -0
  85. package/src/react/pages/PrinterDeviceFormPage.styles.js +159 -0
  86. package/src/react/pages/WhatsAppConnectionPage.js +459 -0
  87. package/src/react/pages/WhatsAppConnectionPage.styles.js +284 -0
  88. package/src/react/pages/integrationsCatalog.js +399 -0
  89. package/src/react/services/Cielo/Cielo.js +193 -193
  90. package/src/react/utils/categoryContexts.js +46 -0
  91. package/src/react/utils/entityDisplay.js +222 -217
  92. package/src/react/utils/fileUrl.js +39 -20
  93. package/src/react/utils/integrationPage.js +16 -0
  94. package/src/react/utils/runtimeFooter.js +40 -22
  95. package/src/react/utils/runtimeLanguage.js +50 -9
  96. package/src/react/utils/runtimeMenu.js +191 -131
  97. package/src/react/utils/runtimeZoom.js +43 -0
  98. package/src/react/utils/shopConfig.js +27 -15
  99. package/src/react/utils/shopFranchises.js +126 -1
  100. package/src/react/utils/storeColumns.js +23 -4
  101. package/src/react/utils/userAvatar.js +104 -0
  102. package/src/store/categories/index.js +48 -20
  103. package/src/store/device_config/index.js +62 -21
  104. package/src/store/entity_log/index.js +22 -1
  105. package/src/store/imports/customActions.js +51 -0
  106. package/src/store/imports/index.js +89 -88
  107. package/src/store/status/index.js +11 -11
  108. package/src/store/themes/index.js +22 -0
  109. package/src/store/timezones/index.js +3 -1
  110. package/src/tests/browser/manager/connections.spec.js +143 -0
  111. package/src/tests/browser/manager/devices-current.spec.js +343 -0
  112. package/src/tests/browser/manager/mercado-livre-integration.spec.js +268 -0
  113. package/src/tests/react/api/deviceHeaders.test.js +114 -0
  114. package/src/tests/react/components/AddImportModal.test.js +166 -0
  115. package/src/tests/react/components/BottomNavigationBar.config.test.js +20 -0
  116. package/src/tests/react/components/UserAvatar.test.js +52 -0
  117. package/src/tests/react/pages/DevicePickerStyles.test.js +47 -0
  118. package/src/tests/react/pages/Devices/deviceListHelpers.test.js +39 -0
  119. package/src/tests/react/pages/DevicesCurrentDevice.test.js +198 -0
  120. package/src/tests/react/pages/connectionsHelpers.test.js +34 -0
  121. package/src/tests/react/pages/mercadoLivreIntegrationPageContract.test.mjs +16 -0
  122. package/src/tests/react/services/Cielo.test.js +190 -190
  123. package/src/tests/react/store/categoriesOrdering.test.js +26 -0
  124. package/src/tests/react/utils/appDomain.test.js +62 -0
  125. package/src/tests/react/utils/entityDisplay.test.js +2 -2
  126. package/src/tests/react/utils/fileUrl.test.js +3 -3
  127. package/src/tests/react/utils/formatter.test.mjs +33 -0
  128. package/src/tests/react/utils/runtimeFooter.test.js +29 -4
  129. package/src/tests/react/utils/runtimeLanguage.test.js +79 -9
  130. package/src/tests/react/utils/runtimeMenu.test.js +198 -134
  131. package/src/tests/react/utils/runtimeZoom.test.js +38 -0
  132. package/src/tests/react/utils/shopConfig.test.js +10 -9
  133. package/src/tests/react/utils/shopFranchises.test.js +43 -0
  134. package/src/tests/react/utils/translate.discovery.test.mjs +170 -0
  135. package/src/tests/react/utils/translate.test.helpers.mjs +152 -0
  136. package/src/tests/react/utils/translate.test.mjs +141 -349
  137. package/src/tests/react/utils/userAvatar.test.js +63 -0
  138. package/src/utils/appDomain.js +15 -4
  139. package/src/utils/formatter.js +104 -16
  140. package/src/utils/integrationConfigs.js +80 -80
  141. package/src/utils/translate.js +222 -438
  142. package/src/utils/translateDiscovery.js +96 -0
  143. package/src/utils/translateNormalize.js +33 -0
  144. package/src/utils/translatePending.js +92 -0
  145. package/src/utils/translateStorage.js +142 -0
  146. package/.github/agents/developer.agent.md +0 -30
  147. package/.github/agents/devops.agent.md +0 -30
  148. package/.github/agents/qa.agent.md +0 -30
  149. package/.github/agents/security.agent.md +0 -30
  150. package/.github/workflows/npmjs.yml +0 -35
@@ -0,0 +1,521 @@
1
+ import React, {useCallback, useMemo, useState} from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ Alert,
5
+ Platform,
6
+ ScrollView,
7
+ Text,
8
+ TextInput,
9
+ TouchableOpacity,
10
+ View,
11
+ } from 'react-native';
12
+ import {SafeAreaView} from 'react-native-safe-area-context';
13
+ import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native';
14
+ import {Picker} from '@react-native-picker/picker';
15
+ import Icon from 'react-native-vector-icons/Feather';
16
+ import {useStore} from '@store';
17
+ import StateStore from '@controleonline/ui-common/src/react/components/StateStore';
18
+ import {resolveThemePalette, withOpacity} from '@controleonline/../../src/styles/branding';
19
+ import {colors} from '@controleonline/../../src/styles/colors';
20
+ import {getNetworkDeviceProfile} from '@controleonline/ui-common/src/react/utils/networkDeviceProfiles';
21
+ import {
22
+ buildNetworkCameraConfigs,
23
+ buildNetworkCameraMetadata,
24
+ DEFAULT_NETWORK_CAMERA_MANUFACTURER,
25
+ DEFAULT_NETWORK_CAMERA_MODEL,
26
+ DEFAULT_NETWORK_CAMERA_PORT,
27
+ DEFAULT_NETWORK_CAMERA_PROTOCOL,
28
+ NETWORK_CAMERA_PROTOCOL_OPTIONS,
29
+ } from '@controleonline/ui-common/src/react/utils/networkCameraDevices';
30
+ import {
31
+ buildNetworkPrinterMetadata,
32
+ DEFAULT_NETWORK_PRINTER_COLUMNS,
33
+ DEFAULT_NETWORK_PRINTER_MANUFACTURER,
34
+ DEFAULT_NETWORK_PRINTER_MODEL,
35
+ DEFAULT_NETWORK_PRINTER_PORT,
36
+ DEFAULT_NETWORK_PRINTER_TRANSPORT,
37
+ IP_CAMERA_DEVICE_TYPE,
38
+ getPrinterManagerDeviceOptions,
39
+ NETWORK_PRINTER_COLUMNS_CONFIG_KEY,
40
+ NETWORK_PRINTER_MANAGER_DEVICE_CONFIG_KEY,
41
+ NETWORK_PRINTER_PORT_CONFIG_KEY,
42
+ NETWORK_PRINTER_TRANSPORT_CONFIG_KEY,
43
+ normalizePrinterColumns,
44
+ normalizePrinterHost,
45
+ normalizePrinterPort,
46
+ PRINT_DEVICE_TYPE,
47
+ } from '@controleonline/ui-common/src/react/utils/printerDevices';
48
+ import styles from './PrinterDeviceFormPage.styles';
49
+
50
+ const PrinterDeviceFormPage = () => {
51
+ const navigation = useNavigation();
52
+ const route = useRoute();
53
+ const normalizedDeviceType =
54
+ String(route.params?.deviceType || '').trim().toUpperCase() ||
55
+ PRINT_DEVICE_TYPE;
56
+ const isIpCamera = normalizedDeviceType === IP_CAMERA_DEVICE_TYPE;
57
+ const deviceProfile = useMemo(
58
+ () => getNetworkDeviceProfile(normalizedDeviceType),
59
+ [normalizedDeviceType],
60
+ );
61
+
62
+ const peopleStore = useStore('people');
63
+ const deviceStore = useStore('device');
64
+ const deviceConfigStore = useStore('device_config');
65
+ const themeStore = useStore('theme');
66
+
67
+ const {currentCompany} = peopleStore.getters;
68
+ const {items: companyDeviceConfigs = [], isLoading: isLoadingDeviceConfigs} =
69
+ deviceConfigStore.getters;
70
+ const {colors: themeColors} = themeStore.getters;
71
+
72
+ const brandColors = useMemo(
73
+ () =>
74
+ resolveThemePalette(
75
+ {...themeColors, ...(currentCompany?.theme?.colors || {})},
76
+ colors,
77
+ ),
78
+ [themeColors, currentCompany?.id],
79
+ );
80
+
81
+ const [alias, setAlias] = useState('');
82
+ const [host, setHost] = useState('');
83
+ const [port, setPort] = useState(
84
+ isIpCamera ? DEFAULT_NETWORK_CAMERA_PORT : DEFAULT_NETWORK_PRINTER_PORT,
85
+ );
86
+ const [columns, setColumns] = useState(DEFAULT_NETWORK_PRINTER_COLUMNS);
87
+ const [manufacturer, setManufacturer] = useState(
88
+ isIpCamera
89
+ ? DEFAULT_NETWORK_CAMERA_MANUFACTURER
90
+ : DEFAULT_NETWORK_PRINTER_MANUFACTURER,
91
+ );
92
+ const [model, setModel] = useState(
93
+ isIpCamera ? DEFAULT_NETWORK_CAMERA_MODEL : DEFAULT_NETWORK_PRINTER_MODEL,
94
+ );
95
+ const [version, setVersion] = useState('');
96
+ const [protocol, setProtocol] = useState(DEFAULT_NETWORK_CAMERA_PROTOCOL);
97
+ const [streamPath, setStreamPath] = useState('');
98
+ const [username, setUsername] = useState('');
99
+ const [password, setPassword] = useState('');
100
+ const [managerDeviceId, setManagerDeviceId] = useState('');
101
+ const [saving, setSaving] = useState(false);
102
+
103
+ const pickerMode = Platform.OS === 'android' ? 'dropdown' : undefined;
104
+
105
+ const managerDeviceOptions = useMemo(
106
+ () =>
107
+ getPrinterManagerDeviceOptions({
108
+ deviceConfigs: companyDeviceConfigs,
109
+ companyId: currentCompany?.id,
110
+ }),
111
+ [companyDeviceConfigs, currentCompany?.id],
112
+ );
113
+
114
+ useFocusEffect(
115
+ useCallback(() => {
116
+ if (!currentCompany?.id) {
117
+ return;
118
+ }
119
+
120
+ deviceConfigStore.actions
121
+ .getItems({people: `/people/${currentCompany.id}`})
122
+ .catch(() => {});
123
+ }, [currentCompany?.id, deviceConfigStore.actions]),
124
+ );
125
+
126
+ const handleSave = useCallback(async () => {
127
+ if (!currentCompany?.id) {
128
+ Alert.alert(
129
+ 'Empresa nao selecionada',
130
+ deviceProfile.companyMissingMessage,
131
+ );
132
+ return;
133
+ }
134
+
135
+ const normalizedHost = normalizePrinterHost(host);
136
+ if (!normalizedHost) {
137
+ Alert.alert(
138
+ deviceProfile.hostAlertTitle,
139
+ deviceProfile.hostMissingMessage,
140
+ );
141
+ return;
142
+ }
143
+
144
+ if (!managerDeviceId) {
145
+ Alert.alert(
146
+ 'Device responsavel',
147
+ deviceProfile.managerMissingMessage,
148
+ );
149
+ return;
150
+ }
151
+
152
+ const normalizedAlias =
153
+ String(alias || '').trim() ||
154
+ [manufacturer, model].map(item => String(item || '').trim()).filter(Boolean).join(' ') ||
155
+ normalizedHost;
156
+ const metadata = isIpCamera
157
+ ? buildNetworkCameraMetadata({
158
+ host: normalizedHost,
159
+ manufacturer,
160
+ model,
161
+ version,
162
+ })
163
+ : buildNetworkPrinterMetadata({
164
+ host: normalizedHost,
165
+ manufacturer,
166
+ model,
167
+ version,
168
+ transport: DEFAULT_NETWORK_PRINTER_TRANSPORT,
169
+ });
170
+
171
+ setSaving(true);
172
+
173
+ try {
174
+ const savedDevice = await deviceStore.actions.save({
175
+ alias: normalizedAlias,
176
+ device: normalizedHost,
177
+ metadata,
178
+ });
179
+
180
+ const configs = isIpCamera
181
+ ? buildNetworkCameraConfigs({
182
+ managerDeviceId,
183
+ port,
184
+ protocol,
185
+ streamPath,
186
+ username,
187
+ password,
188
+ })
189
+ : {
190
+ [NETWORK_PRINTER_MANAGER_DEVICE_CONFIG_KEY]: managerDeviceId,
191
+ [NETWORK_PRINTER_PORT_CONFIG_KEY]: normalizePrinterPort(port),
192
+ [NETWORK_PRINTER_COLUMNS_CONFIG_KEY]:
193
+ normalizePrinterColumns(columns),
194
+ [NETWORK_PRINTER_TRANSPORT_CONFIG_KEY]:
195
+ DEFAULT_NETWORK_PRINTER_TRANSPORT,
196
+ };
197
+
198
+ const savedDeviceConfig = await deviceConfigStore.actions.addDeviceConfigs({
199
+ device: savedDevice?.device || normalizedHost,
200
+ people: `/people/${currentCompany.id}`,
201
+ type: normalizedDeviceType,
202
+ configs: JSON.stringify(configs),
203
+ });
204
+
205
+ navigation.replace(deviceProfile.detailRouteName, {
206
+ dcId: savedDeviceConfig?.id,
207
+ deviceId: savedDevice?.id,
208
+ deviceString: savedDevice?.device || normalizedHost,
209
+ deviceType: normalizedDeviceType,
210
+ alias: savedDevice?.alias || normalizedAlias,
211
+ configs,
212
+ metadata: savedDevice?.metadata || metadata,
213
+ });
214
+ } catch (error) {
215
+ const message =
216
+ error?.response?.data?.['hydra:description'] ||
217
+ error?.response?.data?.message ||
218
+ error?.message ||
219
+ deviceProfile.registerErrorMessage;
220
+
221
+ Alert.alert(deviceProfile.registerErrorTitle, message);
222
+ } finally {
223
+ setSaving(false);
224
+ }
225
+ }, [
226
+ alias,
227
+ columns,
228
+ currentCompany?.id,
229
+ deviceConfigStore.actions,
230
+ deviceStore.actions,
231
+ host,
232
+ managerDeviceId,
233
+ manufacturer,
234
+ model,
235
+ navigation,
236
+ normalizedDeviceType,
237
+ isIpCamera,
238
+ password,
239
+ port,
240
+ protocol,
241
+ version,
242
+ streamPath,
243
+ username,
244
+ deviceProfile,
245
+ ]);
246
+
247
+ return (
248
+ <SafeAreaView
249
+ style={[styles.container, {backgroundColor: brandColors.background}]}>
250
+ <StateStore stores={['device', 'device_config', 'people']} />
251
+
252
+ <ScrollView
253
+ contentContainerStyle={styles.scroll}
254
+ showsVerticalScrollIndicator={false}>
255
+ <View style={styles.heroCard}>
256
+ <View
257
+ style={[
258
+ styles.heroIcon,
259
+ {backgroundColor: withOpacity(brandColors.primary, 0.12)},
260
+ ]}>
261
+ <Icon name={deviceProfile.icon} size={20} color={brandColors.primary} />
262
+ </View>
263
+ <View style={styles.heroCopy}>
264
+ <Text style={styles.heroTitle}>{deviceProfile.heroTitle}</Text>
265
+ <Text style={styles.heroText}>
266
+ {deviceProfile.heroText}
267
+ </Text>
268
+ </View>
269
+ </View>
270
+
271
+ <View style={styles.noticeCard}>
272
+ <Icon name="info" size={14} color="#B45309" />
273
+ <Text style={styles.noticeText}>
274
+ A descoberta automatica na rede ainda nao esta disponivel nesta
275
+ tela. O cadastro inicial e manual.
276
+ </Text>
277
+ </View>
278
+
279
+ <View style={styles.sectionCard}>
280
+ <Text style={styles.sectionTitle}>Identificacao</Text>
281
+
282
+ <View style={styles.fieldBlock}>
283
+ <Text style={styles.fieldLabel}>Nome exibido</Text>
284
+ <TextInput
285
+ style={styles.input}
286
+ value={alias}
287
+ onChangeText={setAlias}
288
+ placeholder={deviceProfile.aliasPlaceholder}
289
+ placeholderTextColor="#94A3B8"
290
+ />
291
+ </View>
292
+
293
+ <View style={styles.fieldBlock}>
294
+ <Text style={styles.fieldLabel}>IP ou hostname</Text>
295
+ <TextInput
296
+ style={styles.input}
297
+ value={host}
298
+ onChangeText={setHost}
299
+ placeholder="192.168.0.120"
300
+ placeholderTextColor="#94A3B8"
301
+ autoCapitalize="none"
302
+ autoCorrect={false}
303
+ />
304
+ </View>
305
+ </View>
306
+
307
+ <View style={styles.sectionCard}>
308
+ <Text style={styles.sectionTitle}>Device responsavel</Text>
309
+ <Text style={styles.sectionDescription}>
310
+ {deviceProfile.managerSectionDescription}
311
+ </Text>
312
+
313
+ {isLoadingDeviceConfigs ? (
314
+ <View style={styles.loadingRow}>
315
+ <ActivityIndicator size="small" color={brandColors.primary} />
316
+ <Text style={styles.loadingText}>Carregando devices da empresa...</Text>
317
+ </View>
318
+ ) : (
319
+ <View style={styles.pickerWrap}>
320
+ <Picker
321
+ selectedValue={managerDeviceId || ''}
322
+ mode={pickerMode}
323
+ onValueChange={value => setManagerDeviceId(String(value || '').trim())}>
324
+ <Picker.Item
325
+ label="Selecione um PDV ou DISPLAY"
326
+ value=""
327
+ />
328
+ {managerDeviceOptions.map(option => (
329
+ <Picker.Item
330
+ key={option.deviceId}
331
+ label={option.label}
332
+ value={option.deviceId}
333
+ />
334
+ ))}
335
+ </Picker>
336
+ </View>
337
+ )}
338
+ </View>
339
+
340
+ <View style={styles.sectionCard}>
341
+ <Text style={styles.sectionTitle}>
342
+ {isIpCamera ? 'Acesso da camera' : 'Configuracao inicial'}
343
+ </Text>
344
+
345
+ <View style={styles.inlineFields}>
346
+ <View style={[styles.fieldBlock, styles.inlineField]}>
347
+ <Text style={styles.fieldLabel}>
348
+ {isIpCamera ? 'Porta' : 'Porta TCP'}
349
+ </Text>
350
+ <TextInput
351
+ style={styles.input}
352
+ value={port}
353
+ onChangeText={setPort}
354
+ placeholder={
355
+ isIpCamera
356
+ ? DEFAULT_NETWORK_CAMERA_PORT
357
+ : DEFAULT_NETWORK_PRINTER_PORT
358
+ }
359
+ placeholderTextColor="#94A3B8"
360
+ keyboardType="numeric"
361
+ />
362
+ </View>
363
+
364
+ {isIpCamera ? (
365
+ <View style={[styles.fieldBlock, styles.inlineField]}>
366
+ <Text style={styles.fieldLabel}>Protocolo</Text>
367
+ <View style={styles.pickerWrap}>
368
+ <Picker
369
+ selectedValue={protocol || DEFAULT_NETWORK_CAMERA_PROTOCOL}
370
+ mode={pickerMode}
371
+ onValueChange={value =>
372
+ setProtocol(String(value || DEFAULT_NETWORK_CAMERA_PROTOCOL))
373
+ }>
374
+ {NETWORK_CAMERA_PROTOCOL_OPTIONS.map(option => (
375
+ <Picker.Item
376
+ key={option.value}
377
+ label={option.label}
378
+ value={option.value}
379
+ />
380
+ ))}
381
+ </Picker>
382
+ </View>
383
+ </View>
384
+ ) : (
385
+ <View style={[styles.fieldBlock, styles.inlineField]}>
386
+ <Text style={styles.fieldLabel}>Colunas</Text>
387
+ <TextInput
388
+ style={styles.input}
389
+ value={columns}
390
+ onChangeText={setColumns}
391
+ placeholder={DEFAULT_NETWORK_PRINTER_COLUMNS}
392
+ placeholderTextColor="#94A3B8"
393
+ keyboardType="numeric"
394
+ />
395
+ </View>
396
+ )}
397
+ </View>
398
+
399
+ {isIpCamera && (
400
+ <>
401
+ <View style={styles.fieldBlock}>
402
+ <Text style={styles.fieldLabel}>Caminho do stream</Text>
403
+ <TextInput
404
+ style={styles.input}
405
+ value={streamPath}
406
+ onChangeText={setStreamPath}
407
+ placeholder="Ex.: /Streaming/Channels/101"
408
+ placeholderTextColor="#94A3B8"
409
+ autoCapitalize="none"
410
+ autoCorrect={false}
411
+ />
412
+ </View>
413
+
414
+ <View style={styles.inlineFields}>
415
+ <View style={[styles.fieldBlock, styles.inlineField]}>
416
+ <Text style={styles.fieldLabel}>Usuario</Text>
417
+ <TextInput
418
+ style={styles.input}
419
+ value={username}
420
+ onChangeText={setUsername}
421
+ placeholder="Ex.: admin"
422
+ placeholderTextColor="#94A3B8"
423
+ autoCapitalize="none"
424
+ autoCorrect={false}
425
+ />
426
+ </View>
427
+
428
+ <View style={[styles.fieldBlock, styles.inlineField]}>
429
+ <Text style={styles.fieldLabel}>Senha</Text>
430
+ <TextInput
431
+ style={styles.input}
432
+ value={password}
433
+ onChangeText={setPassword}
434
+ placeholder="Senha da camera"
435
+ placeholderTextColor="#94A3B8"
436
+ autoCapitalize="none"
437
+ autoCorrect={false}
438
+ secureTextEntry
439
+ />
440
+ </View>
441
+ </View>
442
+ </>
443
+ )}
444
+
445
+ <View style={styles.fieldBlock}>
446
+ <Text style={styles.fieldLabel}>Fabricante</Text>
447
+ <TextInput
448
+ style={styles.input}
449
+ value={manufacturer}
450
+ onChangeText={setManufacturer}
451
+ placeholder={
452
+ isIpCamera
453
+ ? DEFAULT_NETWORK_CAMERA_MANUFACTURER
454
+ : DEFAULT_NETWORK_PRINTER_MANUFACTURER
455
+ }
456
+ placeholderTextColor="#94A3B8"
457
+ />
458
+ </View>
459
+
460
+ <View style={styles.fieldBlock}>
461
+ <Text style={styles.fieldLabel}>Modelo</Text>
462
+ <TextInput
463
+ style={styles.input}
464
+ value={model}
465
+ onChangeText={setModel}
466
+ placeholder={
467
+ isIpCamera
468
+ ? DEFAULT_NETWORK_CAMERA_MODEL
469
+ : DEFAULT_NETWORK_PRINTER_MODEL
470
+ }
471
+ placeholderTextColor="#94A3B8"
472
+ />
473
+ </View>
474
+
475
+ <View style={styles.fieldBlock}>
476
+ <Text style={styles.fieldLabel}>Versao/Firmware</Text>
477
+ <TextInput
478
+ style={styles.input}
479
+ value={version}
480
+ onChangeText={setVersion}
481
+ placeholder="Ex.: 1.0.0"
482
+ placeholderTextColor="#94A3B8"
483
+ />
484
+ </View>
485
+
486
+ {!isIpCamera && (
487
+ <View style={styles.transportRow}>
488
+ <Icon name="wifi" size={14} color="#0F172A" />
489
+ <Text style={styles.transportText}>
490
+ Transporte previsto: TCP RAW ({DEFAULT_NETWORK_PRINTER_PORT})
491
+ </Text>
492
+ </View>
493
+ )}
494
+ </View>
495
+
496
+ <TouchableOpacity
497
+ style={[
498
+ styles.primaryButton,
499
+ {backgroundColor: brandColors.primary},
500
+ saving && styles.primaryButtonDisabled,
501
+ ]}
502
+ activeOpacity={0.85}
503
+ disabled={saving}
504
+ onPress={handleSave}>
505
+ {saving ? (
506
+ <ActivityIndicator size="small" color="#fff" />
507
+ ) : (
508
+ <>
509
+ <Icon name="save" size={15} color="#fff" />
510
+ <Text style={styles.primaryButtonText}>
511
+ {deviceProfile.createButtonLabel}
512
+ </Text>
513
+ </>
514
+ )}
515
+ </TouchableOpacity>
516
+ </ScrollView>
517
+ </SafeAreaView>
518
+ );
519
+ };
520
+
521
+ export default PrinterDeviceFormPage;
@@ -0,0 +1,159 @@
1
+ import {Platform, StyleSheet} from 'react-native';
2
+
3
+ const cardShadow = Platform.select({
4
+ ios: {
5
+ shadowColor: '#0F172A',
6
+ shadowOffset: {width: 0, height: 4},
7
+ shadowOpacity: 0.06,
8
+ shadowRadius: 12,
9
+ },
10
+ android: {elevation: 2},
11
+ web: {boxShadow: '0 4px 12px rgba(15,23,42,0.06)'},
12
+ });
13
+
14
+ const styles = StyleSheet.create({
15
+ container: {
16
+ flex: 1,
17
+ paddingHorizontal: 16,
18
+ paddingTop: 14,
19
+ },
20
+ scroll: {
21
+ paddingBottom: 28,
22
+ gap: 12,
23
+ },
24
+ heroCard: {
25
+ backgroundColor: '#fff',
26
+ borderRadius: 18,
27
+ padding: 16,
28
+ flexDirection: 'row',
29
+ alignItems: 'flex-start',
30
+ gap: 14,
31
+ ...cardShadow,
32
+ },
33
+ heroIcon: {
34
+ width: 42,
35
+ height: 42,
36
+ borderRadius: 12,
37
+ alignItems: 'center',
38
+ justifyContent: 'center',
39
+ },
40
+ heroCopy: {
41
+ flex: 1,
42
+ gap: 4,
43
+ },
44
+ heroTitle: {
45
+ fontSize: 17,
46
+ fontWeight: '800',
47
+ color: '#0F172A',
48
+ },
49
+ heroText: {
50
+ fontSize: 13,
51
+ lineHeight: 19,
52
+ color: '#475569',
53
+ },
54
+ noticeCard: {
55
+ backgroundColor: '#FEF3C7',
56
+ borderRadius: 14,
57
+ paddingHorizontal: 14,
58
+ paddingVertical: 12,
59
+ flexDirection: 'row',
60
+ alignItems: 'flex-start',
61
+ gap: 10,
62
+ },
63
+ noticeText: {
64
+ flex: 1,
65
+ fontSize: 12,
66
+ lineHeight: 18,
67
+ color: '#92400E',
68
+ fontWeight: '600',
69
+ },
70
+ sectionCard: {
71
+ backgroundColor: '#fff',
72
+ borderRadius: 18,
73
+ padding: 16,
74
+ gap: 12,
75
+ ...cardShadow,
76
+ },
77
+ sectionTitle: {
78
+ fontSize: 15,
79
+ fontWeight: '800',
80
+ color: '#0F172A',
81
+ },
82
+ sectionDescription: {
83
+ fontSize: 12,
84
+ lineHeight: 18,
85
+ color: '#64748B',
86
+ },
87
+ fieldBlock: {
88
+ gap: 6,
89
+ },
90
+ fieldLabel: {
91
+ fontSize: 12,
92
+ fontWeight: '700',
93
+ color: '#475569',
94
+ },
95
+ input: {
96
+ borderWidth: 1,
97
+ borderColor: '#E2E8F0',
98
+ borderRadius: 12,
99
+ paddingHorizontal: 12,
100
+ paddingVertical: 11,
101
+ backgroundColor: '#F8FAFC',
102
+ color: '#0F172A',
103
+ fontSize: 14,
104
+ },
105
+ inlineFields: {
106
+ flexDirection: 'row',
107
+ gap: 10,
108
+ },
109
+ inlineField: {
110
+ flex: 1,
111
+ },
112
+ pickerWrap: {
113
+ borderWidth: 1,
114
+ borderColor: '#E2E8F0',
115
+ borderRadius: 12,
116
+ overflow: 'hidden',
117
+ backgroundColor: '#F8FAFC',
118
+ },
119
+ loadingRow: {
120
+ flexDirection: 'row',
121
+ alignItems: 'center',
122
+ gap: 8,
123
+ },
124
+ loadingText: {
125
+ fontSize: 12,
126
+ color: '#64748B',
127
+ fontWeight: '600',
128
+ },
129
+ transportRow: {
130
+ flexDirection: 'row',
131
+ alignItems: 'center',
132
+ gap: 8,
133
+ paddingTop: 2,
134
+ },
135
+ transportText: {
136
+ fontSize: 12,
137
+ color: '#0F172A',
138
+ fontWeight: '600',
139
+ },
140
+ primaryButton: {
141
+ minHeight: 48,
142
+ borderRadius: 14,
143
+ alignItems: 'center',
144
+ justifyContent: 'center',
145
+ flexDirection: 'row',
146
+ gap: 8,
147
+ ...cardShadow,
148
+ },
149
+ primaryButtonDisabled: {
150
+ opacity: 0.65,
151
+ },
152
+ primaryButtonText: {
153
+ fontSize: 14,
154
+ fontWeight: '800',
155
+ color: '#fff',
156
+ },
157
+ });
158
+
159
+ export default styles;