@controleonline/ui-common 1.2.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/.github/agents/developer.agent.md +30 -0
  2. package/.github/agents/devops.agent.md +30 -0
  3. package/.github/agents/qa.agent.md +30 -0
  4. package/.github/agents/security.agent.md +30 -0
  5. package/.github/workflows/npmjs.yml +35 -0
  6. package/.github/workflows/pull-request-checks.yml +19 -0
  7. package/.scrutinizer.yml +24 -0
  8. package/FUNDING.yml +1 -0
  9. package/package.json +28 -0
  10. package/src/api/fetch.js +74 -0
  11. package/src/api/index.js +155 -0
  12. package/src/api/localDB.js +131 -0
  13. package/src/api/queue.js +66 -0
  14. package/src/react/assets/tap.mp3 +0 -0
  15. package/src/react/components/AddImportModal.js +157 -0
  16. package/src/react/components/AddImportModal.styles.js +81 -0
  17. package/src/react/components/BackgroundRuntimeBridge.js +219 -0
  18. package/src/react/components/BottomNavigationBar.config.js +238 -0
  19. package/src/react/components/BottomNavigationBar.js +120 -0
  20. package/src/react/components/BottomNavigationBar.styles.js +110 -0
  21. package/src/react/components/CategoryForm.js +572 -0
  22. package/src/react/components/CategoryForm.styles.js +334 -0
  23. package/src/react/components/ConfirmModal.js +24 -0
  24. package/src/react/components/ConfirmModal.styles.js +43 -0
  25. package/src/react/components/ContextHelpButton.js +69 -0
  26. package/src/react/components/ContextHelpButton.styles.js +62 -0
  27. package/src/react/components/DefaultProvider.native.js +848 -0
  28. package/src/react/components/DefaultProvider.styles.js +21 -0
  29. package/src/react/components/DefaultProvider.web.js +909 -0
  30. package/src/react/components/DeliveryPushBridge.native.js +314 -0
  31. package/src/react/components/DeliveryPushBridge.web.js +5 -0
  32. package/src/react/components/DeviceAlertSoundService.js +499 -0
  33. package/src/react/components/EntityLogContent.js +909 -0
  34. package/src/react/components/EntityLogContent.styles.js +400 -0
  35. package/src/react/components/EventBus.js +12 -0
  36. package/src/react/components/KioskModeBridge.js +65 -0
  37. package/src/react/components/LauncherModeBridge.js +47 -0
  38. package/src/react/components/LinkedOrderProductsTab.js +388 -0
  39. package/src/react/components/LinkedOrderProductsTab.styles.js +180 -0
  40. package/src/react/components/ManagerPushBridge.native.js +345 -0
  41. package/src/react/components/MessageService.js +314 -0
  42. package/src/react/components/Paywall/Google.js +89 -0
  43. package/src/react/components/PrintService.js +778 -0
  44. package/src/react/components/ProductCatalogCacheService.js +45 -0
  45. package/src/react/components/RemoteCheckoutService.js +292 -0
  46. package/src/react/components/RuntimeBottomNavigationBar.js +151 -0
  47. package/src/react/components/RuntimeInfoFooter.js +214 -0
  48. package/src/react/components/RuntimeInfoFooter.styles.js +56 -0
  49. package/src/react/components/SystemErrorToast.js +29 -0
  50. package/src/react/components/SystemErrorToast.styles.js +38 -0
  51. package/src/react/components/TouchFeedbackProvider.js +65 -0
  52. package/src/react/components/UnifiedPaymentBar.js +155 -0
  53. package/src/react/components/UnifiedPaymentBar.styles.js +174 -0
  54. package/src/react/components/WebsocketListener.native.js +487 -0
  55. package/src/react/components/WebsocketListener.web.js +102 -0
  56. package/src/react/components/inputs/IdInput.js +49 -0
  57. package/src/react/components/inputs/IdInput.styles.js +26 -0
  58. package/src/react/components/inputs/InvoiceDueDateFilterInput.js +74 -0
  59. package/src/react/components/inputs/InvoiceDueDateFilterInput.styles.js +34 -0
  60. package/src/react/components/inputs/InvoiceDueDateRangeFilterInput.js +115 -0
  61. package/src/react/components/inputs/InvoiceDueDateRangeFilterInput.styles.js +39 -0
  62. package/src/react/components/lists/CategoriesList.js +56 -0
  63. package/src/react/components/lists/PaymentTypeList.js +55 -0
  64. package/src/react/components/lists/ReceiverList.js +56 -0
  65. package/src/react/components/lists/SelectList.styles.js +18 -0
  66. package/src/react/components/lists/StatusList.js +55 -0
  67. package/src/react/components/lists/WalletList.js +54 -0
  68. package/src/react/components/radio/Spotify.js +144 -0
  69. package/src/react/components/radio/Spotify.styles.js +32 -0
  70. package/src/react/components/radio/YouTube.js +82 -0
  71. package/src/react/components/radio/YouTube.styles.js +16 -0
  72. package/src/react/components/toastConfig.js +16 -0
  73. package/src/react/config/deviceConfigBootstrap.js +571 -0
  74. package/src/react/index.js +4 -0
  75. package/src/react/localStorage.js +30 -0
  76. package/src/react/pages/EntityLogPage.js +124 -0
  77. package/src/react/pages/EntityLogPage.styles.js +17 -0
  78. package/src/react/pages/GenericLogPage.js +760 -0
  79. package/src/react/pages/GenericLogPage.styles.js +401 -0
  80. package/src/react/pages/Imports.js +361 -0
  81. package/src/react/pages/Imports.styles.js +223 -0
  82. package/src/react/pages/SettingsPage/PaymentTypesByWalletTab.js +462 -0
  83. package/src/react/pages/SettingsPage/index.js +1171 -0
  84. package/src/react/pages/SettingsPage/index.styles.js +122 -0
  85. package/src/react/print/jobs.js +147 -0
  86. package/src/react/print/providers/local.js +53 -0
  87. package/src/react/print/providers/remote.js +81 -0
  88. package/src/react/print/selection.js +296 -0
  89. package/src/react/print/usePrintButtonController.js +474 -0
  90. package/src/react/router/routes.js +42 -0
  91. package/src/react/services/NetworkPrinterService.native.js +391 -0
  92. package/src/react/services/NetworkPrinterService.web.js +15 -0
  93. package/src/react/stores/index.js +128 -0
  94. package/src/react/stores/storeErrorBridge.js +36 -0
  95. package/src/react/utils/backgroundRuntimeRegistration.js +69 -0
  96. package/src/react/utils/cashPayment.js +87 -0
  97. package/src/react/utils/commercialDocumentOrders.js +269 -0
  98. package/src/react/utils/dateRangeFilter.js +264 -0
  99. package/src/react/utils/deviceRuntime.js +193 -0
  100. package/src/react/utils/entityDisplay.js +231 -0
  101. package/src/react/utils/entityLog.js +649 -0
  102. package/src/react/utils/fileUrl.js +102 -0
  103. package/src/react/utils/frontendDebugLog.js +45 -0
  104. package/src/react/utils/googleMapsConfig.js +49 -0
  105. package/src/react/utils/importStatus.js +100 -0
  106. package/src/react/utils/invoiceDueDateMessages.js +14 -0
  107. package/src/react/utils/invoicePresentation.js +46 -0
  108. package/src/react/utils/logSettings.js +143 -0
  109. package/src/react/utils/maintenanceSettings.js +53 -0
  110. package/src/react/utils/managerOrderNotifications.js +523 -0
  111. package/src/react/utils/mapNavigation.js +76 -0
  112. package/src/react/utils/menuCatalogConfig.js +8 -0
  113. package/src/react/utils/menuCatalogDownload.js +157 -0
  114. package/src/react/utils/networkCameraDevices.js +175 -0
  115. package/src/react/utils/networkDeviceProfiles.js +135 -0
  116. package/src/react/utils/normalizedCatalogDownload.js +157 -0
  117. package/src/react/utils/notificationNavigation.js +94 -0
  118. package/src/react/utils/notificationSound.js +42 -0
  119. package/src/react/utils/paymentDevices.js +397 -0
  120. package/src/react/utils/paymentGatewayExecution.js +91 -0
  121. package/src/react/utils/paymentOptions.js +88 -0
  122. package/src/react/utils/peopleDisplay.js +39 -0
  123. package/src/react/utils/printerDevices.js +465 -0
  124. package/src/react/utils/remotePayment.js +61 -0
  125. package/src/react/utils/runtimeFooter.js +475 -0
  126. package/src/react/utils/runtimeLanguage.js +50 -0
  127. package/src/react/utils/runtimeMenu.js +224 -0
  128. package/src/react/utils/screenMetrics.js +30 -0
  129. package/src/react/utils/shopConfig.js +294 -0
  130. package/src/react/utils/shopFranchises.js +114 -0
  131. package/src/react/utils/socketRuntimePipeline.js +454 -0
  132. package/src/react/utils/spoolAckTracker.js +77 -0
  133. package/src/react/utils/storeColumns.js +427 -0
  134. package/src/react/utils/systemErrorChannel.js +109 -0
  135. package/src/react/utils/systemErrorMessage.js +67 -0
  136. package/src/react/utils/toastPresentation.js +53 -0
  137. package/src/react/utils/websocketSharedRuntime.js +826 -0
  138. package/src/store/acl/getters.js +4 -0
  139. package/src/store/acl/index.js +19 -0
  140. package/src/store/acl/mutation_types.js +4 -0
  141. package/src/store/acl/mutations.js +17 -0
  142. package/src/store/address/city.js +22 -0
  143. package/src/store/categories/category_file/index.js +25 -0
  144. package/src/store/categories/index.js +93 -0
  145. package/src/store/configs/customActions.js +131 -0
  146. package/src/store/configs/index.js +25 -0
  147. package/src/store/connections/customActions.js +26 -0
  148. package/src/store/connections/index.js +124 -0
  149. package/src/store/device/index.js +22 -0
  150. package/src/store/device_config/customActions.js +190 -0
  151. package/src/store/device_config/index.js +28 -0
  152. package/src/store/entity_log/customActions.js +63 -0
  153. package/src/store/entity_log/index.js +25 -0
  154. package/src/store/extra/data.js +85 -0
  155. package/src/store/extra/fields.js +88 -0
  156. package/src/store/file/actions.js +27 -0
  157. package/src/store/file/index.js +24 -0
  158. package/src/store/imports/index.js +88 -0
  159. package/src/store/print/customActions.js +251 -0
  160. package/src/store/print/index.js +28 -0
  161. package/src/store/printer/customActions.js +54 -0
  162. package/src/store/printer/index.js +26 -0
  163. package/src/store/runtime_debug/customActions.js +71 -0
  164. package/src/store/runtime_debug/index.js +30 -0
  165. package/src/store/status/index.js +65 -0
  166. package/src/store/timezones/index.js +43 -0
  167. package/src/store/websocket/customActions.js +24 -0
  168. package/src/store/websocket/index.js +28 -0
  169. package/src/tests/react/components/LauncherModeBridge.test.js +93 -0
  170. package/src/tests/react/config/deviceConfigBootstrap.test.js +337 -0
  171. package/src/tests/react/print/localPrintProvider.test.js +45 -0
  172. package/src/tests/react/utils/backgroundRuntimeRegistration.test.js +123 -0
  173. package/src/tests/react/utils/cashPayment.test.js +41 -0
  174. package/src/tests/react/utils/dateRangeFilter.test.mjs +29 -0
  175. package/src/tests/react/utils/entityDisplay.test.js +18 -0
  176. package/src/tests/react/utils/googleMapsConfig.test.js +32 -0
  177. package/src/tests/react/utils/importStatus.test.js +85 -0
  178. package/src/tests/react/utils/invoicePresentation.test.mjs +40 -0
  179. package/src/tests/react/utils/managerOrderNotifications.test.js +127 -0
  180. package/src/tests/react/utils/mapNavigation.test.js +58 -0
  181. package/src/tests/react/utils/notificationNavigation.test.js +39 -0
  182. package/src/tests/react/utils/notificationSound.test.js +27 -0
  183. package/src/tests/react/utils/paymentDevices.test.js +88 -0
  184. package/src/tests/react/utils/runtimeFooter.test.js +429 -0
  185. package/src/tests/react/utils/runtimeLanguage.test.js +80 -0
  186. package/src/tests/react/utils/runtimeMenu.test.js +174 -0
  187. package/src/tests/react/utils/shopFranchises.test.js +82 -0
  188. package/src/tests/react/utils/socketRuntimePipeline.test.js +124 -0
  189. package/src/tests/react/utils/spoolAckTracker.test.js +40 -0
  190. package/src/tests/react/utils/storeErrorBridge.test.js +66 -0
  191. package/src/tests/react/utils/systemErrorChannel.test.js +58 -0
  192. package/src/tests/react/utils/systemErrorMessage.test.js +59 -0
  193. package/src/tests/react/utils/toastPresentation.test.js +75 -0
  194. package/src/tests/react/utils/translate.test.mjs +396 -0
  195. package/src/tests/react/utils/websocketSharedRuntime.test.js +245 -0
  196. package/src/utils/apiEntryPoint.js +31 -0
  197. package/src/utils/appDomain.js +50 -0
  198. package/src/utils/config.js +14 -0
  199. package/src/utils/formatter.js +419 -0
  200. package/src/utils/integrationConfigs.js +79 -0
  201. package/src/utils/methods.js +23 -0
  202. package/src/utils/oauth.js +42 -0
  203. package/src/utils/translate.js +620 -0
