@sincpro/mobile 0.1.0

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 (232) hide show
  1. package/README.md +74 -0
  2. package/dist/adapters/Bluetooth.adapter.d.ts +14 -0
  3. package/dist/adapters/Bluetooth.adapter.js +100 -0
  4. package/dist/adapters/Geo.adapter.d.ts +8 -0
  5. package/dist/adapters/Geo.adapter.js +30 -0
  6. package/dist/adapters/JsonSerializer.adapter.d.ts +28 -0
  7. package/dist/adapters/JsonSerializer.adapter.js +47 -0
  8. package/dist/adapters/Network.adapter.d.ts +4 -0
  9. package/dist/adapters/Network.adapter.js +13 -0
  10. package/dist/adapters/ReceiptExporter.adapter.d.ts +19 -0
  11. package/dist/adapters/ReceiptExporter.adapter.js +142 -0
  12. package/dist/adapters/repositories/database_table.repository.d.ts +13 -0
  13. package/dist/adapters/repositories/database_table.repository.js +29 -0
  14. package/dist/adapters/repositories/domain_event.repository.d.ts +32 -0
  15. package/dist/adapters/repositories/domain_event.repository.js +147 -0
  16. package/dist/adapters/repositories/domain_event_dead_letter.repository.d.ts +23 -0
  17. package/dist/adapters/repositories/domain_event_dead_letter.repository.js +81 -0
  18. package/dist/adapters/repositories/setting.repository.d.ts +10 -0
  19. package/dist/adapters/repositories/setting.repository.js +21 -0
  20. package/dist/adapters/webview.adapter.d.ts +10 -0
  21. package/dist/adapters/webview.adapter.js +166 -0
  22. package/dist/domain/connectivity/bluetooth.d.ts +19 -0
  23. package/dist/domain/connectivity/bluetooth.js +1 -0
  24. package/dist/domain/connectivity/events.d.ts +17 -0
  25. package/dist/domain/connectivity/events.js +17 -0
  26. package/dist/domain/connectivity/geo.d.ts +4 -0
  27. package/dist/domain/connectivity/geo.js +1 -0
  28. package/dist/domain/connectivity/index.d.ts +3 -0
  29. package/dist/domain/connectivity/index.js +3 -0
  30. package/dist/domain/connectivity/network.d.ts +7 -0
  31. package/dist/domain/connectivity/network.js +1 -0
  32. package/dist/domain/database/database.d.ts +16 -0
  33. package/dist/domain/database/database.js +1 -0
  34. package/dist/domain/database/index.d.ts +2 -0
  35. package/dist/domain/database/index.js +2 -0
  36. package/dist/domain/database/repository.d.ts +19 -0
  37. package/dist/domain/database/repository.js +1 -0
  38. package/dist/domain/entity/entity.d.ts +109 -0
  39. package/dist/domain/entity/entity.js +246 -0
  40. package/dist/domain/entity/entity_collection.d.ts +396 -0
  41. package/dist/domain/entity/entity_collection.js +824 -0
  42. package/dist/domain/entity/index.d.ts +3 -0
  43. package/dist/domain/entity/index.js +3 -0
  44. package/dist/domain/entity/value_object.d.ts +12 -0
  45. package/dist/domain/entity/value_object.js +39 -0
  46. package/dist/domain/event_sourcing/domain_event.d.ts +58 -0
  47. package/dist/domain/event_sourcing/domain_event.js +164 -0
  48. package/dist/domain/event_sourcing/event.d.ts +25 -0
  49. package/dist/domain/event_sourcing/event.js +25 -0
  50. package/dist/domain/event_sourcing/event_handler.d.ts +7 -0
  51. package/dist/domain/event_sourcing/event_handler.js +13 -0
  52. package/dist/domain/event_sourcing/index.d.ts +2 -0
  53. package/dist/domain/event_sourcing/index.js +2 -0
  54. package/dist/domain/events.d.ts +33 -0
  55. package/dist/domain/events.js +32 -0
  56. package/dist/domain/icon.d.ts +1 -0
  57. package/dist/domain/icon.js +1 -0
  58. package/dist/domain/index.d.ts +9 -0
  59. package/dist/domain/index.js +9 -0
  60. package/dist/domain/print/driver_registry.d.ts +4 -0
  61. package/dist/domain/print/driver_registry.js +29 -0
  62. package/dist/domain/print/events.d.ts +13 -0
  63. package/dist/domain/print/events.js +13 -0
  64. package/dist/domain/print/index.d.ts +2 -0
  65. package/dist/domain/print/index.js +1 -0
  66. package/dist/domain/print/printer.d.ts +35 -0
  67. package/dist/domain/print/printer.js +1 -0
  68. package/dist/domain/receipt.d.ts +31 -0
  69. package/dist/domain/receipt.js +1 -0
  70. package/dist/domain/repositories.d.ts +6 -0
  71. package/dist/domain/repositories.js +7 -0
  72. package/dist/domain/settings.d.ts +7 -0
  73. package/dist/domain/settings.js +1 -0
  74. package/dist/domain/webview/events.d.ts +11 -0
  75. package/dist/domain/webview/events.js +11 -0
  76. package/dist/domain/webview/index.d.ts +1 -0
  77. package/dist/domain/webview/index.js +1 -0
  78. package/dist/domain/webview/webview.d.ts +57 -0
  79. package/dist/domain/webview/webview.js +9 -0
  80. package/dist/entrypoints/cron/Cron.d.ts +13 -0
  81. package/dist/entrypoints/cron/Cron.js +57 -0
  82. package/dist/entrypoints/cron/checkNetworkStatus.cron.d.ts +3 -0
  83. package/dist/entrypoints/cron/checkNetworkStatus.cron.js +10 -0
  84. package/dist/entrypoints/db/index.d.ts +2 -0
  85. package/dist/entrypoints/db/index.js +2 -0
  86. package/dist/entrypoints/db/migrations.d.ts +10 -0
  87. package/dist/entrypoints/db/migrations.js +115 -0
  88. package/dist/entrypoints/db/repositories.d.ts +8 -0
  89. package/dist/entrypoints/db/repositories.js +34 -0
  90. package/dist/entrypoints/queue/QueueProcessor.d.ts +12 -0
  91. package/dist/entrypoints/queue/QueueProcessor.js +75 -0
  92. package/dist/entrypoints/queue/activateDomain.subscriber.d.ts +7 -0
  93. package/dist/entrypoints/queue/activateDomain.subscriber.js +15 -0
  94. package/dist/entrypoints/queue/newAppSettings.handler.d.ts +7 -0
  95. package/dist/entrypoints/queue/newAppSettings.handler.js +17 -0
  96. package/dist/entrypoints/queue/printImage.subscriber.d.ts +7 -0
  97. package/dist/entrypoints/queue/printImage.subscriber.js +14 -0
  98. package/dist/entrypoints/queue/processWebViewMessage.subscriber.d.ts +7 -0
  99. package/dist/entrypoints/queue/processWebViewMessage.subscriber.js +23 -0
  100. package/dist/entrypoints/ui/AppShell.d.ts +15 -0
  101. package/dist/entrypoints/ui/AppShell.js +58 -0
  102. package/dist/entrypoints/ui/common_provider.d.ts +35 -0
  103. package/dist/entrypoints/ui/common_provider.js +244 -0
  104. package/dist/entrypoints/ui/domain_switcher.d.ts +19 -0
  105. package/dist/entrypoints/ui/domain_switcher.js +22 -0
  106. package/dist/entrypoints/ui/theme.d.ts +15 -0
  107. package/dist/entrypoints/ui/theme.js +16 -0
  108. package/dist/exceptions.d.ts +22 -0
  109. package/dist/exceptions.js +60 -0
  110. package/dist/framework/base_module.d.ts +14 -0
  111. package/dist/framework/base_module.js +40 -0
  112. package/dist/framework/createApp.d.ts +6 -0
  113. package/dist/framework/createApp.js +9 -0
  114. package/dist/framework/domain_module.d.ts +13 -0
  115. package/dist/framework/domain_module.js +18 -0
  116. package/dist/framework/kernel.d.ts +18 -0
  117. package/dist/framework/kernel.js +60 -0
  118. package/dist/framework/orchestrator.d.ts +29 -0
  119. package/dist/framework/orchestrator.js +118 -0
  120. package/dist/index.d.ts +16 -0
  121. package/dist/index.js +11 -0
  122. package/dist/infrastructure/database/connector.d.ts +13 -0
  123. package/dist/infrastructure/database/connector.js +165 -0
  124. package/dist/infrastructure/database/index.d.ts +2 -0
  125. package/dist/infrastructure/database/index.js +2 -0
  126. package/dist/infrastructure/database/mapped.d.ts +128 -0
  127. package/dist/infrastructure/database/mapped.js +174 -0
  128. package/dist/infrastructure/database/utils.d.ts +10 -0
  129. package/dist/infrastructure/database/utils.js +35 -0
  130. package/dist/infrastructure/logger.d.ts +45 -0
  131. package/dist/infrastructure/logger.js +125 -0
  132. package/dist/infrastructure/ui/ToastHost.d.ts +1 -0
  133. package/dist/infrastructure/ui/ToastHost.js +19 -0
  134. package/dist/infrastructure/ui/UIEventBus.d.ts +12 -0
  135. package/dist/infrastructure/ui/UIEventBus.js +65 -0
  136. package/dist/infrastructure/ui/errorHandler.d.ts +1 -0
  137. package/dist/infrastructure/ui/errorHandler.js +20 -0
  138. package/dist/infrastructure/ui/events.d.ts +1 -0
  139. package/dist/infrastructure/ui/events.js +1 -0
  140. package/dist/infrastructure/workers/CronWorker.d.ts +42 -0
  141. package/dist/infrastructure/workers/CronWorker.js +143 -0
  142. package/dist/infrastructure/workers/EventBus.d.ts +67 -0
  143. package/dist/infrastructure/workers/EventBus.js +279 -0
  144. package/dist/infrastructure/workers/index.d.ts +2 -0
  145. package/dist/infrastructure/workers/index.js +2 -0
  146. package/dist/services/bluetooth.service.d.ts +14 -0
  147. package/dist/services/bluetooth.service.js +72 -0
  148. package/dist/services/database_table.service.d.ts +8 -0
  149. package/dist/services/database_table.service.js +19 -0
  150. package/dist/services/dead_letter_queue.service.d.ts +38 -0
  151. package/dist/services/dead_letter_queue.service.js +99 -0
  152. package/dist/services/event.service.d.ts +7 -0
  153. package/dist/services/event.service.js +24 -0
  154. package/dist/services/network.service.d.ts +7 -0
  155. package/dist/services/network.service.js +43 -0
  156. package/dist/services/printer.service.d.ts +25 -0
  157. package/dist/services/printer.service.js +220 -0
  158. package/dist/services/webview.service.d.ts +17 -0
  159. package/dist/services/webview.service.js +59 -0
  160. package/dist/tools/utils/Initials.d.ts +1 -0
  161. package/dist/tools/utils/Initials.js +12 -0
  162. package/dist/tools/utils/collections.d.ts +120 -0
  163. package/dist/tools/utils/collections.js +158 -0
  164. package/dist/tools/utils/date.d.ts +70 -0
  165. package/dist/tools/utils/date.js +126 -0
  166. package/dist/tools/utils/maps.d.ts +4 -0
  167. package/dist/tools/utils/maps.js +20 -0
  168. package/dist/tools/utils/monetary.d.ts +3 -0
  169. package/dist/tools/utils/monetary.js +31 -0
  170. package/dist/tools/utils/quantity.d.ts +11 -0
  171. package/dist/tools/utils/quantity.js +44 -0
  172. package/dist/tools/utils/searchTools.d.ts +39 -0
  173. package/dist/tools/utils/searchTools.js +56 -0
  174. package/dist/tools/utils/serializer.d.ts +2 -0
  175. package/dist/tools/utils/serializer.js +44 -0
  176. package/dist/ui/components/atoms/DebugBanner.d.ts +2 -0
  177. package/dist/ui/components/atoms/DebugBanner.js +15 -0
  178. package/dist/ui/components/atoms/index.d.ts +1 -0
  179. package/dist/ui/components/atoms/index.js +1 -0
  180. package/dist/ui/components/molecules/BluetoothDeviceSelectorModal.d.ts +8 -0
  181. package/dist/ui/components/molecules/BluetoothDeviceSelectorModal.js +61 -0
  182. package/dist/ui/components/molecules/DeadLetterErrorBlock.d.ts +8 -0
  183. package/dist/ui/components/molecules/DeadLetterErrorBlock.js +14 -0
  184. package/dist/ui/components/molecules/EventTimelineItem.d.ts +7 -0
  185. package/dist/ui/components/molecules/EventTimelineItem.js +65 -0
  186. package/dist/ui/components/molecules/ProcessToast.d.ts +5 -0
  187. package/dist/ui/components/molecules/ProcessToast.js +51 -0
  188. package/dist/ui/components/molecules/ProcessToastProvider.d.ts +4 -0
  189. package/dist/ui/components/molecules/ProcessToastProvider.js +5 -0
  190. package/dist/ui/components/molecules/index.d.ts +5 -0
  191. package/dist/ui/components/molecules/index.js +5 -0
  192. package/dist/ui/components/organisms/BluetoothPrinterSelector.d.ts +7 -0
  193. package/dist/ui/components/organisms/BluetoothPrinterSelector.js +92 -0
  194. package/dist/ui/components/organisms/DatabaseInfoRow.d.ts +11 -0
  195. package/dist/ui/components/organisms/DatabaseInfoRow.js +8 -0
  196. package/dist/ui/components/organisms/DeadLetterQueueRow.d.ts +7 -0
  197. package/dist/ui/components/organisms/DeadLetterQueueRow.js +72 -0
  198. package/dist/ui/components/organisms/EventRow.d.ts +7 -0
  199. package/dist/ui/components/organisms/EventRow.js +90 -0
  200. package/dist/ui/components/organisms/InjectableWebView.d.ts +35 -0
  201. package/dist/ui/components/organisms/InjectableWebView.js +169 -0
  202. package/dist/ui/components/organisms/Receipt.d.ts +11 -0
  203. package/dist/ui/components/organisms/Receipt.js +207 -0
  204. package/dist/ui/components/organisms/TableInfoInfoRow.d.ts +9 -0
  205. package/dist/ui/components/organisms/TableInfoInfoRow.js +19 -0
  206. package/dist/ui/components/organisms/index.d.ts +7 -0
  207. package/dist/ui/components/organisms/index.js +7 -0
  208. package/dist/ui/index.d.ts +4 -0
  209. package/dist/ui/index.js +4 -0
  210. package/dist/ui/layouts/router_layouts.d.ts +17 -0
  211. package/dist/ui/layouts/router_layouts.js +20 -0
  212. package/dist/ui/screens/database/database.context.d.ts +32 -0
  213. package/dist/ui/screens/database/database.context.js +158 -0
  214. package/dist/ui/screens/database/database.json_detail.d.ts +2 -0
  215. package/dist/ui/screens/database/database.json_detail.js +19 -0
  216. package/dist/ui/screens/database/database.list.d.ts +1 -0
  217. package/dist/ui/screens/database/database.list.js +41 -0
  218. package/dist/ui/screens/database/database.table_rows.d.ts +2 -0
  219. package/dist/ui/screens/database/database.table_rows.js +10 -0
  220. package/dist/ui/screens/dead_letter_queue/dead_letter_queue.context.d.ts +34 -0
  221. package/dist/ui/screens/dead_letter_queue/dead_letter_queue.context.js +166 -0
  222. package/dist/ui/screens/dead_letter_queue/dead_letter_queue.list.d.ts +2 -0
  223. package/dist/ui/screens/dead_letter_queue/dead_letter_queue.list.js +24 -0
  224. package/dist/ui/screens/events/events.context.d.ts +25 -0
  225. package/dist/ui/screens/events/events.context.js +113 -0
  226. package/dist/ui/screens/events/events.list.d.ts +1 -0
  227. package/dist/ui/screens/events/events.list.js +26 -0
  228. package/dist/ui/screens/events/index.d.ts +1 -0
  229. package/dist/ui/screens/events/index.js +1 -0
  230. package/dist/ui/screens/index.d.ts +3 -0
  231. package/dist/ui/screens/index.js +3 -0
  232. package/package.json +125 -0
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import { useCommon } from "../../../entrypoints/ui/common_provider";
3
+ import { Display } from "@sincpro/mobile-ui/Display";
4
+ import { Feedback } from "@sincpro/mobile-ui/Feedback";
5
+ import { theme } from "@sincpro/mobile-ui/theme";
6
+ import { cn } from "@sincpro/mobile-ui/theme/tw";
7
+ import { Typography } from "@sincpro/mobile-ui/Typography";
8
+ import { useEffect, useState } from "react";
9
+ import { Animated, Platform, TouchableOpacity, View } from "react-native";
10
+ const Icon = Display.Icon;
11
+ const Spinner = Feedback.Spinner;
12
+ const Text = Typography.Text;
13
+ function ProcessToast({ position = "top" }) {
14
+ const { currentActivity, lastError, isProcessing, dismiss } = useCommon();
15
+ const [slideAnim] = useState(new Animated.Value(-100));
16
+ const [showError, setShowError] = useState(false);
17
+ useEffect(() => {
18
+ if (lastError)
19
+ setShowError(true);
20
+ }, [lastError]);
21
+ useEffect(() => {
22
+ Animated.spring(slideAnim, {
23
+ toValue: isProcessing ? 0 : position === "top" ? -100 : 100,
24
+ tension: 300,
25
+ friction: 25,
26
+ useNativeDriver: true,
27
+ }).start();
28
+ }, [isProcessing, slideAnim, position]);
29
+ function renderToast() {
30
+ if (!isProcessing) {
31
+ return null;
32
+ }
33
+ const hasError = !!lastError;
34
+ const activity = lastError || currentActivity;
35
+ const label = activity?.label || "Sincronizando...";
36
+ function handleDismiss() {
37
+ setShowError(false);
38
+ dismiss();
39
+ }
40
+ return (_jsx(Animated.View, { className: "absolute left-4 right-4 z-50 self-center", style: [
41
+ position === "top"
42
+ ? { top: Platform.OS === "ios" ? 100 : 60 }
43
+ : { bottom: Platform.OS === "ios" ? 100 : 80 },
44
+ { transform: [{ translateY: slideAnim }] },
45
+ ], children: _jsxs(View, { className: cn("bg-white/[0.98] rounded-xl px-3.5 py-3 border-l-[3px] border-emerald-500 shadow-md", hasError && "bg-red-50/[0.98] border-red-500"), children: [_jsxs(View, { className: "flex-row items-center gap-3", children: [_jsx(View, { className: "w-5 h-5 justify-center items-center", children: hasError ? (_jsx(Icon, { color: theme.danger, name: "alert-circle", size: 16 })) : (_jsx(Spinner, { size: "small" })) }), _jsx(View, { className: "flex-1", children: _jsx(Text, { className: "text-gray-900 text-[13px] font-medium leading-4", variant: "captionSmall", children: hasError ? `Error: ${label}` : label }) }), _jsx(TouchableOpacity, { accessibilityLabel: "Cerrar notificación", className: "w-6 h-6 justify-center items-center rounded-full bg-gray-100", onPress: handleDismiss, children: _jsx(Icon, { color: theme.text.secondary, name: "close", size: 14 }) })] }), showError && lastError?.error && (_jsx(View, { className: "mt-2 pt-2 border-t border-gray-100", children: _jsx(Text, { className: "text-red-500 text-[11px] leading-[15px] font-mono", variant: "captionSmall", children: lastError.error.length > 150
46
+ ? `${lastError.error.substring(0, 150)}...`
47
+ : lastError.error }) }))] }) }));
48
+ }
49
+ return renderToast();
50
+ }
51
+ export default ProcessToast;
@@ -0,0 +1,4 @@
1
+ import type { ReactNode } from "react";
2
+ export declare function ProcessToastProvider({ children }: {
3
+ children: ReactNode;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import ProcessToast from "./ProcessToast";
3
+ export function ProcessToastProvider({ children }) {
4
+ return (_jsxs(_Fragment, { children: [children, _jsx(ProcessToast, { position: "top" })] }));
5
+ }
@@ -0,0 +1,5 @@
1
+ export { BluetoothDeviceSelectorModal } from "./BluetoothDeviceSelectorModal";
2
+ export { default as DeadLetterErrorBlock } from "./DeadLetterErrorBlock";
3
+ export { default as EventTimelineItem } from "./EventTimelineItem";
4
+ export { default as ProcessToast } from "./ProcessToast";
5
+ export { ProcessToastProvider } from "./ProcessToastProvider";
@@ -0,0 +1,5 @@
1
+ export { BluetoothDeviceSelectorModal } from "./BluetoothDeviceSelectorModal";
2
+ export { default as DeadLetterErrorBlock } from "./DeadLetterErrorBlock";
3
+ export { default as EventTimelineItem } from "./EventTimelineItem";
4
+ export { default as ProcessToast } from "./ProcessToast";
5
+ export { ProcessToastProvider } from "./ProcessToastProvider";
@@ -0,0 +1,7 @@
1
+ import { ISelectedPrinter } from "../../../domain/print";
2
+ interface BluetoothPrinterSelectorProps {
3
+ onPrinterSelected?: (printer: ISelectedPrinter) => void;
4
+ onConnectionChange?: (isConnected: boolean) => void;
5
+ }
6
+ declare function BluetoothPrinterSelector({ onPrinterSelected, onConnectionChange, }: BluetoothPrinterSelectorProps): import("react/jsx-runtime").JSX.Element;
7
+ export { BluetoothPrinterSelector };
@@ -0,0 +1,92 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import { bluetoothService } from "../../../services/bluetooth.service";
3
+ import { printerService } from "../../../services/printer.service";
4
+ import { BluetoothDeviceSelectorModal } from "../../../ui/components/molecules/BluetoothDeviceSelectorModal";
5
+ import { Display, Form } from "@sincpro/mobile-ui";
6
+ import PrinterIcon from "@sincpro/mobile-ui/icons/PrinterIcon";
7
+ import { Typography } from "@sincpro/mobile-ui/Typography";
8
+ import { useCallback, useEffect, useState } from "react";
9
+ import { View } from "react-native";
10
+ import { tv } from "tailwind-variants";
11
+ const statusTextVariants = tv({
12
+ variants: {
13
+ granted: {
14
+ true: "text-green-600",
15
+ false: "text-red-500",
16
+ },
17
+ },
18
+ });
19
+ function PermissionRequired({ loading, onRequestPermission, }) {
20
+ return (_jsxs(_Fragment, { children: [_jsx(Typography.Text, { className: statusTextVariants({ granted: false }), children: "Permiso de Bluetooth requerido" }), _jsx(Form.Button, { loading: loading, onPress: onRequestPermission, size: "small", title: "Solicitar Permiso" })] }));
21
+ }
22
+ function PrinterInfo({ printer, isConnected, loading, onConnect, onTestPrint, onChangePrinter, }) {
23
+ return (_jsxs(View, { className: "mt-2 bg-white rounded-lg p-3", children: [_jsx(Typography.Text, { className: "text-gray-700", semibold: true, children: printer.name }), _jsx(Typography.Text, { className: "text-gray-400 text-xs", children: printer.address }), _jsxs(View, { className: "flex-row gap-2 mt-2", children: [!isConnected ? (_jsx(Form.Button, { loading: loading, onPress: onConnect, size: "small", title: "Conectar", variant: "primary" })) : (_jsx(Form.Button, { loading: loading, onPress: onTestPrint, size: "small", title: "Imprimir Prueba", variant: "secondary" })), _jsx(Form.Button, { onPress: onChangePrinter, size: "small", title: "Cambiar", variant: "outline" })] }), isConnected && (_jsxs(View, { className: "flex-row items-center mt-2", children: [_jsx(View, { className: "w-2 h-2 rounded-full bg-green-500 mr-2" }), _jsx(Typography.Text, { className: "text-green-600 text-xs", children: "Conectado" })] }))] }));
24
+ }
25
+ function NoPrinterSelected({ loading, onSelectPrinter, }) {
26
+ return (_jsx(Form.Button, { loading: loading, onPress: onSelectPrinter, size: "small", title: "Seleccionar Impresora" }));
27
+ }
28
+ function BluetoothPrinterSelector({ onPrinterSelected, onConnectionChange, }) {
29
+ const [hasPermission, setHasPermission] = useState(null);
30
+ const [selectedPrinter, setSelectedPrinter] = useState(null);
31
+ const [isConnected, setIsConnected] = useState(false);
32
+ const [loading, setLoading] = useState(false);
33
+ const [selectorVisible, setSelectorVisible] = useState(false);
34
+ const checkStatus = useCallback(async () => {
35
+ const permissions = await bluetoothService.checkPermissions();
36
+ setHasPermission(permissions.isGranted);
37
+ const printer = await printerService.getSelectedPrinter();
38
+ setSelectedPrinter(printer);
39
+ const connected = printerService.isConnected();
40
+ setIsConnected(connected);
41
+ }, []);
42
+ useEffect(() => {
43
+ checkStatus();
44
+ }, [checkStatus]);
45
+ useEffect(() => {
46
+ onConnectionChange?.(isConnected);
47
+ }, [isConnected, onConnectionChange]);
48
+ async function handleRequestPermission() {
49
+ setLoading(true);
50
+ try {
51
+ const permissions = await bluetoothService.requestPermissions();
52
+ setHasPermission(permissions.isGranted);
53
+ }
54
+ finally {
55
+ setLoading(false);
56
+ }
57
+ }
58
+ async function handleConnect() {
59
+ if (!selectedPrinter)
60
+ return;
61
+ setLoading(true);
62
+ try {
63
+ const connected = await printerService.connect(selectedPrinter.address, selectedPrinter.name);
64
+ setIsConnected(connected);
65
+ }
66
+ finally {
67
+ setLoading(false);
68
+ }
69
+ }
70
+ async function handleTestPrint() {
71
+ setLoading(true);
72
+ try {
73
+ await printerService.printTestPage();
74
+ }
75
+ finally {
76
+ setLoading(false);
77
+ }
78
+ }
79
+ function handleDeviceSelected(device) {
80
+ checkStatus();
81
+ setSelectorVisible(false);
82
+ const printer = {
83
+ name: device.name || "Impresora",
84
+ address: device.address,
85
+ selectedAt: new Date().toISOString(),
86
+ };
87
+ onPrinterSelected?.(printer);
88
+ }
89
+ const granted = hasPermission === true;
90
+ return (_jsxs(View, { className: "bg-slate-50 rounded-xl p-5 my-2.5 shadow-sm", children: [_jsxs(View, { className: "flex-row items-start", children: [_jsx(Display.Icon, { color: granted ? "#22c55e" : "#ef4444", customIcon: PrinterIcon, size: 35, type: "custom" }), _jsxs(View, { className: "ml-2.5 flex-1", children: [_jsx(Typography.Text, { className: "text-base mb-1", semibold: true, children: "Impresora Bluetooth" }), hasPermission === null ? (_jsx(Typography.Text, { className: "text-gray-500", children: "Verificando permisos..." })) : !granted ? (_jsx(PermissionRequired, { loading: loading, onRequestPermission: handleRequestPermission })) : (_jsxs(_Fragment, { children: [_jsx(Typography.Text, { className: statusTextVariants({ granted: true }), children: "Bluetooth habilitado" }), selectedPrinter ? (_jsx(PrinterInfo, { isConnected: isConnected, loading: loading, onChangePrinter: () => setSelectorVisible(true), onConnect: handleConnect, onTestPrint: handleTestPrint, printer: selectedPrinter })) : (_jsx(NoPrinterSelected, { loading: loading, onSelectPrinter: () => setSelectorVisible(true) }))] }))] })] }), _jsx(BluetoothDeviceSelectorModal, { onClose: () => setSelectorVisible(false), onSelect: handleDeviceSelected, visible: selectorVisible })] }));
91
+ }
92
+ export { BluetoothPrinterSelector };
@@ -0,0 +1,11 @@
1
+ export interface DatabaseInfoRowData {
2
+ name: string;
3
+ description?: string;
4
+ tableName?: string;
5
+ }
6
+ interface DatabaseInfoRowProps {
7
+ item: DatabaseInfoRowData;
8
+ onPress?: () => void;
9
+ }
10
+ declare function DatabaseInfoRow({ item, onPress }: DatabaseInfoRowProps): import("react/jsx-runtime").JSX.Element;
11
+ export default DatabaseInfoRow;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import { Display } from "@sincpro/mobile-ui/Display";
3
+ import { ListViewV2 } from "@sincpro/mobile-ui/views/ListViewV2";
4
+ import { View } from "react-native";
5
+ function DatabaseInfoRow({ item, onPress }) {
6
+ return (_jsxs(ListViewV2.Content.Row, { onPress: onPress, children: [_jsx(ListViewV2.Content.Row.Avatar, { children: _jsx(View, { className: "justify-center items-center p-2 rounded-lg bg-gray-100", children: _jsx(Display.Icon, { name: "database", size: 32, type: "antdesign" }) }) }), _jsxs(ListViewV2.Content.Row.Content, { children: [_jsx(ListViewV2.Content.Row.Title, { numberOfLines: 2, children: item.name }), _jsx(ListViewV2.Content.Row.Subtitle, { children: item.description || "N/A" })] })] }));
7
+ }
8
+ export default DatabaseInfoRow;
@@ -0,0 +1,7 @@
1
+ import { DomainEvent } from "../../../domain/event_sourcing";
2
+ import { IRowItemProps } from "@sincpro/mobile-ui/views/types/IListView";
3
+ interface DeadLetterQueueRowProps extends IRowItemProps<DomainEvent> {
4
+ onRetry: (event: DomainEvent) => Promise<void> | void;
5
+ }
6
+ declare function DeadLetterQueueRow({ item, onRetry }: DeadLetterQueueRowProps): import("react/jsx-runtime").JSX.Element | null;
7
+ export default DeadLetterQueueRow;
@@ -0,0 +1,72 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import DeadLetterErrorBlock from "../../../ui/components/molecules/DeadLetterErrorBlock";
3
+ import { Display } from "@sincpro/mobile-ui/Display";
4
+ import { Form } from "@sincpro/mobile-ui/Form";
5
+ import { Typography } from "@sincpro/mobile-ui/Typography";
6
+ import JsonPreview from "@sincpro/mobile-ui/widgets/JSONViewer";
7
+ import * as Clipboard from "expo-clipboard";
8
+ import { useState } from "react";
9
+ import { View } from "react-native";
10
+ function getAttemptsBadgeVariant(attempts) {
11
+ if (attempts < 3)
12
+ return "warning";
13
+ return "danger";
14
+ }
15
+ function DeadLetterQueueRow({ item, onRetry }) {
16
+ const [showPayload, setShowPayload] = useState(false);
17
+ const [showError, setShowError] = useState(false);
18
+ const [sending, setSending] = useState(false);
19
+ if (!item || typeof item !== "object") {
20
+ return null;
21
+ }
22
+ const event = item;
23
+ if (!event.name || !event.createdAt) {
24
+ return null;
25
+ }
26
+ const togglePayload = () => setShowPayload((v) => !v);
27
+ const handleRetry = async () => {
28
+ if (sending)
29
+ return;
30
+ setSending(true);
31
+ try {
32
+ await onRetry(event);
33
+ setTimeout(() => setSending(false), 5000);
34
+ }
35
+ catch {
36
+ setSending(false);
37
+ }
38
+ };
39
+ const friendlyName = event.label || event.name;
40
+ const attemptsBadgeVariant = getAttemptsBadgeVariant(event.attempts ?? 0);
41
+ function renderBadges() {
42
+ const badges = [];
43
+ badges.push(_jsx(Display.Badge, { className: "self-end", label: "FAILED", variant: "danger" }, "status"));
44
+ badges.push(_jsx(Display.Badge, { className: "self-end", label: `${event.attempts} intento${event.attempts !== 1 ? "s" : ""}`, variant: attemptsBadgeVariant }, "attempts"));
45
+ if (event.requiresNetwork) {
46
+ badges.push(_jsx(Display.Badge, { className: "self-end", label: "Internet", variant: "info" }, "network"));
47
+ }
48
+ if (typeof event.sequence === "number" && event.sequence > 0) {
49
+ badges.push(_jsx(Display.Badge, { className: "self-end", label: `#${event.sequence}`, variant: "infoDark" }, "seq"));
50
+ }
51
+ return _jsx(View, { className: "flex-col items-end gap-1.5", children: badges });
52
+ }
53
+ function renderHeader() {
54
+ return (_jsxs(View, { className: "flex-row items-start mb-2", children: [_jsxs(View, { className: "flex-1 pr-2", children: [_jsx(Typography.Text, { className: "mb-1", numberOfLines: 2, semibold: true, variant: "body", children: friendlyName }), _jsx(Display.CopyableText, { value: event.name })] }), _jsx(View, { className: "ml-2 self-start", children: renderBadges() })] }));
55
+ }
56
+ function renderMeta() {
57
+ return (_jsxs(View, { className: "flex-row items-center justify-between mb-3", children: [_jsx(Display.Date, { className: "text-gray-800", showTime: true, textVariant: "bodySmall", value: event.createdAt }), _jsx(Display.CopyableText, { label: "Copiar UUID", value: event.uuid })] }));
58
+ }
59
+ function renderPayload() {
60
+ if (!showPayload) {
61
+ return null;
62
+ }
63
+ return (_jsxs(View, { className: "bg-gray-50 rounded-lg p-2 border border-gray-200 mb-2.5", children: [_jsx(Typography.Text, { className: "text-gray-700 mb-1", semibold: true, variant: "bodySmall", children: "Payload" }), _jsx(JsonPreview, { selectedJson: event.asJSON(true) })] }));
64
+ }
65
+ function renderActions() {
66
+ return (_jsxs(View, { className: "mt-2 gap-2", children: [_jsx(Form.Button, { className: "w-full", onPress: () => setShowError((v) => !v), size: "small", title: showError ? "Ocultar error" : "Ver error", variant: "secondary" }), _jsx(Form.Button, { className: "w-full", onPress: togglePayload, size: "small", title: showPayload ? "Ocultar payload" : "Ver payload", variant: "secondary" }), _jsx(Form.Button, { className: "w-full", onPress: async () => {
67
+ await Clipboard.setStringAsync(event.asJSON(true));
68
+ }, size: "small", title: "Copiar payload", variant: "secondary" }), _jsx(Form.Button, { className: "w-full", disabled: sending, loading: sending, onPress: handleRetry, size: "medium", title: sending ? "Reintentando…" : "Reintentar", variant: "primary" })] }));
69
+ }
70
+ return (_jsxs(View, { className: "bg-white border border-gray-200 rounded-2xl mx-4 p-3.5 mb-3 shadow-sm", children: [renderHeader(), renderMeta(), _jsx(DeadLetterErrorBlock, { errorMessage: event.errorMessage, expanded: showError, onToggle: () => setShowError((v) => !v) }), renderPayload(), renderActions()] }));
71
+ }
72
+ export default DeadLetterQueueRow;
@@ -0,0 +1,7 @@
1
+ import { DomainEvent } from "../../../domain/event_sourcing";
2
+ interface EventRowProps {
3
+ item: DomainEvent;
4
+ onRetry: (event: DomainEvent) => Promise<void> | void;
5
+ }
6
+ declare function EventRow({ item, onRetry }: EventRowProps): import("react/jsx-runtime").JSX.Element | null;
7
+ export default EventRow;
@@ -0,0 +1,90 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import { EEventStatus } from "../../../domain/event_sourcing";
3
+ import { Display } from "@sincpro/mobile-ui/Display";
4
+ import { Form } from "@sincpro/mobile-ui/Form";
5
+ import { Typography } from "@sincpro/mobile-ui/Typography";
6
+ import JsonPreview from "@sincpro/mobile-ui/widgets/JSONViewer";
7
+ import * as Clipboard from "expo-clipboard";
8
+ import { useState } from "react";
9
+ import { View } from "react-native";
10
+ function EventRow({ item, onRetry }) {
11
+ const [showPayload, setShowPayload] = useState(false);
12
+ const [sending, setSending] = useState(false);
13
+ if (!item)
14
+ return null;
15
+ const payloadString = item.asJSON(true);
16
+ const friendlyName = item.label || item.name;
17
+ const statusBadge = (() => {
18
+ if (item.status === EEventStatus.FAILED) {
19
+ return {
20
+ backgroundClass: "bg-red-100 border-red-200",
21
+ textClass: "text-red-700",
22
+ label: "FAILED",
23
+ };
24
+ }
25
+ if (item.status === EEventStatus.ACKNOWLEDGED) {
26
+ return {
27
+ backgroundClass: "bg-green-100 border-green-200",
28
+ textClass: "text-green-700",
29
+ label: "ACKNOWLEDGED",
30
+ };
31
+ }
32
+ if (item.status === EEventStatus.PENDING || item.status === EEventStatus.PROCESSING) {
33
+ return {
34
+ backgroundClass: "bg-blue-100 border-blue-200",
35
+ textClass: "text-blue-700",
36
+ label: item.status,
37
+ };
38
+ }
39
+ return {
40
+ backgroundClass: "bg-gray-100 border-gray-200",
41
+ textClass: "text-gray-700",
42
+ label: item.status,
43
+ };
44
+ })();
45
+ const handleRetry = async () => {
46
+ if (sending)
47
+ return;
48
+ setSending(true);
49
+ try {
50
+ await onRetry(item);
51
+ }
52
+ finally {
53
+ setTimeout(() => setSending(false), 1200);
54
+ }
55
+ };
56
+ function renderMeta() {
57
+ return (_jsxs(View, { className: "flex-row items-center justify-between mb-3", children: [_jsx(Display.Date, { className: "text-gray-800", showTime: true, textVariant: "bodySmall", value: item.createdAt }), _jsx(Display.CopyableText, { label: "Copiar UUID", value: item.uuid })] }));
58
+ }
59
+ function renderBadges() {
60
+ const badges = [];
61
+ const statusVariantMap = {
62
+ "bg-red-100 border-red-200": "danger",
63
+ "bg-green-100 border-green-200": "successDark",
64
+ "bg-blue-100 border-blue-200": "infoDark",
65
+ "bg-gray-100 border-gray-200": "warning",
66
+ };
67
+ const variant = statusVariantMap[statusBadge.backgroundClass] || "info";
68
+ badges.push(_jsx(Display.Badge, { className: "self-end", label: statusBadge.label, variant: variant }, "status"));
69
+ if (item.requiresNetwork) {
70
+ badges.push(_jsx(Display.Badge, { className: "self-end", label: "Internet", variant: "info" }, "network"));
71
+ }
72
+ if (typeof item.sequence === "number" && item.sequence > 0) {
73
+ badges.push(_jsx(Display.Badge, { className: "self-end", label: `#${item.sequence}`, variant: "infoDark" }, "seq"));
74
+ }
75
+ return _jsx(View, { className: "flex-col items-end gap-1.5", children: badges });
76
+ }
77
+ function renderPayload() {
78
+ if (!showPayload) {
79
+ return null;
80
+ }
81
+ return (_jsxs(View, { className: "bg-gray-50 rounded-lg p-2 border border-gray-200 mb-2", children: [_jsx(Typography.Text, { className: "text-gray-700 mb-1", semibold: true, variant: "bodySmall", children: "Payload" }), _jsx(JsonPreview, { selectedJson: payloadString })] }));
82
+ }
83
+ function renderActions() {
84
+ return (_jsxs(View, { className: "mt-2 gap-2", children: [_jsx(Form.Button, { className: "w-full", onPress: () => setShowPayload((v) => !v), size: "small", title: showPayload ? "Ocultar payload" : "Ver payload", variant: "secondary" }), _jsx(Form.Button, { className: "w-full", onPress: async () => {
85
+ await Clipboard.setStringAsync(payloadString);
86
+ }, size: "small", title: "Copiar payload", variant: "secondary" }), _jsx(Form.Button, { className: "w-full", disabled: sending, loading: sending, onPress: handleRetry, size: "medium", title: sending ? "Reenviando…" : "Re-disparar", variant: "primary" })] }));
87
+ }
88
+ return (_jsxs(View, { className: "bg-white border border-gray-200 rounded-2xl mx-4 p-3.5 mb-3 shadow-sm", children: [_jsxs(View, { className: "flex-row items-start mb-2", children: [_jsxs(View, { className: "flex-1 pr-2", children: [_jsx(Typography.Text, { className: "mb-1", numberOfLines: 2, semibold: true, variant: "body", children: friendlyName }), _jsx(Display.CopyableText, { value: item.name })] }), _jsx(View, { className: "ml-2 self-start", children: renderBadges() })] }), renderMeta(), renderPayload(), renderActions()] }));
89
+ }
90
+ export default EventRow;
@@ -0,0 +1,35 @@
1
+ import { IInjectedScript } from "../../../domain/webview";
2
+ import { NativeSyntheticEvent } from "react-native";
3
+ import { type WebViewMessageEvent, type WebViewNavigation } from "react-native-webview";
4
+ import type { WebViewError } from "react-native-webview/lib/WebViewTypes";
5
+ export type WebViewErrorEvent = NativeSyntheticEvent<WebViewError>;
6
+ export interface InjectableWebViewProps {
7
+ url: string;
8
+ scripts?: IInjectedScript[];
9
+ injectedJavaScript?: string;
10
+ onMessage?: (event: WebViewMessageEvent) => void;
11
+ onNavigationChange?: (state: WebViewNavigation) => void;
12
+ onLoadStart?: () => void;
13
+ onLoadEnd?: () => void;
14
+ onError?: (error: WebViewErrorEvent) => void;
15
+ showControls?: boolean;
16
+ renderLoading?: () => React.ReactElement;
17
+ backgroundColor?: string;
18
+ reloadKey?: number | string;
19
+ hardRefreshKey?: number | string;
20
+ maxRetries?: number;
21
+ useDefaultCache?: boolean;
22
+ }
23
+ interface WebViewControlsProps {
24
+ canGoBack: boolean;
25
+ canGoForward: boolean;
26
+ onGoBack: () => void;
27
+ onGoForward: () => void;
28
+ onReload: () => void;
29
+ }
30
+ declare function WebViewControls({ canGoBack: _canGoBack, canGoForward: _canGoForward, onGoBack: _onGoBack, onGoForward: _onGoForward, onReload: _onReload, }: WebViewControlsProps): import("react/jsx-runtime").JSX.Element;
31
+ declare function InjectableWebView({ url, scripts, injectedJavaScript, onMessage, onNavigationChange, onLoadStart, onLoadEnd, onError, renderLoading, backgroundColor, reloadKey, hardRefreshKey, maxRetries, useDefaultCache, }: InjectableWebViewProps): import("react/jsx-runtime").JSX.Element;
32
+ declare namespace InjectableWebView {
33
+ var Controls: typeof WebViewControls;
34
+ }
35
+ export { InjectableWebView };
@@ -0,0 +1,169 @@
1
+ import { jsx as _jsx } from "nativewind/jsx-runtime";
2
+ import { loggerUseCases } from "../../../infrastructure/logger";
3
+ import { webViewService } from "../../../services/webview.service";
4
+ import Spinner from "@sincpro/mobile-ui/Feedback/Feedback.Spinner";
5
+ import { useCallback, useEffect, useRef, useState } from "react";
6
+ import { BackHandler, StyleSheet, View } from "react-native";
7
+ import { SafeAreaView } from "react-native-safe-area-context";
8
+ import Toast from "react-native-toast-message";
9
+ import WebView from "react-native-webview";
10
+ function WebViewControls({ canGoBack: _canGoBack, canGoForward: _canGoForward, onGoBack: _onGoBack, onGoForward: _onGoForward, onReload: _onReload, }) {
11
+ return _jsx(View, {});
12
+ }
13
+ function WebViewLoadingIndicator({ backgroundColor }) {
14
+ return (_jsx(View, { style: [StyleSheet.absoluteFillObject, styles.loading, { backgroundColor }], children: _jsx(Spinner, { size: "large", text: "Cargando p\u00E1gina..." }) }));
15
+ }
16
+ function useWebView() {
17
+ const webViewRef = useRef(null);
18
+ const [canGoBack, setCanGoBack] = useState(false);
19
+ const [retryCount, setRetryCount] = useState(0);
20
+ const errorTimeoutRef = useRef(null);
21
+ const loadingTimeoutRef = useRef(null);
22
+ const isLoadingRef = useRef(false);
23
+ const handleNavigationStateChange = useCallback((navState) => {
24
+ setCanGoBack(navState.canGoBack);
25
+ }, []);
26
+ const goBack = useCallback(() => {
27
+ if (webViewRef.current && canGoBack) {
28
+ webViewRef.current.goBack();
29
+ }
30
+ }, [canGoBack]);
31
+ const reload = useCallback(() => {
32
+ if (webViewRef.current) {
33
+ loggerUseCases.info("Reloading WebView");
34
+ webViewRef.current.reload();
35
+ }
36
+ }, []);
37
+ const hardRefresh = useCallback(() => {
38
+ if (webViewRef.current) {
39
+ loggerUseCases.info("Hard refresh: Clearing cache and reloading");
40
+ Toast.show({
41
+ type: "info",
42
+ text1: "Limpiando caché...",
43
+ text2: "Descargando cliente fresco",
44
+ visibilityTime: 2000,
45
+ });
46
+ if (webViewRef.current.clearCache) {
47
+ webViewRef.current.clearCache(true);
48
+ }
49
+ setTimeout(() => {
50
+ if (webViewRef.current) {
51
+ webViewRef.current.reload();
52
+ }
53
+ }, 300);
54
+ }
55
+ }, []);
56
+ useEffect(() => {
57
+ const backHandler = BackHandler.addEventListener("hardwareBackPress", () => {
58
+ if (canGoBack) {
59
+ goBack();
60
+ return true;
61
+ }
62
+ return false;
63
+ });
64
+ return () => backHandler.remove();
65
+ }, [canGoBack, goBack]);
66
+ useEffect(() => {
67
+ return () => {
68
+ if (errorTimeoutRef.current) {
69
+ clearTimeout(errorTimeoutRef.current);
70
+ }
71
+ if (loadingTimeoutRef.current) {
72
+ clearTimeout(loadingTimeoutRef.current);
73
+ }
74
+ };
75
+ }, []);
76
+ return {
77
+ webViewRef,
78
+ canGoBack,
79
+ handleNavigationStateChange,
80
+ retryCount,
81
+ setRetryCount,
82
+ reload,
83
+ hardRefresh,
84
+ errorTimeoutRef,
85
+ loadingTimeoutRef,
86
+ isLoadingRef,
87
+ };
88
+ }
89
+ function InjectableWebView({ url, scripts, injectedJavaScript, onMessage, onNavigationChange, onLoadStart, onLoadEnd, onError, renderLoading, backgroundColor = "#f8f8f8", reloadKey, hardRefreshKey, maxRetries = 2, useDefaultCache = true, }) {
90
+ const { webViewRef, handleNavigationStateChange, retryCount, setRetryCount, reload, hardRefresh, errorTimeoutRef, loadingTimeoutRef, isLoadingRef, } = useWebView();
91
+ const finalScript = webViewService.combineScripts(scripts, injectedJavaScript);
92
+ function handleMessage(event) {
93
+ onMessage?.(event);
94
+ }
95
+ function handleNavigationStateChangeInternal(navState) {
96
+ handleNavigationStateChange(navState);
97
+ onNavigationChange?.(navState);
98
+ }
99
+ function handleLoadStart() {
100
+ isLoadingRef.current = true;
101
+ if (loadingTimeoutRef.current) {
102
+ clearTimeout(loadingTimeoutRef.current);
103
+ }
104
+ loadingTimeoutRef.current = setTimeout(() => {
105
+ if (isLoadingRef.current && webViewRef.current) {
106
+ loggerUseCases.warn("WebView stuck loading for 15s, attempting reload");
107
+ webViewRef.current.reload();
108
+ }
109
+ }, 15000);
110
+ onLoadStart?.();
111
+ }
112
+ function handleLoadEnd() {
113
+ isLoadingRef.current = false;
114
+ if (loadingTimeoutRef.current) {
115
+ clearTimeout(loadingTimeoutRef.current);
116
+ loadingTimeoutRef.current = null;
117
+ }
118
+ setRetryCount(0);
119
+ onLoadEnd?.();
120
+ }
121
+ function handleError(error) {
122
+ const errorMsg = error.nativeEvent.description || "Error desconocido";
123
+ const errorCode = error.nativeEvent.code || -1;
124
+ loggerUseCases.warn(`WebView error (${errorCode}): ${errorMsg}`);
125
+ if (retryCount < maxRetries) {
126
+ const retryDelay = Math.min(1000 * Math.pow(2, retryCount), 5000);
127
+ loggerUseCases.info(`Reintentando carga en ${retryDelay}ms (intento ${retryCount + 1}/${maxRetries})`);
128
+ errorTimeoutRef.current = setTimeout(() => {
129
+ setRetryCount((prev) => prev + 1);
130
+ reload();
131
+ }, retryDelay);
132
+ }
133
+ else {
134
+ loggerUseCases.error(`WebView falló después de ${maxRetries} reintentos`);
135
+ onError?.(error);
136
+ }
137
+ }
138
+ const prevReloadKeyRef = useRef(reloadKey);
139
+ const prevHardRefreshKeyRef = useRef(hardRefreshKey);
140
+ useEffect(() => {
141
+ if (reloadKey === prevReloadKeyRef.current)
142
+ return;
143
+ prevReloadKeyRef.current = reloadKey;
144
+ if (webViewRef.current) {
145
+ loggerUseCases.info(`Reloading WebView due to key change: ${reloadKey}`);
146
+ reload();
147
+ }
148
+ }, [reloadKey, reload]);
149
+ useEffect(() => {
150
+ if (hardRefreshKey === prevHardRefreshKeyRef.current)
151
+ return;
152
+ prevHardRefreshKeyRef.current = hardRefreshKey;
153
+ if (webViewRef.current) {
154
+ loggerUseCases.info(`Hard refresh WebView due to key change: ${hardRefreshKey}`);
155
+ hardRefresh();
156
+ }
157
+ }, [hardRefreshKey, hardRefresh]);
158
+ const cacheMode = useDefaultCache ? "LOAD_DEFAULT" : "LOAD_NO_CACHE";
159
+ return (_jsx(SafeAreaView, { className: "flex-1", edges: ["top", "bottom"], style: { backgroundColor }, children: _jsx(WebView, { allowsBackForwardNavigationGestures: true, cacheEnabled: true, cacheMode: cacheMode, domStorageEnabled: true, injectedJavaScript: finalScript, javaScriptEnabled: true, mixedContentMode: "compatibility", onError: handleError, onLoadEnd: handleLoadEnd, onLoadStart: handleLoadStart, onMessage: handleMessage, onNavigationStateChange: handleNavigationStateChangeInternal, ref: webViewRef, renderLoading: renderLoading ||
160
+ (() => _jsx(WebViewLoadingIndicator, { backgroundColor: backgroundColor })), scalesPageToFit: true, sharedCookiesEnabled: true, source: { uri: url }, startInLoadingState: true, style: { flex: 1, backgroundColor } }) }));
161
+ }
162
+ const styles = StyleSheet.create({
163
+ loading: {
164
+ alignItems: "center",
165
+ justifyContent: "center",
166
+ },
167
+ });
168
+ InjectableWebView.Controls = WebViewControls;
169
+ export { InjectableWebView };
@@ -0,0 +1,11 @@
1
+ import { FormatterMap, LabelMap } from "../../../domain/receipt";
2
+ interface ReceiptProps {
3
+ data: Record<string, any>;
4
+ labels?: LabelMap;
5
+ order?: string[];
6
+ strictOrder?: boolean;
7
+ formatters?: FormatterMap;
8
+ emptyPlaceholder?: string;
9
+ }
10
+ declare function Receipt({ data, labels, order, strictOrder, formatters, emptyPlaceholder, }: ReceiptProps): import("react/jsx-runtime").JSX.Element;
11
+ export default Receipt;