@sincpro/mobile 0.1.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +831 -47
- package/dist/entrypoints/queue/printImage.subscriber.js +3 -3
- package/dist/entrypoints/ui/AppShell.d.ts +61 -0
- package/dist/entrypoints/ui/AppShell.js +72 -11
- package/dist/entrypoints/ui/common_provider.d.ts +6 -1
- package/dist/entrypoints/ui/common_provider.js +45 -1
- package/dist/entrypoints/ui/domain_switcher.js +5 -4
- package/dist/entrypoints/ui/theme.d.ts +8 -7
- package/dist/entrypoints/ui/theme.js +8 -7
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infrastructure/ui/ToastHost.d.ts +6 -1
- package/dist/infrastructure/ui/ToastHost.js +18 -9
- package/dist/services/printer.service.d.ts +6 -0
- package/dist/services/printer.service.js +36 -3
- package/dist/ui/components/atoms/DebugBanner.js +4 -8
- package/dist/ui/components/atoms/PrinterIcon.d.ts +4 -0
- package/dist/ui/components/atoms/PrinterIcon.js +4 -0
- package/dist/ui/components/atoms/index.d.ts +1 -0
- package/dist/ui/components/atoms/index.js +1 -0
- package/dist/ui/components/molecules/BluetoothDeviceSelectorModal.js +16 -10
- package/dist/ui/components/molecules/DeadLetterErrorBlock.js +3 -2
- package/dist/ui/components/molecules/EventTimelineItem.js +31 -33
- package/dist/ui/components/molecules/GeoPermissionCard.d.ts +8 -0
- package/dist/ui/components/molecules/GeoPermissionCard.js +21 -0
- package/dist/ui/components/molecules/ProcessToast.d.ts +7 -4
- package/dist/ui/components/molecules/ProcessToast.js +45 -44
- package/dist/ui/components/molecules/ProcessToastProvider.js +1 -1
- package/dist/ui/components/molecules/TimeZoneSelector.d.ts +7 -0
- package/dist/ui/components/molecules/TimeZoneSelector.js +19 -0
- package/dist/ui/components/molecules/TimeZoneSelectorModal.d.ts +9 -0
- package/dist/ui/components/molecules/TimeZoneSelectorModal.js +61 -0
- package/dist/ui/components/molecules/index.d.ts +4 -0
- package/dist/ui/components/molecules/index.js +3 -0
- package/dist/ui/components/molecules/timezone.d.ts +4 -0
- package/dist/ui/components/molecules/timezone.js +1 -0
- package/dist/ui/components/organisms/BluetoothPrinterSelector.d.ts +3 -1
- package/dist/ui/components/organisms/BluetoothPrinterSelector.js +29 -9
- package/dist/ui/components/organisms/DatabaseInfoRow.js +1 -1
- package/dist/ui/components/organisms/DeadLetterQueueRow.js +3 -3
- package/dist/ui/components/organisms/EventRow.js +15 -15
- package/dist/ui/components/organisms/InjectableWebView.js +20 -10
- package/dist/ui/components/organisms/TableInfoInfoRow.js +1 -1
- package/dist/ui/screens/database/database.json_detail.js +3 -3
- package/dist/ui/screens/database/database.list.js +4 -5
- package/dist/ui/screens/database/database.table_rows.js +1 -2
- package/dist/ui/screens/dead_letter_queue/dead_letter_queue.list.js +4 -5
- package/dist/ui/screens/events/events.list.js +4 -5
- package/package.json +9 -5
- package/dist/ui/layouts/router_layouts.d.ts +0 -17
- package/dist/ui/layouts/router_layouts.js +0 -20
|
@@ -2,41 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
|
2
2
|
import { EEventStatus } from "../../../domain/event_sourcing";
|
|
3
3
|
import { Display } from "@sincpro/mobile-ui/Display";
|
|
4
4
|
import CopyableText from "@sincpro/mobile-ui/Display/Display.CopyableText";
|
|
5
|
-
import {
|
|
5
|
+
import { useTheme } from "@sincpro/mobile-ui/theme";
|
|
6
6
|
import { Typography } from "@sincpro/mobile-ui/Typography";
|
|
7
7
|
import JsonPreview from "@sincpro/mobile-ui/widgets/JSONViewer";
|
|
8
8
|
import { useState } from "react";
|
|
9
9
|
import { TouchableOpacity, View } from "react-native";
|
|
10
|
-
const statusColorMap = {
|
|
11
|
-
[EEventStatus.ACKNOWLEDGED]: {
|
|
12
|
-
bg: "bg-emerald-600",
|
|
13
|
-
color: theme.success,
|
|
14
|
-
light: `${theme.success}4D`,
|
|
15
|
-
},
|
|
16
|
-
[EEventStatus.PENDING]: {
|
|
17
|
-
bg: "bg-amber-500",
|
|
18
|
-
color: theme.warning,
|
|
19
|
-
light: `${theme.warning}4D`,
|
|
20
|
-
},
|
|
21
|
-
[EEventStatus.PROCESSING]: {
|
|
22
|
-
bg: "bg-accent",
|
|
23
|
-
color: theme.warning,
|
|
24
|
-
light: `${theme.warning}4D`,
|
|
25
|
-
},
|
|
26
|
-
[EEventStatus.FAILED]: {
|
|
27
|
-
bg: "bg-red-500",
|
|
28
|
-
color: theme.danger,
|
|
29
|
-
light: `${theme.danger}4D`,
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
const defaultStatus = {
|
|
33
|
-
bg: "bg-gray-500",
|
|
34
|
-
color: theme.text.secondary,
|
|
35
|
-
light: `${theme.text.secondary}4D`,
|
|
36
|
-
};
|
|
37
|
-
function getStatusColor(status) {
|
|
38
|
-
return statusColorMap[status] ?? defaultStatus;
|
|
39
|
-
}
|
|
40
10
|
function getStatusIcon(status) {
|
|
41
11
|
switch (status) {
|
|
42
12
|
case EEventStatus.ACKNOWLEDGED:
|
|
@@ -57,9 +27,37 @@ function formatEventName(name) {
|
|
|
57
27
|
}
|
|
58
28
|
function EventTimelineItem({ event, isLast = false }) {
|
|
59
29
|
const [expanded, setExpanded] = useState(false);
|
|
60
|
-
const
|
|
30
|
+
const theme = useTheme();
|
|
31
|
+
const statusColorMap = {
|
|
32
|
+
[EEventStatus.ACKNOWLEDGED]: {
|
|
33
|
+
bg: "bg-success",
|
|
34
|
+
color: theme.success,
|
|
35
|
+
light: `${theme.success}4D`,
|
|
36
|
+
},
|
|
37
|
+
[EEventStatus.PENDING]: {
|
|
38
|
+
bg: "bg-warning",
|
|
39
|
+
color: theme.warning,
|
|
40
|
+
light: `${theme.warning}4D`,
|
|
41
|
+
},
|
|
42
|
+
[EEventStatus.PROCESSING]: {
|
|
43
|
+
bg: "bg-accent",
|
|
44
|
+
color: theme.warning,
|
|
45
|
+
light: `${theme.warning}4D`,
|
|
46
|
+
},
|
|
47
|
+
[EEventStatus.FAILED]: {
|
|
48
|
+
bg: "bg-danger",
|
|
49
|
+
color: theme.danger,
|
|
50
|
+
light: `${theme.danger}4D`,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
const defaultStatus = {
|
|
54
|
+
bg: "bg-text-tertiary",
|
|
55
|
+
color: theme.text.secondary,
|
|
56
|
+
light: `${theme.text.secondary}4D`,
|
|
57
|
+
};
|
|
58
|
+
const statusInfo = statusColorMap[event.status] ?? defaultStatus;
|
|
61
59
|
const statusIcon = getStatusIcon(event.status);
|
|
62
60
|
const friendlyName = formatEventName(event.name);
|
|
63
|
-
return (_jsxs(View, { className: "flex-row px-4", children: [_jsxs(View, { className: "items-center w-8 mr-3", children: [_jsx(View, { className: `w-6 h-6 rounded-full items-center justify-center ${statusInfo.bg}`, children: _jsx(Display.Icon, { color: "#ffffff", name: statusIcon, size: 12, type: "feather" }) }), !isLast && (_jsx(View, { className: "w-0.5 flex-1 mt-1", style: { backgroundColor: statusInfo.light } }))] }), _jsxs(TouchableOpacity, { activeOpacity: 0.7, className: "flex-1 bg-
|
|
61
|
+
return (_jsxs(View, { className: "flex-row px-4", children: [_jsxs(View, { className: "items-center w-8 mr-3", children: [_jsx(View, { className: `w-6 h-6 rounded-full items-center justify-center ${statusInfo.bg}`, children: _jsx(Display.Icon, { color: "#ffffff", name: statusIcon, size: 12, type: "feather" }) }), !isLast && (_jsx(View, { className: "w-0.5 flex-1 mt-1", style: { backgroundColor: statusInfo.light } }))] }), _jsxs(TouchableOpacity, { activeOpacity: 0.7, className: "flex-1 bg-bg-card rounded-xl p-3 mb-3 border border-border-light shadow-sm", onPress: () => setExpanded(!expanded), children: [_jsxs(View, { className: "flex-row justify-between items-center mb-2", children: [_jsx(Typography.Text, { semibold: true, variant: "body", children: friendlyName }), _jsx(Display.Icon, { color: theme.text.secondary, name: expanded ? "chevron-up" : "chevron-down", size: 16, type: "feather" })] }), _jsxs(View, { className: "flex-row items-center mb-1 gap-1.5", children: [_jsx(Display.Icon, { color: theme.text.secondary, name: "clock", size: 12, type: "feather" }), _jsx(Display.Date, { className: "text-text-tertiary", showTime: true, textVariant: "caption", value: event.createdAt })] }), _jsxs(View, { className: "flex-row items-center mb-1 gap-1.5", children: [_jsx(Display.Icon, { color: statusInfo.color, name: statusIcon, size: 12, type: "feather" }), _jsxs(Typography.Text, { style: { color: statusInfo.color }, variant: "caption", children: [event.status, " \u2022 Intentos: ", event.attempts] })] }), (event.sequence ?? 0) > 0 && (_jsxs(View, { className: "flex-row items-center mb-1 gap-1.5", children: [_jsx(Display.Icon, { color: theme.text.secondary, name: "hash", size: 12, type: "feather" }), _jsxs(Typography.Text, { className: "text-text-tertiary", variant: "caption", children: ["Secuencia: ", event.sequence] })] })), expanded && event && (_jsxs(View, { className: "mt-3 border-t border-border-light pt-3", children: [_jsxs(View, { className: "flex-row justify-between items-center mb-2", children: [_jsx(Typography.Text, { className: "text-text-tertiary", semibold: true, variant: "caption", children: "Payload" }), _jsx(CopyableText, { className: "text-primary text-xs", getValue: () => event.asJSON(true), label: "copy" })] }), _jsx(View, { className: "bg-bg-muted rounded-lg border border-border-default overflow-hidden", children: _jsx(JsonPreview, { selectedJson: event.asJSON(true) }) })] }))] })] }));
|
|
64
62
|
}
|
|
65
63
|
export default EventTimelineItem;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface GeoPermissionCardProps {
|
|
2
|
+
title: string;
|
|
3
|
+
hasPermission?: boolean;
|
|
4
|
+
loading?: boolean;
|
|
5
|
+
handleRequestPermission?: () => void;
|
|
6
|
+
}
|
|
7
|
+
declare function GeoPermissionCard({ title, loading, handleRequestPermission, hasPermission, }: GeoPermissionCardProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export default GeoPermissionCard;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
|
+
import { Display } from "@sincpro/mobile-ui/Display";
|
|
3
|
+
import { Form } from "@sincpro/mobile-ui/Form";
|
|
4
|
+
import { useTheme } from "@sincpro/mobile-ui/theme";
|
|
5
|
+
import { Typography } from "@sincpro/mobile-ui/Typography";
|
|
6
|
+
import { View } from "react-native";
|
|
7
|
+
import { tv } from "tailwind-variants";
|
|
8
|
+
const statusTextVariants = tv({
|
|
9
|
+
variants: {
|
|
10
|
+
granted: {
|
|
11
|
+
true: "text-success",
|
|
12
|
+
false: "text-danger",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
function GeoPermissionCard({ title, loading, handleRequestPermission, hasPermission, }) {
|
|
17
|
+
const theme = useTheme();
|
|
18
|
+
const granted = hasPermission !== undefined ? hasPermission : false;
|
|
19
|
+
return (_jsx(Display.Card, { className: "my-2.5", children: _jsxs(View, { className: "flex-row items-start", children: [_jsx(Display.Icon, { color: granted ? theme.success : theme.danger, name: granted ? "check-circle" : "location-off", size: 35, type: "material" }), _jsxs(View, { className: "ml-2.5 flex-1", children: [_jsx(Typography.Text, { className: "text-base mb-1", semibold: true, children: title }), _jsx(Typography.Text, { className: statusTextVariants({ granted }), children: granted ? "Permiso activo." : "Permiso inactivo." }), !granted && (_jsx(Form.Button, { loading: loading, onPress: handleRequestPermission, size: "small", title: "Abrir Configuración", variant: "cta" }))] })] }) }));
|
|
20
|
+
}
|
|
21
|
+
export default GeoPermissionCard;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Bridges the framework's queue/cron activity state (from CommonProvider) to the
|
|
3
|
+
* design-system Toast: a persistent `loading` toast while a queue/cron task runs, and a
|
|
4
|
+
* `danger` toast on failure. Renders nothing — the toast UI is owned by the DS
|
|
5
|
+
* `ToastProvider` (mounted in AppShell), so this must live inside it.
|
|
6
|
+
*/
|
|
7
|
+
declare function ProcessToast(): null;
|
|
5
8
|
export default ProcessToast;
|
|
@@ -1,51 +1,52 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
1
|
import { useCommon } from "../../../entrypoints/ui/common_provider";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
2
|
+
import { useToast } from "@sincpro/mobile-ui/Feedback";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
const MAX_ERROR_LEN = 150;
|
|
5
|
+
/**
|
|
6
|
+
* Bridges the framework's queue/cron activity state (from CommonProvider) to the
|
|
7
|
+
* design-system Toast: a persistent `loading` toast while a queue/cron task runs, and a
|
|
8
|
+
* `danger` toast on failure. Renders nothing — the toast UI is owned by the DS
|
|
9
|
+
* `ToastProvider` (mounted in AppShell), so this must live inside it.
|
|
10
|
+
*/
|
|
11
|
+
function ProcessToast() {
|
|
12
|
+
const { currentActivity, lastError, isProcessing } = useCommon();
|
|
13
|
+
const toast = useToast();
|
|
14
|
+
const loadingIdRef = useRef(null);
|
|
15
|
+
const shownErrorRef = useRef(null);
|
|
17
16
|
useEffect(() => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const showLoading = isProcessing && !lastError;
|
|
18
|
+
if (showLoading) {
|
|
19
|
+
const label = currentActivity?.label ?? "Sincronizando...";
|
|
20
|
+
if (loadingIdRef.current) {
|
|
21
|
+
toast.update(loadingIdRef.current, { message: label, variant: "loading" });
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
loadingIdRef.current = toast.loading(label);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else if (loadingIdRef.current) {
|
|
28
|
+
toast.hide(loadingIdRef.current);
|
|
29
|
+
loadingIdRef.current = null;
|
|
30
|
+
}
|
|
31
|
+
}, [isProcessing, lastError, currentActivity, toast]);
|
|
21
32
|
useEffect(() => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
friction: 25,
|
|
26
|
-
useNativeDriver: true,
|
|
27
|
-
}).start();
|
|
28
|
-
}, [isProcessing, slideAnim, position]);
|
|
29
|
-
function renderToast() {
|
|
30
|
-
if (!isProcessing) {
|
|
31
|
-
return null;
|
|
33
|
+
if (!lastError) {
|
|
34
|
+
shownErrorRef.current = null;
|
|
35
|
+
return;
|
|
32
36
|
}
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
function handleDismiss() {
|
|
37
|
-
setShowError(false);
|
|
38
|
-
dismiss();
|
|
37
|
+
const key = `${lastError.id}:${lastError.error ?? ""}`;
|
|
38
|
+
if (shownErrorRef.current === key) {
|
|
39
|
+
return;
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
return
|
|
41
|
+
shownErrorRef.current = key;
|
|
42
|
+
const raw = lastError.error;
|
|
43
|
+
const detail = raw
|
|
44
|
+
? raw.length > MAX_ERROR_LEN
|
|
45
|
+
? `${raw.slice(0, MAX_ERROR_LEN)}...`
|
|
46
|
+
: raw
|
|
47
|
+
: undefined;
|
|
48
|
+
toast.danger(detail ?? lastError.label, { title: `Error: ${lastError.label}` });
|
|
49
|
+
}, [lastError, toast]);
|
|
50
|
+
return null;
|
|
50
51
|
}
|
|
51
52
|
export default ProcessToast;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
2
|
import ProcessToast from "./ProcessToast";
|
|
3
3
|
export function ProcessToastProvider({ children }) {
|
|
4
|
-
return (_jsxs(_Fragment, { children: [children, _jsx(ProcessToast, {
|
|
4
|
+
return (_jsxs(_Fragment, { children: [children, _jsx(ProcessToast, {})] }));
|
|
5
5
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TimezoneLocale } from "./timezone";
|
|
2
|
+
interface TimeZoneSelectorProps {
|
|
3
|
+
onSelect?: (tz: TimezoneLocale) => void;
|
|
4
|
+
timezone?: string | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function TimeZoneSelector({ onSelect, timezone }: TimeZoneSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
|
|
2
|
+
import { Display, Form } from "@sincpro/mobile-ui/index";
|
|
3
|
+
import { theme } from "@sincpro/mobile-ui/theme/useTheme";
|
|
4
|
+
import { Typography } from "@sincpro/mobile-ui/Typography";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { View } from "react-native";
|
|
7
|
+
import { TimeZoneSelectorModal } from "./TimeZoneSelectorModal";
|
|
8
|
+
export function TimeZoneSelector({ onSelect, timezone }) {
|
|
9
|
+
const [currentTimezone, setCurrentTimezone] = useState(timezone);
|
|
10
|
+
const [selectorVisible, setSelectorVisible] = useState(false);
|
|
11
|
+
function handleTimezoneSelected(tz) {
|
|
12
|
+
setCurrentTimezone(tz.timezone);
|
|
13
|
+
onSelect?.(tz);
|
|
14
|
+
}
|
|
15
|
+
const displayTimezone = currentTimezone
|
|
16
|
+
? currentTimezone.replace(/_/g, " ").replace("America/", "")
|
|
17
|
+
: "No configurada";
|
|
18
|
+
return (_jsxs(Display.Card, { className: "my-2.5", children: [_jsxs(View, { className: "flex-row items-start", children: [_jsx(Display.Icon, { color: currentTimezone ? theme.info : theme.text.tertiary, name: "schedule", size: 35, type: "material" }), _jsxs(View, { className: "ml-2.5 flex-1", children: [_jsx(Typography.Text, { className: "text-base mb-1", semibold: true, children: "Zona Horaria" }), currentTimezone ? (_jsxs(View, { className: "mt-2 bg-bg-card rounded-lg p-3", children: [_jsx(Typography.Text, { className: "text-text-primary", semibold: true, children: displayTimezone }), _jsx(Typography.Text, { className: "text-text-tertiary text-xs", children: currentTimezone }), _jsx(View, { className: "flex-row gap-2 mt-2", children: _jsx(Form.Button, { onPress: () => setSelectorVisible(true), size: "small", title: "Cambiar", variant: "outline" }) })] })) : (_jsxs(_Fragment, { children: [_jsx(Typography.Text, { className: "text-text-secondary mb-2", children: "Selecciona una zona horaria" }), _jsx(Form.Button, { onPress: () => setSelectorVisible(true), size: "small", title: "Seleccionar Zona Horaria", variant: "cta" })] }))] })] }), _jsx(TimeZoneSelectorModal, { currentTimezone: currentTimezone, onClose: () => setSelectorVisible(false), onSelect: handleTimezoneSelected, visible: selectorVisible })] }));
|
|
19
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TimezoneLocale } from "./timezone";
|
|
2
|
+
interface TimeZoneSelectorModalProps {
|
|
3
|
+
visible: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
onSelect: (tz: TimezoneLocale) => void;
|
|
6
|
+
currentTimezone?: string | null;
|
|
7
|
+
}
|
|
8
|
+
declare function TimeZoneSelectorModal({ visible, onClose, onSelect, currentTimezone, }: TimeZoneSelectorModalProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export { TimeZoneSelectorModal };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
|
+
import Sheet from "@sincpro/mobile-ui/Dialog/Sheet";
|
|
3
|
+
import { Display } from "@sincpro/mobile-ui/Display";
|
|
4
|
+
import { Form } from "@sincpro/mobile-ui/Form";
|
|
5
|
+
import { useTheme } from "@sincpro/mobile-ui/theme";
|
|
6
|
+
import { Typography } from "@sincpro/mobile-ui/Typography";
|
|
7
|
+
import { useMemo, useState } from "react";
|
|
8
|
+
import { TextInput, TouchableOpacity, View } from "react-native";
|
|
9
|
+
const TIMEZONES_LOCALES = {
|
|
10
|
+
bolivia: { timezone: "America/La_Paz", locale: "es-BO" },
|
|
11
|
+
costaRica: { timezone: "America/Costa_Rica", locale: "es-CR" },
|
|
12
|
+
guatemala: { timezone: "America/Guatemala", locale: "es-GT" },
|
|
13
|
+
argentina: { timezone: "America/Argentina/Buenos_Aires", locale: "es-AR" },
|
|
14
|
+
peru: { timezone: "America/Lima", locale: "es-PE" },
|
|
15
|
+
newYork: { timezone: "America/New_York", locale: "en-US" },
|
|
16
|
+
japon: { timezone: "Asia/Tokyo", locale: "ja-JP" },
|
|
17
|
+
};
|
|
18
|
+
function TimezoneRow({ item, isSelected, onSelect, }) {
|
|
19
|
+
const theme = useTheme();
|
|
20
|
+
return (_jsxs(TouchableOpacity, { activeOpacity: 0.7, className: `flex-row items-center p-4 rounded-xl ${isSelected ? "bg-bg-accent" : "bg-bg-card"}`, onPress: onSelect, children: [_jsx(Display.Icon, { color: isSelected ? theme.accent : theme.text.secondary, name: "schedule", size: 24, type: "material" }), _jsxs(View, { className: "ml-3 flex-1", children: [_jsx(Typography.Text, { semibold: true, children: item.label }), _jsx(Typography.Text, { className: "text-text-tertiary text-xs", children: item.locale })] }), isSelected && (_jsx(View, { className: "w-6 h-6 rounded-full bg-accent items-center justify-center", children: _jsx(Display.Icon, { color: theme.text.inverse, name: "check", size: 16, type: "material" }) }))] }));
|
|
21
|
+
}
|
|
22
|
+
function EmptyState() {
|
|
23
|
+
const theme = useTheme();
|
|
24
|
+
return (_jsxs(View, { className: "p-8 items-center justify-center", style: { minHeight: 200 }, children: [_jsx(Display.Icon, { color: theme.text.tertiary, name: "search-off", size: 48, type: "material" }), _jsx(Typography.Text, { className: "text-text-secondary text-center mt-4", children: "No se encontraron zonas horarias" })] }));
|
|
25
|
+
}
|
|
26
|
+
function TimeZoneSelectorModal({ visible, onClose, onSelect, currentTimezone, }) {
|
|
27
|
+
const theme = useTheme();
|
|
28
|
+
const [query, setQuery] = useState("");
|
|
29
|
+
const [selectedTimezone, setSelectedTimezone] = useState(currentTimezone || null);
|
|
30
|
+
const timezoneList = useMemo(() => {
|
|
31
|
+
return Object.values(TIMEZONES_LOCALES).map((tz) => ({
|
|
32
|
+
...tz,
|
|
33
|
+
label: tz.timezone.replace(/_/g, " ").replace("America/", ""),
|
|
34
|
+
}));
|
|
35
|
+
}, []);
|
|
36
|
+
const filteredTimezones = useMemo(() => {
|
|
37
|
+
if (!query.trim())
|
|
38
|
+
return timezoneList;
|
|
39
|
+
const normalizedQuery = query
|
|
40
|
+
.toLowerCase()
|
|
41
|
+
.normalize("NFD")
|
|
42
|
+
.replace(/[\u0300-\u036f]/g, "");
|
|
43
|
+
return timezoneList.filter(({ label, timezone }) => {
|
|
44
|
+
const normalizedLabel = label
|
|
45
|
+
.toLowerCase()
|
|
46
|
+
.normalize("NFD")
|
|
47
|
+
.replace(/[\u0300-\u036f]/g, "");
|
|
48
|
+
const normalizedTz = timezone.toLowerCase();
|
|
49
|
+
return (normalizedLabel.includes(normalizedQuery) || normalizedTz.includes(normalizedQuery));
|
|
50
|
+
});
|
|
51
|
+
}, [query, timezoneList]);
|
|
52
|
+
function handleConfirm() {
|
|
53
|
+
const selected = timezoneList.find((tz) => tz.timezone === selectedTimezone);
|
|
54
|
+
if (selected) {
|
|
55
|
+
onSelect({ timezone: selected.timezone, locale: selected.locale });
|
|
56
|
+
onClose();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return (_jsxs(Sheet, { onClose: onClose, title: "Zona Horaria", visible: visible, children: [_jsx(Typography.Text, { className: "text-text-secondary text-xs mb-3", children: "Selecciona tu zona horaria" }), _jsx(TextInput, { className: "bg-bg-muted px-4 py-3 rounded-xl mb-2", onChangeText: setQuery, placeholder: "Buscar zona horaria...", placeholderTextColor: theme.text.tertiary, value: query }), filteredTimezones.length === 0 ? (_jsx(EmptyState, {})) : (_jsx(View, { className: "gap-1", children: filteredTimezones.map((item) => (_jsx(TimezoneRow, { isSelected: selectedTimezone === item.timezone, item: item, onSelect: () => setSelectedTimezone(item.timezone) }, item.timezone))) })), _jsxs(View, { className: "flex-row gap-2 mt-4", children: [_jsx(View, { className: "flex-1", children: _jsx(Form.Button, { onPress: onClose, title: "Cancelar", variant: "outline" }) }), selectedTimezone ? (_jsx(View, { className: "flex-1", children: _jsx(Form.Button, { onPress: handleConfirm, title: "Confirmar", variant: "cta" }) })) : null] })] }));
|
|
60
|
+
}
|
|
61
|
+
export { TimeZoneSelectorModal };
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export { BluetoothDeviceSelectorModal } from "./BluetoothDeviceSelectorModal";
|
|
2
2
|
export { default as DeadLetterErrorBlock } from "./DeadLetterErrorBlock";
|
|
3
3
|
export { default as EventTimelineItem } from "./EventTimelineItem";
|
|
4
|
+
export { default as GeoPermissionCard } from "./GeoPermissionCard";
|
|
4
5
|
export { default as ProcessToast } from "./ProcessToast";
|
|
5
6
|
export { ProcessToastProvider } from "./ProcessToastProvider";
|
|
7
|
+
export type { TimezoneLocale } from "./timezone";
|
|
8
|
+
export { TimeZoneSelector } from "./TimeZoneSelector";
|
|
9
|
+
export { TimeZoneSelectorModal } from "./TimeZoneSelectorModal";
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { BluetoothDeviceSelectorModal } from "./BluetoothDeviceSelectorModal";
|
|
2
2
|
export { default as DeadLetterErrorBlock } from "./DeadLetterErrorBlock";
|
|
3
3
|
export { default as EventTimelineItem } from "./EventTimelineItem";
|
|
4
|
+
export { default as GeoPermissionCard } from "./GeoPermissionCard";
|
|
4
5
|
export { default as ProcessToast } from "./ProcessToast";
|
|
5
6
|
export { ProcessToastProvider } from "./ProcessToastProvider";
|
|
7
|
+
export { TimeZoneSelector } from "./TimeZoneSelector";
|
|
8
|
+
export { TimeZoneSelectorModal } from "./TimeZoneSelectorModal";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,6 +2,8 @@ import { ISelectedPrinter } from "../../../domain/print";
|
|
|
2
2
|
interface BluetoothPrinterSelectorProps {
|
|
3
3
|
onPrinterSelected?: (printer: ISelectedPrinter) => void;
|
|
4
4
|
onConnectionChange?: (isConnected: boolean) => void;
|
|
5
|
+
/** Render as section rows (no outer card) — use inside FormViewV2.Content.Section. */
|
|
6
|
+
compact?: boolean;
|
|
5
7
|
}
|
|
6
|
-
declare function BluetoothPrinterSelector({ onPrinterSelected, onConnectionChange, }: BluetoothPrinterSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
declare function BluetoothPrinterSelector({ onPrinterSelected, onConnectionChange, compact, }: BluetoothPrinterSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
7
9
|
export { BluetoothPrinterSelector };
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
2
|
import { bluetoothService } from "../../../services/bluetooth.service";
|
|
3
3
|
import { printerService } from "../../../services/printer.service";
|
|
4
|
+
import PrinterIcon from "../../../ui/components/atoms/PrinterIcon";
|
|
4
5
|
import { BluetoothDeviceSelectorModal } from "../../../ui/components/molecules/BluetoothDeviceSelectorModal";
|
|
5
|
-
import { Display
|
|
6
|
-
import
|
|
6
|
+
import { Display } from "@sincpro/mobile-ui/Display";
|
|
7
|
+
import { Form } from "@sincpro/mobile-ui/Form";
|
|
8
|
+
import { useTheme } from "@sincpro/mobile-ui/theme";
|
|
7
9
|
import { Typography } from "@sincpro/mobile-ui/Typography";
|
|
8
10
|
import { useCallback, useEffect, useState } from "react";
|
|
9
11
|
import { View } from "react-native";
|
|
@@ -11,21 +13,22 @@ import { tv } from "tailwind-variants";
|
|
|
11
13
|
const statusTextVariants = tv({
|
|
12
14
|
variants: {
|
|
13
15
|
granted: {
|
|
14
|
-
true: "text-
|
|
15
|
-
false: "text-
|
|
16
|
+
true: "text-success",
|
|
17
|
+
false: "text-danger",
|
|
16
18
|
},
|
|
17
19
|
},
|
|
18
20
|
});
|
|
19
21
|
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" })] }));
|
|
22
|
+
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", variant: "cta" })] }));
|
|
21
23
|
}
|
|
22
24
|
function PrinterInfo({ printer, isConnected, loading, onConnect, onTestPrint, onChangePrinter, }) {
|
|
23
|
-
return (_jsxs(View, { className: "mt-2 bg-
|
|
25
|
+
return (_jsxs(View, { className: "mt-2 bg-bg-muted rounded-lg p-3", children: [_jsx(Typography.Text, { className: "text-text-primary", semibold: true, children: printer.name }), _jsx(Typography.Text, { className: "text-text-tertiary 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: "cta" })) : (_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-success mr-2" }), _jsx(Typography.Text, { className: "text-success text-xs", children: "Conectado" })] }))] }));
|
|
24
26
|
}
|
|
25
27
|
function NoPrinterSelected({ loading, onSelectPrinter, }) {
|
|
26
|
-
return (_jsx(Form.Button, { loading: loading, onPress: onSelectPrinter, size: "small", title: "Seleccionar Impresora" }));
|
|
28
|
+
return (_jsx(Form.Button, { loading: loading, onPress: onSelectPrinter, size: "small", title: "Seleccionar Impresora", variant: "cta" }));
|
|
27
29
|
}
|
|
28
|
-
function BluetoothPrinterSelector({ onPrinterSelected, onConnectionChange, }) {
|
|
30
|
+
function BluetoothPrinterSelector({ onPrinterSelected, onConnectionChange, compact = false, }) {
|
|
31
|
+
const theme = useTheme();
|
|
29
32
|
const [hasPermission, setHasPermission] = useState(null);
|
|
30
33
|
const [selectedPrinter, setSelectedPrinter] = useState(null);
|
|
31
34
|
const [isConnected, setIsConnected] = useState(false);
|
|
@@ -87,6 +90,23 @@ function BluetoothPrinterSelector({ onPrinterSelected, onConnectionChange, }) {
|
|
|
87
90
|
onPrinterSelected?.(printer);
|
|
88
91
|
}
|
|
89
92
|
const granted = hasPermission === true;
|
|
90
|
-
|
|
93
|
+
const modal = (_jsx(BluetoothDeviceSelectorModal, { onClose: () => setSelectorVisible(false), onSelect: handleDeviceSelected, visible: selectorVisible }));
|
|
94
|
+
if (compact) {
|
|
95
|
+
if (hasPermission === null) {
|
|
96
|
+
return (_jsxs(_Fragment, { children: [_jsx(Display.MenuButton, { description: "Verificando permisos...", icon: PrinterIcon, label: "Impresora Bluetooth", showDivider: false }), modal] }));
|
|
97
|
+
}
|
|
98
|
+
if (!granted) {
|
|
99
|
+
return (_jsxs(_Fragment, { children: [_jsx(Display.MenuButton, { description: "Permiso de Bluetooth requerido", icon: PrinterIcon, label: "Impresora Bluetooth", onPress: handleRequestPermission, showDivider: false }), modal] }));
|
|
100
|
+
}
|
|
101
|
+
if (!selectedPrinter) {
|
|
102
|
+
return (_jsxs(_Fragment, { children: [_jsx(Display.MenuButton, { description: "No configurada", icon: PrinterIcon, label: "Impresora Bluetooth", onPress: () => setSelectorVisible(true), showDivider: false }), modal] }));
|
|
103
|
+
}
|
|
104
|
+
return (_jsxs(_Fragment, { children: [_jsx(Display.MenuButton, { description: loading
|
|
105
|
+
? "Conectando..."
|
|
106
|
+
: isConnected
|
|
107
|
+
? "Conectado"
|
|
108
|
+
: "Sin conexión — toca para conectar", icon: PrinterIcon, label: selectedPrinter.name, onPress: !isConnected && !loading ? handleConnect : undefined }), _jsx(Display.MenuButton, { description: "Seleccionar otro dispositivo", icon: PrinterIcon, label: "Cambiar impresora", onPress: () => setSelectorVisible(true), showDivider: false }), modal] }));
|
|
109
|
+
}
|
|
110
|
+
return (_jsxs(Display.Card, { className: "my-2.5", children: [_jsxs(View, { className: "flex-row items-start", children: [_jsx(Display.Icon, { color: granted ? theme.success : theme.danger, 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-text-secondary", 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
111
|
}
|
|
92
112
|
export { BluetoothPrinterSelector };
|
|
@@ -3,6 +3,6 @@ import { Display } from "@sincpro/mobile-ui/Display";
|
|
|
3
3
|
import { ListViewV2 } from "@sincpro/mobile-ui/views/ListViewV2";
|
|
4
4
|
import { View } from "react-native";
|
|
5
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-
|
|
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-bg-muted", 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
7
|
}
|
|
8
8
|
export default DatabaseInfoRow;
|
|
@@ -54,19 +54,19 @@ function DeadLetterQueueRow({ item, onRetry }) {
|
|
|
54
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
55
|
}
|
|
56
56
|
function renderMeta() {
|
|
57
|
-
return (_jsxs(View, { className: "flex-row items-center justify-between mb-3", children: [_jsx(Display.Date, { className: "text-
|
|
57
|
+
return (_jsxs(View, { className: "flex-row items-center justify-between mb-3", children: [_jsx(Display.Date, { className: "text-text-primary", showTime: true, textVariant: "bodySmall", value: event.createdAt }), _jsx(Display.CopyableText, { label: "Copiar UUID", value: event.uuid })] }));
|
|
58
58
|
}
|
|
59
59
|
function renderPayload() {
|
|
60
60
|
if (!showPayload) {
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
|
-
return (_jsxs(View, { className: "bg-
|
|
63
|
+
return (_jsxs(View, { className: "bg-bg-muted rounded-lg p-2 border border-border-default mb-2.5", children: [_jsx(Typography.Text, { className: "text-text-primary mb-1", semibold: true, variant: "bodySmall", children: "Payload" }), _jsx(JsonPreview, { selectedJson: event.asJSON(true) })] }));
|
|
64
64
|
}
|
|
65
65
|
function renderActions() {
|
|
66
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
67
|
await Clipboard.setStringAsync(event.asJSON(true));
|
|
68
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
69
|
}
|
|
70
|
-
return (_jsxs(View, { className: "bg-
|
|
70
|
+
return (_jsxs(View, { className: "bg-bg-card border border-border-default 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
71
|
}
|
|
72
72
|
export default DeadLetterQueueRow;
|
|
@@ -17,28 +17,28 @@ function EventRow({ item, onRetry }) {
|
|
|
17
17
|
const statusBadge = (() => {
|
|
18
18
|
if (item.status === EEventStatus.FAILED) {
|
|
19
19
|
return {
|
|
20
|
-
backgroundClass: "bg-
|
|
21
|
-
textClass: "text-
|
|
20
|
+
backgroundClass: "bg-danger-light border-danger",
|
|
21
|
+
textClass: "text-danger",
|
|
22
22
|
label: "FAILED",
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
if (item.status === EEventStatus.ACKNOWLEDGED) {
|
|
26
26
|
return {
|
|
27
|
-
backgroundClass: "bg-
|
|
28
|
-
textClass: "text-
|
|
27
|
+
backgroundClass: "bg-success-light border-success",
|
|
28
|
+
textClass: "text-success",
|
|
29
29
|
label: "ACKNOWLEDGED",
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
if (item.status === EEventStatus.PENDING || item.status === EEventStatus.PROCESSING) {
|
|
33
33
|
return {
|
|
34
|
-
backgroundClass: "bg-
|
|
35
|
-
textClass: "text-
|
|
34
|
+
backgroundClass: "bg-info-light border-info",
|
|
35
|
+
textClass: "text-info",
|
|
36
36
|
label: item.status,
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
return {
|
|
40
|
-
backgroundClass: "bg-
|
|
41
|
-
textClass: "text-
|
|
40
|
+
backgroundClass: "bg-bg-muted border-border-default",
|
|
41
|
+
textClass: "text-text-primary",
|
|
42
42
|
label: item.status,
|
|
43
43
|
};
|
|
44
44
|
})();
|
|
@@ -54,15 +54,15 @@ function EventRow({ item, onRetry }) {
|
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
function renderMeta() {
|
|
57
|
-
return (_jsxs(View, { className: "flex-row items-center justify-between mb-3", children: [_jsx(Display.Date, { className: "text-
|
|
57
|
+
return (_jsxs(View, { className: "flex-row items-center justify-between mb-3", children: [_jsx(Display.Date, { className: "text-text-primary", showTime: true, textVariant: "bodySmall", value: item.createdAt }), _jsx(Display.CopyableText, { label: "Copiar UUID", value: item.uuid })] }));
|
|
58
58
|
}
|
|
59
59
|
function renderBadges() {
|
|
60
60
|
const badges = [];
|
|
61
61
|
const statusVariantMap = {
|
|
62
|
-
"bg-
|
|
63
|
-
"bg-
|
|
64
|
-
"bg-
|
|
65
|
-
"bg-
|
|
62
|
+
"bg-danger-light border-danger": "danger",
|
|
63
|
+
"bg-success-light border-success": "successDark",
|
|
64
|
+
"bg-info-light border-info": "infoDark",
|
|
65
|
+
"bg-bg-muted border-border-default": "warning",
|
|
66
66
|
};
|
|
67
67
|
const variant = statusVariantMap[statusBadge.backgroundClass] || "info";
|
|
68
68
|
badges.push(_jsx(Display.Badge, { className: "self-end", label: statusBadge.label, variant: variant }, "status"));
|
|
@@ -78,13 +78,13 @@ function EventRow({ item, onRetry }) {
|
|
|
78
78
|
if (!showPayload) {
|
|
79
79
|
return null;
|
|
80
80
|
}
|
|
81
|
-
return (_jsxs(View, { className: "bg-
|
|
81
|
+
return (_jsxs(View, { className: "bg-bg-muted rounded-lg p-2 border border-border-default mb-2", children: [_jsx(Typography.Text, { className: "text-text-primary mb-1", semibold: true, variant: "bodySmall", children: "Payload" }), _jsx(JsonPreview, { selectedJson: payloadString })] }));
|
|
82
82
|
}
|
|
83
83
|
function renderActions() {
|
|
84
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
85
|
await Clipboard.setStringAsync(payloadString);
|
|
86
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
87
|
}
|
|
88
|
-
return (_jsxs(View, { className: "bg-
|
|
88
|
+
return (_jsxs(View, { className: "bg-bg-card border border-border-default 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
89
|
}
|
|
90
90
|
export default EventRow;
|