@@ -0,0 +1,361 @@
1
+ import React, { useCallback, useState, useEffect, useLayoutEffect, useMemo } from 'react';
2
+ import {
3
+ Text,
4
+ View,
5
+ TouchableOpacity,
6
+ FlatList,
7
+ TextInput,
8
+ RefreshControl,
9
+ } from 'react-native';
10
+ import { useNavigation, useFocusEffect } from '@react-navigation/native';
11
+ import { useStore } from '@store';
12
+ import { api } from '@controleonline/ui-common/src/api';
13
+ import { colors } from '@controleonline/../../src/styles/colors';
14
+ import Icon from 'react-native-vector-icons/FontAwesome';
15
+ import IconAdd from 'react-native-vector-icons/MaterialIcons';
16
+ import AddImportModal from '../components/AddImportModal';
17
+ import { Directory, File } from 'expo-file-system';
18
+ import * as Sharing from 'expo-sharing';
19
+ import styles from './Imports.styles';
20
+ const {
21
+ getImportErrorDetail,
22
+ getImportStatusMeta,
23
+ resolveImportLabels,
24
+ resolveImportStatusLabel: resolveImportStatusLabelValue,
25
+ } = require('@controleonline/ui-common/src/react/utils/importStatus');
26
+
27
+ const formatDate = (dateString) => {
28
+ const d = new Date(dateString);
29
+ return d.toLocaleString(); // Formato local legível
30
+ };
31
+
32
+ const Imports = ({ context = {}, onClose }) => {
33
+ const peopleStore = useStore('people');
34
+ const { getters: peopleGetters } = peopleStore.getters;
35
+ const { currentCompany } = peopleGetters;
36
+
37
+ const importType = context.context;
38
+ const title = context.title;
39
+ const searchPlaceholder = context.searchPlaceholder;
40
+ const {
41
+ refreshLabel,
42
+ processingLabel,
43
+ errorLabel,
44
+ doneLabel,
45
+ noStatusLabel,
46
+ processingHelpLabel,
47
+ } = resolveImportLabels(global.t?.t);
48
+
49
+ const navigation = useNavigation();
50
+
51
+ const importsStore = useStore('imports');
52
+ const getters = importsStore.getters;
53
+ const actions = importsStore.actions;
54
+
55
+ const { items, isLoading } = getters;
56
+
57
+ const [currentPage, setCurrentPage] = useState(1);
58
+
59
+ const [searchText, setSearchText] = useState('');
60
+ const [searchQuery, setSearchQuery] = useState('');
61
+
62
+ const [allImports, setAllImports] = useState([]);
63
+
64
+ const [refreshing, setRefreshing] = useState(false);
65
+ const [showAddImportModal, setShowAddImportModal] = useState(false);
66
+
67
+ const fetchImports = useCallback(
68
+ (query, page) => {
69
+ const params = {
70
+ people: currentCompany ? '/people/' + currentCompany.id : null,
71
+ importType: importType,
72
+ page: page ?? currentPage,
73
+ };
74
+
75
+ if (String(query ?? searchQuery).trim()) {
76
+ params.name = String(query ?? searchQuery).trim();
77
+ }
78
+
79
+ return actions.getItems(params);
80
+ },
81
+ [actions, currentCompany, currentPage, searchQuery, importType],
82
+ );
83
+
84
+ useLayoutEffect(() => {
85
+ navigation.setOptions({
86
+ headerTitle: title,
87
+ });
88
+ }, [navigation, title]);
89
+
90
+ useFocusEffect(
91
+ useCallback(() => {
92
+ fetchImports(searchQuery, currentPage);
93
+ }, [currentPage, fetchImports, searchQuery]),
94
+ );
95
+
96
+ useEffect(() => {
97
+ if (isLoading) return;
98
+ if (items && Array.isArray(items)) {
99
+ if (currentPage === 1) {
100
+ setAllImports(items);
101
+ } else {
102
+ setAllImports((prev) => {
103
+ const newIds = new Set(items.map((i) => i.id));
104
+ const filteredPrev = prev.filter((p) => !newIds.has(p.id));
105
+ return [...filteredPrev, ...items];
106
+ });
107
+ }
108
+ }
109
+ }, [items, currentPage, isLoading]);
110
+
111
+ useEffect(() => {
112
+ const t = setTimeout(() => {
113
+ setSearchQuery(searchText.trim());
114
+ setCurrentPage(1);
115
+ }, 300);
116
+ return () => clearTimeout(t);
117
+ }, [searchText]);
118
+
119
+ const hasProcessingImports = useMemo(
120
+ () => allImports.some(item => getImportStatusMeta(item).isProcessing),
121
+ [allImports],
122
+ );
123
+
124
+ const resolveImportStatusLabel = useCallback(
125
+ statusMeta => {
126
+ return resolveImportStatusLabelValue(statusMeta, {
127
+ processingLabel,
128
+ errorLabel,
129
+ doneLabel,
130
+ noStatusLabel,
131
+ });
132
+ },
133
+ [doneLabel, errorLabel, noStatusLabel, processingLabel],
134
+ );
135
+
136
+ useEffect(() => {
137
+ if (!hasProcessingImports) {
138
+ return undefined;
139
+ }
140
+
141
+ const intervalId = setInterval(() => {
142
+ setCurrentPage(1);
143
+ fetchImports(searchQuery, 1);
144
+ }, 15000);
145
+
146
+ return () => clearInterval(intervalId);
147
+ }, [fetchImports, hasProcessingImports, searchQuery]);
148
+
149
+ const onRefresh = useCallback(async () => {
150
+ setRefreshing(true);
151
+ try {
152
+ setCurrentPage(1);
153
+ await fetchImports(searchQuery, 1);
154
+ } finally {
155
+ setRefreshing(false);
156
+ }
157
+ }, [fetchImports, searchQuery]);
158
+
159
+ const renderImportCard = ({ item }) => {
160
+ const statusMeta = getImportStatusMeta(item);
161
+ const errorDetail = statusMeta.isError ? getImportErrorDetail(item) : '';
162
+
163
+ return (
164
+ <TouchableOpacity style={styles.card} activeOpacity={0.8}>
165
+ <View style={styles.cardHeader}>
166
+ <View style={styles.avatar}>
167
+ <Text style={styles.avatarText}>
168
+ {item.name?.charAt(0)?.toUpperCase() || 'I'}
169
+ </Text>
170
+ </View>
171
+
172
+ <Text style={styles.title} numberOfLines={1}>
173
+ {item.name} (ID: {item.id})
174
+ </Text>
175
+
176
+ <Icon name="chevron-right" size={14} color="#CBD5E1" />
177
+ </View>
178
+
179
+ <View style={styles.cardBody}>
180
+ <View style={styles.infoRow}>
181
+ <Icon name="file-text-o" size={16} color={colors.primary} />
182
+ <Text style={styles.infoText}>{item.fileFormat?.toUpperCase()}</Text>
183
+ </View>
184
+
185
+ <View style={styles.infoRow}>
186
+ <Icon name="calendar" size={16} color={colors.primary} />
187
+ <Text style={styles.infoText}>{formatDate(item.uploadDate)}</Text>
188
+ </View>
189
+
190
+ {(item.status?.status || statusMeta.rawStatus || statusMeta.labelKey) && (
191
+ <View style={styles.infoRow}>
192
+ <Icon name="info-circle" size={16} color={colors.primary} />
193
+ <View style={styles.statusContent}>
194
+ <View
195
+ style={[
196
+ styles.statusBadge,
197
+ {
198
+ backgroundColor: statusMeta.backgroundColor,
199
+ },
200
+ ]}>
201
+ <Text
202
+ style={[
203
+ styles.statusBadgeText,
204
+ { color: statusMeta.textColor },
205
+ ]}>
206
+ {resolveImportStatusLabel(statusMeta)}
207
+ </Text>
208
+ </View>
209
+
210
+ {statusMeta.isProcessing && (
211
+ <Text style={styles.statusHelperText}>
212
+ {processingHelpLabel}
213
+ </Text>
214
+ )}
215
+ </View>
216
+ </View>
217
+ )}
218
+
219
+ {errorDetail ? (
220
+ <View style={styles.infoRow}>
221
+ <Icon name="exclamation-circle" size={16} color="#DC2626" />
222
+ <Text style={styles.errorText}>{errorDetail}</Text>
223
+ </View>
224
+ ) : null}
225
+ </View>
226
+ </TouchableOpacity>
227
+ );
228
+ };
229
+
230
+ const downloadTemplate = async () => {
231
+ try {
232
+ const response = await api.fetch(`/imports/example/${importType}`, {
233
+ method: 'GET',
234
+ responseType: 'text',
235
+ });
236
+
237
+ const csvText = typeof response === 'string' ? response : await response.text?.();
238
+ console.log('CSV recebido:', csvText.slice(0, 200));
239
+
240
+ if (!csvText) throw new Error('CSV vazio');
241
+
242
+ // mobile: verifica se o diretório está disponível
243
+ const dirUri = Directory?.cacheDocumentDirectory || Directory?.documentDirectory;
244
+
245
+ if (dirUri) {
246
+ const fileUri = `${dirUri}modelo_${importType}.csv`;
247
+
248
+ const file = new File(fileUri, { contents: csvText });
249
+ await file.create();
250
+
251
+ if (await Sharing.isAvailableAsync()) {
252
+ await Sharing.shareAsync(fileUri);
253
+ }
254
+
255
+ console.log(`Arquivo CSV criado em: ${fileUri}`);
256
+ return fileUri;
257
+ } else if (typeof window !== 'undefined') {
258
+ // fallback web
259
+ const blob = new Blob([csvText], { type: 'text/csv' });
260
+ const link = document.createElement('a');
261
+ link.href = URL.createObjectURL(blob);
262
+ link.download = `modelo_${importType}.csv`;
263
+ link.click();
264
+ URL.revokeObjectURL(link.href);
265
+ console.log('Download CSV disparado no web');
266
+ return 'web-download';
267
+ } else {
268
+ throw new Error('FileSystem não disponível');
269
+ }
270
+ } catch (err) {
271
+ console.error('Erro detalhado ao baixar CSV:', err);
272
+ throw new Error('Falha ao baixar o modelo CSV');
273
+ }
274
+ };
275
+
276
+ return (
277
+ <View style={styles.container}>
278
+
279
+ <View style={styles.headerBar}>
280
+ <Text style={styles.headerTitle}>{title}</Text>
281
+ <TouchableOpacity onPress={onClose}>
282
+ <Icon name="close" size={24} color="#000" />
283
+ </TouchableOpacity>
284
+ </View>
285
+
286
+ <View style={styles.subHeader}>
287
+ <View style={styles.topRow}>
288
+ <View style={styles.topRowActions}>
289
+ <TouchableOpacity style={styles.downloadButton} onPress={downloadTemplate}>
290
+ <IconAdd name="download" size={20} color="#fff" />
291
+ <Text style={styles.downloadText}>Baixar Modelo</Text>
292
+ </TouchableOpacity>
293
+
294
+ <TouchableOpacity style={styles.refreshButton} onPress={onRefresh}>
295
+ <Icon name="refresh" size={16} color={colors.primary} />
296
+ <Text style={styles.refreshButtonText}>{refreshLabel}</Text>
297
+ </TouchableOpacity>
298
+ </View>
299
+
300
+ {hasProcessingImports ? (
301
+ <View style={styles.processingBadge}>
302
+ <Icon name="clock-o" size={14} color="#B45309" />
303
+ <Text style={styles.processingBadgeText}>{processingLabel}</Text>
304
+ </View>
305
+ ) : null}
306
+ </View>
307
+
308
+ <View style={styles.searchRow}>
309
+ <View style={styles.searchInputContainer}>
310
+ <Icon name="search" size={16} color="#94A3B8" />
311
+
312
+ <TextInput
313
+ style={styles.searchInput}
314
+ placeholder={searchPlaceholder}
315
+ placeholderTextColor="#94A3B8"
316
+ value={searchText}
317
+ onChangeText={setSearchText}
318
+ />
319
+
320
+ {searchText.length > 0 && (
321
+ <TouchableOpacity
322
+ onPress={() => setSearchText('')}
323
+ style={styles.clearSearchButton}
324
+ >
325
+ <Icon name="times-circle" size={16} color="#94A3B8" />
326
+ </TouchableOpacity>
327
+ )}
328
+ </View>
329
+
330
+ <TouchableOpacity
331
+ style={styles.addButton}
332
+ onPress={() => setShowAddImportModal(true)}
333
+ >
334
+ <IconAdd name="upload-file" size={24} color="#FFFFFF" />
335
+ </TouchableOpacity>
336
+ </View>
337
+ </View>
338
+
339
+ <FlatList
340
+ data={allImports}
341
+ keyExtractor={(item) => String(item.id)}
342
+ renderItem={renderImportCard}
343
+ contentContainerStyle={styles.scrollContent}
344
+ refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
345
+ />
346
+
347
+ <AddImportModal
348
+ visible={showAddImportModal}
349
+ onClose={() => setShowAddImportModal(false)}
350
+ context={context}
351
+ onSuccess={() => {
352
+ setCurrentPage(1);
353
+ fetchImports(searchQuery, 1);
354
+ }}
355
+ />
356
+ </View>
357
+ );
358
+ };
359
+
360
+ export default Imports;
361
+ // TODO(store-first): quando este arquivo for mexido, mover a leitura para stores, remover api.fetch e evitar repassar dados em objetos quando o store ja resolver isso.
@@ -0,0 +1,223 @@
1
+ import { Platform, StyleSheet } from 'react-native'
2
+ import { colors } from '@controleonline/../../src/styles/colors'
3
+
4
+ const styles = StyleSheet.create({
5
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
6
+ headerBar: {
7
+ flexDirection: 'row',
8
+ justifyContent: 'space-between',
9
+ alignItems: 'center',
10
+ padding: 16,
11
+ backgroundColor: '#fff',
12
+ borderBottomWidth: 1,
13
+ borderBottomColor: '#eee',
14
+ },
15
+ headerTitle: {
16
+ fontSize: 18,
17
+ fontWeight: '700',
18
+ },
19
+
20
+ subHeader: {
21
+ paddingHorizontal: 16,
22
+ paddingTop: 9,
23
+ paddingBottom: 9,
24
+ backgroundColor: colors.white,
25
+ borderBottomWidth: 1,
26
+ borderBottomColor: colors.border,
27
+ },
28
+
29
+ topRow: {
30
+ marginBottom: 8,
31
+ },
32
+
33
+ topRowActions: {
34
+ flexDirection: 'row',
35
+ alignItems: 'center',
36
+ flexWrap: 'wrap',
37
+ },
38
+
39
+ downloadButton: {
40
+ backgroundColor: colors.primary,
41
+ paddingHorizontal: 12,
42
+ paddingVertical: 6,
43
+ borderRadius: 8,
44
+ flexDirection: 'row',
45
+ alignItems: 'center',
46
+ },
47
+
48
+ downloadText: { color: '#fff', fontWeight: '600', marginLeft: 6 },
49
+ refreshButton: {
50
+ marginLeft: 8,
51
+ paddingHorizontal: 12,
52
+ paddingVertical: 6,
53
+ borderRadius: 8,
54
+ flexDirection: 'row',
55
+ alignItems: 'center',
56
+ backgroundColor: '#EFF6FF',
57
+ borderWidth: 1,
58
+ borderColor: '#BFDBFE',
59
+ },
60
+ refreshButtonText: {
61
+ color: colors.primary,
62
+ fontWeight: '600',
63
+ marginLeft: 6,
64
+ },
65
+ processingBadge: {
66
+ alignSelf: 'flex-start',
67
+ flexDirection: 'row',
68
+ alignItems: 'center',
69
+ backgroundColor: '#FEF3C7',
70
+ borderRadius: 999,
71
+ paddingHorizontal: 10,
72
+ paddingVertical: 5,
73
+ marginTop: 10,
74
+ },
75
+ processingBadgeText: {
76
+ marginLeft: 6,
77
+ fontSize: 12,
78
+ fontWeight: '600',
79
+ color: '#B45309',
80
+ },
81
+ searchRow: { flexDirection: 'row', alignItems: 'center' },
82
+
83
+ searchInputContainer: {
84
+ flex: 1,
85
+ flexDirection: 'row',
86
+ alignItems: 'center',
87
+ backgroundColor: colors.background,
88
+ borderRadius: 8,
89
+ paddingHorizontal: 12,
90
+ borderWidth: 1,
91
+ borderColor: colors.border,
92
+ height: 40,
93
+ },
94
+
95
+ searchInput: {
96
+ flex: 1,
97
+ height: '100%',
98
+ paddingHorizontal: 8,
99
+ color: colors.text,
100
+ fontSize: 14,
101
+ outlineStyle: 'none',
102
+ outlineWidth: 0,
103
+ },
104
+
105
+ clearSearchButton: { padding: 4 },
106
+ addButton: {
107
+ backgroundColor: colors.primary,
108
+ width: 40,
109
+ height: 40,
110
+ borderRadius: 20,
111
+ alignItems: 'center',
112
+ justifyContent: 'center',
113
+ marginLeft: 12,
114
+ },
115
+
116
+ scrollContent: {
117
+ padding: 16,
118
+ paddingBottom: 32,
119
+ },
120
+
121
+ card: {
122
+ backgroundColor: '#fff',
123
+ borderRadius: 12,
124
+ padding: 16,
125
+ marginBottom: 12,
126
+ borderWidth: 1,
127
+ borderColor: '#F1F5F9',
128
+ ...Platform.select({
129
+ ios: {
130
+ shadowColor: '#64748B',
131
+ shadowOffset: { width: 0, height: 4 },
132
+ shadowOpacity: 0.1,
133
+ shadowRadius: 12,
134
+ },
135
+ android: { elevation: 4 },
136
+ web: { boxShadow: '0 8px 16px rgba(15, 23, 42, 0.1)' },
137
+ }),
138
+ },
139
+
140
+ cardHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 12 },
141
+ avatar: {
142
+ width: 48,
143
+ height: 48,
144
+ borderRadius: 16,
145
+ backgroundColor: colors.primary,
146
+ justifyContent: 'center',
147
+ alignItems: 'center',
148
+ marginRight: 12,
149
+ },
150
+ avatarText: { color: '#fff', fontSize: 20, fontWeight: '700' },
151
+ title: {
152
+ flex: 1,
153
+ fontWeight: '700',
154
+ fontSize: 16,
155
+ color: '#1E293B',
156
+ lineHeight: 24,
157
+ },
158
+ cardBody: { marginTop: 4 },
159
+ infoRow: {
160
+ flexDirection: 'row',
161
+ alignItems: 'center',
162
+ marginBottom: 8,
163
+ backgroundColor: '#F8FAFC',
164
+ padding: 8,
165
+ borderRadius: 8,
166
+ },
167
+ infoText: {
168
+ fontSize: 14,
169
+ color: '#475569',
170
+ marginLeft: 8,
171
+ fontWeight: '500',
172
+ flex: 1,
173
+ },
174
+ statusContent: {
175
+ flex: 1,
176
+ marginLeft: 8,
177
+ },
178
+ statusBadge: {
179
+ alignSelf: 'flex-start',
180
+ borderRadius: 999,
181
+ paddingHorizontal: 10,
182
+ paddingVertical: 4,
183
+ },
184
+ statusBadgeText: {
185
+ fontSize: 12,
186
+ fontWeight: '700',
187
+ },
188
+ statusHelperText: {
189
+ marginTop: 6,
190
+ fontSize: 12,
191
+ color: '#92400E',
192
+ },
193
+ errorText: {
194
+ fontSize: 13,
195
+ color: '#B91C1C',
196
+ marginLeft: 8,
197
+ fontWeight: '500',
198
+ flex: 1,
199
+ },
200
+ emptyContainer: {
201
+ flex: 1,
202
+ justifyContent: 'center',
203
+ alignItems: 'center',
204
+ paddingTop: 80,
205
+ },
206
+ emptyIcon: { marginBottom: 14 },
207
+ emptyTitle: {
208
+ fontSize: 18,
209
+ fontWeight: '600',
210
+ color: '#0F172A',
211
+ marginBottom: 6,
212
+ textAlign: 'center',
213
+ },
214
+ emptySubtitle: { fontSize: 14, color: '#94A3B8', textAlign: 'center' },
215
+ modalHeader: {
216
+ flexDirection: 'row',
217
+ justifyContent: 'space-between',
218
+ alignItems: 'center',
219
+ marginBottom: 16,
220
+ },
221
+ })
222
+
223
+ export default styles