@agent-native/dispatch 0.30.3 → 0.31.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.
- package/dist/actions/provider-api-register.d.ts +5 -5
- package/dist/components/automation-details-panel.d.ts +8 -0
- package/dist/components/automation-details-panel.d.ts.map +1 -0
- package/dist/components/automation-details-panel.js +80 -0
- package/dist/components/automation-details-panel.js.map +1 -0
- package/dist/lib/automations.d.ts +12 -0
- package/dist/lib/automations.d.ts.map +1 -1
- package/dist/lib/automations.js +9 -0
- package/dist/lib/automations.js.map +1 -1
- package/dist/routes/pages/admin.d.ts.map +1 -1
- package/dist/routes/pages/admin.js +6 -1
- package/dist/routes/pages/admin.js.map +1 -1
- package/dist/routes/pages/automations.d.ts.map +1 -1
- package/dist/routes/pages/automations.js +59 -21
- package/dist/routes/pages/automations.js.map +1 -1
- package/dist/routes/pages/chat.d.ts.map +1 -1
- package/dist/routes/pages/chat.js +45 -18
- package/dist/routes/pages/chat.js.map +1 -1
- package/package.json +4 -4
- package/src/components/automation-details-panel.tsx +420 -0
- package/src/lib/automations.spec.ts +19 -1
- package/src/lib/automations.ts +23 -0
- package/src/routes/pages/admin.tsx +8 -1
- package/src/routes/pages/automations.tsx +177 -97
- package/src/routes/pages/chat.spec.tsx +49 -0
- package/src/routes/pages/chat.tsx +51 -0
- package/dist/components/automation-run-history-dialog.d.ts +0 -9
- package/dist/components/automation-run-history-dialog.d.ts.map +0 -1
- package/dist/components/automation-run-history-dialog.js +0 -63
- package/dist/components/automation-run-history-dialog.js.map +0 -1
- package/src/components/automation-run-history-dialog.tsx +0 -247
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
3
|
-
import { IconAlertTriangle, IconClock, IconExternalLink, } from "@tabler/icons-react";
|
|
4
|
-
import { Link } from "react-router";
|
|
5
|
-
import { automationScopeLabel, automationTarget, } from "../lib/automation-display.js";
|
|
6
|
-
import { ActionQueryError } from "./action-query-error.js";
|
|
7
|
-
import { Badge } from "./ui/badge.js";
|
|
8
|
-
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "./ui/dialog.js";
|
|
9
|
-
import { Skeleton } from "./ui/skeleton.js";
|
|
10
|
-
function runStatusVariant(status) {
|
|
11
|
-
if (status === "error")
|
|
12
|
-
return "destructive";
|
|
13
|
-
if (status === "success")
|
|
14
|
-
return "default";
|
|
15
|
-
return "outline";
|
|
16
|
-
}
|
|
17
|
-
function formatTimestamp(value) {
|
|
18
|
-
const date = new Date(value);
|
|
19
|
-
return Number.isNaN(date.getTime()) ? "Unknown time" : date.toLocaleString();
|
|
20
|
-
}
|
|
21
|
-
function formatDuration(run) {
|
|
22
|
-
if (!run.finishedAt)
|
|
23
|
-
return null;
|
|
24
|
-
const seconds = Math.max(0, Math.round((run.finishedAt - run.startedAt) / 1000));
|
|
25
|
-
return `${seconds}s`;
|
|
26
|
-
}
|
|
27
|
-
function runDebugPath(run) {
|
|
28
|
-
const params = new URLSearchParams();
|
|
29
|
-
if (run.runId)
|
|
30
|
-
params.set("runId", run.runId);
|
|
31
|
-
else if (run.threadId)
|
|
32
|
-
params.set("threadId", run.threadId);
|
|
33
|
-
else
|
|
34
|
-
return null;
|
|
35
|
-
return `/admin/thread-debug?${params.toString()}`;
|
|
36
|
-
}
|
|
37
|
-
export function AutomationRunHistoryDialog({ automation, open, onOpenChange, }) {
|
|
38
|
-
const scope = automation?.scope === "organization" ||
|
|
39
|
-
automation?.owner.startsWith("__organization__:") ||
|
|
40
|
-
automation?.owner === "__shared__"
|
|
41
|
-
? "organization"
|
|
42
|
-
: "personal";
|
|
43
|
-
const runsQuery = useActionQuery("list-automation-runs", {
|
|
44
|
-
name: automation?.name ?? "",
|
|
45
|
-
scope,
|
|
46
|
-
appId: automation?.appId ?? "dispatch",
|
|
47
|
-
}, {
|
|
48
|
-
enabled: open && Boolean(automation),
|
|
49
|
-
staleTime: 5_000,
|
|
50
|
-
});
|
|
51
|
-
const runs = runsQuery.data ?? [];
|
|
52
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-h-[85vh] max-w-2xl overflow-y-auto", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: automation?.name ?? "Automation details" }), _jsx(DialogDescription, { children: automation
|
|
53
|
-
? `${automationTarget(automation)} · ${automationScopeLabel(automation)}`
|
|
54
|
-
: "Execution history" })] }), automation ? (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-x-4 gap-y-3 rounded-lg border bg-muted/20 p-3 text-sm", children: [_jsxs("div", { children: [_jsx("div", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: "Status" }), _jsx("div", { className: "mt-1 font-medium", children: automation.lastStatus ??
|
|
55
|
-
(automation.enabled ? "ready" : "paused") })] }), _jsxs("div", { children: [_jsx("div", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: "Last check" }), _jsx("div", { className: "mt-1 font-medium", children: automation.lastCheck
|
|
56
|
-
? formatTimestamp(new Date(automation.lastCheck).getTime())
|
|
57
|
-
: "Not recorded" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: "Created by" }), _jsx("div", { className: "mt-1 break-words font-medium", children: automation.createdBy ?? "Unknown" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: "Timezone" }), _jsx("div", { className: "mt-1 font-medium", children: automation.timezone ?? "Account default" })] })] }), automation.lastError ? (_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 p-3", children: [_jsx(IconAlertTriangle, { className: "mt-0.5 size-4 shrink-0 text-destructive" }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-destructive", children: "Latest scheduler result" }), _jsx("p", { className: "mt-1 break-words text-sm text-muted-foreground", children: automation.lastError })] })] })) : null, _jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Past runs" }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "Only executions that actually started appear here. Blocked or skipped scheduler checks are shown above, not as fake runs." })] }), runs.length > 0 ? (_jsxs(Badge, { variant: "outline", children: [runs.length, " shown"] })) : null] }), _jsx("div", { className: "mt-3", children: runsQuery.isLoading ? (_jsx("div", { className: "space-y-3 rounded-lg border p-4", children: Array.from({ length: 3 }).map((_, index) => (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-4 w-2/3" }), _jsx(Skeleton, { className: "h-3 w-1/2" })] }, index))) })) : runsQuery.isError ? (_jsx(ActionQueryError, { error: runsQuery.error, onRetry: () => void runsQuery.refetch() })) : runs.length === 0 ? (_jsx("div", { className: "rounded-lg border border-dashed p-4 text-sm text-muted-foreground", children: "This automation has not recorded an execution yet. If it is enabled but shows no runs, inspect the latest scheduler result and last check above." })) : (_jsx("ul", { className: "divide-y rounded-lg border", children: runs.map((run) => {
|
|
58
|
-
const debugPath = runDebugPath(run);
|
|
59
|
-
const duration = formatDuration(run);
|
|
60
|
-
return (_jsxs("li", { className: "space-y-2 p-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [_jsx(Badge, { variant: runStatusVariant(run.status), children: run.status }), _jsx("span", { className: "text-muted-foreground", children: formatTimestamp(run.startedAt) }), duration ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(IconClock, { className: "size-3.5" }), duration] })) : null, debugPath ? (_jsxs(Link, { to: debugPath, className: "ml-auto inline-flex items-center gap-1 text-xs font-medium text-foreground underline-offset-4 hover:underline", children: [_jsx(IconExternalLink, { className: "size-3.5" }), "Open debug"] })) : null] }), run.error ? (_jsx("p", { className: "break-words text-sm text-destructive", children: run.error })) : null] }, run.id));
|
|
61
|
-
}) })) })] })] })) : null] }) }));
|
|
62
|
-
}
|
|
63
|
-
//# sourceMappingURL=automation-run-history-dialog.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"automation-run-history-dialog.js","sourceRoot":"","sources":["../../src/components/automation-run-history-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useActionQuery } from \"@agent-native/core/client/hooks\";\nimport {\n IconAlertTriangle,\n IconClock,\n IconExternalLink,\n} from \"@tabler/icons-react\";\nimport { Link } from \"react-router\";\n\nimport {\n automationScopeLabel,\n automationTarget,\n} from \"../lib/automation-display\";\nimport type { DispatchAutomationItem } from \"../lib/automations\";\nimport { ActionQueryError } from \"./action-query-error\";\nimport { Badge } from \"./ui/badge\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from \"./ui/dialog\";\nimport { Skeleton } from \"./ui/skeleton\";\n\ninterface AutomationRun {\n id: string;\n runId: string | null;\n threadId: string | null;\n status: \"running\" | \"success\" | \"error\" | \"interrupted\";\n startedAt: number;\n finishedAt: number | null;\n error: string | null;\n}\n\ninterface AutomationRunHistoryDialogProps {\n automation: DispatchAutomationItem | null;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\nfunction runStatusVariant(\n status: AutomationRun[\"status\"],\n): \"default\" | \"destructive\" | \"outline\" {\n if (status === \"error\") return \"destructive\";\n if (status === \"success\") return \"default\";\n return \"outline\";\n}\n\nfunction formatTimestamp(value: number): string {\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? \"Unknown time\" : date.toLocaleString();\n}\n\nfunction formatDuration(run: AutomationRun): string | null {\n if (!run.finishedAt) return null;\n const seconds = Math.max(\n 0,\n Math.round((run.finishedAt - run.startedAt) / 1000),\n );\n return `${seconds}s`;\n}\n\nfunction runDebugPath(run: AutomationRun): string | null {\n const params = new URLSearchParams();\n if (run.runId) params.set(\"runId\", run.runId);\n else if (run.threadId) params.set(\"threadId\", run.threadId);\n else return null;\n return `/admin/thread-debug?${params.toString()}`;\n}\n\nexport function AutomationRunHistoryDialog({\n automation,\n open,\n onOpenChange,\n}: AutomationRunHistoryDialogProps) {\n const scope =\n automation?.scope === \"organization\" ||\n automation?.owner.startsWith(\"__organization__:\") ||\n automation?.owner === \"__shared__\"\n ? \"organization\"\n : \"personal\";\n const runsQuery = useActionQuery<AutomationRun[]>(\n \"list-automation-runs\",\n {\n name: automation?.name ?? \"\",\n scope,\n appId: automation?.appId ?? \"dispatch\",\n },\n {\n enabled: open && Boolean(automation),\n staleTime: 5_000,\n },\n );\n const runs = runsQuery.data ?? [];\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[85vh] max-w-2xl overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>{automation?.name ?? \"Automation details\"}</DialogTitle>\n <DialogDescription>\n {automation\n ? `${automationTarget(automation)} · ${automationScopeLabel(automation)}`\n : \"Execution history\"}\n </DialogDescription>\n </DialogHeader>\n\n {automation ? (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-x-4 gap-y-3 rounded-lg border bg-muted/20 p-3 text-sm\">\n <div>\n <div className=\"text-[11px] uppercase tracking-wide text-muted-foreground\">\n Status\n </div>\n <div className=\"mt-1 font-medium\">\n {automation.lastStatus ??\n (automation.enabled ? \"ready\" : \"paused\")}\n </div>\n </div>\n <div>\n <div className=\"text-[11px] uppercase tracking-wide text-muted-foreground\">\n Last check\n </div>\n <div className=\"mt-1 font-medium\">\n {automation.lastCheck\n ? formatTimestamp(new Date(automation.lastCheck).getTime())\n : \"Not recorded\"}\n </div>\n </div>\n <div>\n <div className=\"text-[11px] uppercase tracking-wide text-muted-foreground\">\n Created by\n </div>\n <div className=\"mt-1 break-words font-medium\">\n {automation.createdBy ?? \"Unknown\"}\n </div>\n </div>\n <div>\n <div className=\"text-[11px] uppercase tracking-wide text-muted-foreground\">\n Timezone\n </div>\n <div className=\"mt-1 font-medium\">\n {automation.timezone ?? \"Account default\"}\n </div>\n </div>\n </div>\n\n {automation.lastError ? (\n <div className=\"flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 p-3\">\n <IconAlertTriangle className=\"mt-0.5 size-4 shrink-0 text-destructive\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-destructive\">\n Latest scheduler result\n </p>\n <p className=\"mt-1 break-words text-sm text-muted-foreground\">\n {automation.lastError}\n </p>\n </div>\n </div>\n ) : null}\n\n <div>\n <div className=\"flex items-center justify-between gap-3\">\n <div>\n <h3 className=\"text-sm font-semibold text-foreground\">\n Past runs\n </h3>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n Only executions that actually started appear here. Blocked\n or skipped scheduler checks are shown above, not as fake\n runs.\n </p>\n </div>\n {runs.length > 0 ? (\n <Badge variant=\"outline\">{runs.length} shown</Badge>\n ) : null}\n </div>\n\n <div className=\"mt-3\">\n {runsQuery.isLoading ? (\n <div className=\"space-y-3 rounded-lg border p-4\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"space-y-2\">\n <Skeleton className=\"h-4 w-2/3\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n ))}\n </div>\n ) : runsQuery.isError ? (\n <ActionQueryError\n error={runsQuery.error}\n onRetry={() => void runsQuery.refetch()}\n />\n ) : runs.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-4 text-sm text-muted-foreground\">\n This automation has not recorded an execution yet. If it is\n enabled but shows no runs, inspect the latest scheduler\n result and last check above.\n </div>\n ) : (\n <ul className=\"divide-y rounded-lg border\">\n {runs.map((run) => {\n const debugPath = runDebugPath(run);\n const duration = formatDuration(run);\n return (\n <li key={run.id} className=\"space-y-2 p-3\">\n <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n <Badge variant={runStatusVariant(run.status)}>\n {run.status}\n </Badge>\n <span className=\"text-muted-foreground\">\n {formatTimestamp(run.startedAt)}\n </span>\n {duration ? (\n <span className=\"inline-flex items-center gap-1 text-xs text-muted-foreground\">\n <IconClock className=\"size-3.5\" />\n {duration}\n </span>\n ) : null}\n {debugPath ? (\n <Link\n to={debugPath}\n className=\"ml-auto inline-flex items-center gap-1 text-xs font-medium text-foreground underline-offset-4 hover:underline\"\n >\n <IconExternalLink className=\"size-3.5\" />\n Open debug\n </Link>\n ) : null}\n </div>\n {run.error ? (\n <p className=\"break-words text-sm text-destructive\">\n {run.error}\n </p>\n ) : null}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n </div>\n </div>\n ) : null}\n </DialogContent>\n </Dialog>\n );\n}\n"]}
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
2
|
-
import {
|
|
3
|
-
IconAlertTriangle,
|
|
4
|
-
IconClock,
|
|
5
|
-
IconExternalLink,
|
|
6
|
-
} from "@tabler/icons-react";
|
|
7
|
-
import { Link } from "react-router";
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
automationScopeLabel,
|
|
11
|
-
automationTarget,
|
|
12
|
-
} from "../lib/automation-display";
|
|
13
|
-
import type { DispatchAutomationItem } from "../lib/automations";
|
|
14
|
-
import { ActionQueryError } from "./action-query-error";
|
|
15
|
-
import { Badge } from "./ui/badge";
|
|
16
|
-
import {
|
|
17
|
-
Dialog,
|
|
18
|
-
DialogContent,
|
|
19
|
-
DialogDescription,
|
|
20
|
-
DialogHeader,
|
|
21
|
-
DialogTitle,
|
|
22
|
-
} from "./ui/dialog";
|
|
23
|
-
import { Skeleton } from "./ui/skeleton";
|
|
24
|
-
|
|
25
|
-
interface AutomationRun {
|
|
26
|
-
id: string;
|
|
27
|
-
runId: string | null;
|
|
28
|
-
threadId: string | null;
|
|
29
|
-
status: "running" | "success" | "error" | "interrupted";
|
|
30
|
-
startedAt: number;
|
|
31
|
-
finishedAt: number | null;
|
|
32
|
-
error: string | null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
interface AutomationRunHistoryDialogProps {
|
|
36
|
-
automation: DispatchAutomationItem | null;
|
|
37
|
-
open: boolean;
|
|
38
|
-
onOpenChange: (open: boolean) => void;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function runStatusVariant(
|
|
42
|
-
status: AutomationRun["status"],
|
|
43
|
-
): "default" | "destructive" | "outline" {
|
|
44
|
-
if (status === "error") return "destructive";
|
|
45
|
-
if (status === "success") return "default";
|
|
46
|
-
return "outline";
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function formatTimestamp(value: number): string {
|
|
50
|
-
const date = new Date(value);
|
|
51
|
-
return Number.isNaN(date.getTime()) ? "Unknown time" : date.toLocaleString();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function formatDuration(run: AutomationRun): string | null {
|
|
55
|
-
if (!run.finishedAt) return null;
|
|
56
|
-
const seconds = Math.max(
|
|
57
|
-
0,
|
|
58
|
-
Math.round((run.finishedAt - run.startedAt) / 1000),
|
|
59
|
-
);
|
|
60
|
-
return `${seconds}s`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function runDebugPath(run: AutomationRun): string | null {
|
|
64
|
-
const params = new URLSearchParams();
|
|
65
|
-
if (run.runId) params.set("runId", run.runId);
|
|
66
|
-
else if (run.threadId) params.set("threadId", run.threadId);
|
|
67
|
-
else return null;
|
|
68
|
-
return `/admin/thread-debug?${params.toString()}`;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function AutomationRunHistoryDialog({
|
|
72
|
-
automation,
|
|
73
|
-
open,
|
|
74
|
-
onOpenChange,
|
|
75
|
-
}: AutomationRunHistoryDialogProps) {
|
|
76
|
-
const scope =
|
|
77
|
-
automation?.scope === "organization" ||
|
|
78
|
-
automation?.owner.startsWith("__organization__:") ||
|
|
79
|
-
automation?.owner === "__shared__"
|
|
80
|
-
? "organization"
|
|
81
|
-
: "personal";
|
|
82
|
-
const runsQuery = useActionQuery<AutomationRun[]>(
|
|
83
|
-
"list-automation-runs",
|
|
84
|
-
{
|
|
85
|
-
name: automation?.name ?? "",
|
|
86
|
-
scope,
|
|
87
|
-
appId: automation?.appId ?? "dispatch",
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
enabled: open && Boolean(automation),
|
|
91
|
-
staleTime: 5_000,
|
|
92
|
-
},
|
|
93
|
-
);
|
|
94
|
-
const runs = runsQuery.data ?? [];
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
98
|
-
<DialogContent className="max-h-[85vh] max-w-2xl overflow-y-auto">
|
|
99
|
-
<DialogHeader>
|
|
100
|
-
<DialogTitle>{automation?.name ?? "Automation details"}</DialogTitle>
|
|
101
|
-
<DialogDescription>
|
|
102
|
-
{automation
|
|
103
|
-
? `${automationTarget(automation)} · ${automationScopeLabel(automation)}`
|
|
104
|
-
: "Execution history"}
|
|
105
|
-
</DialogDescription>
|
|
106
|
-
</DialogHeader>
|
|
107
|
-
|
|
108
|
-
{automation ? (
|
|
109
|
-
<div className="space-y-4">
|
|
110
|
-
<div className="grid grid-cols-2 gap-x-4 gap-y-3 rounded-lg border bg-muted/20 p-3 text-sm">
|
|
111
|
-
<div>
|
|
112
|
-
<div className="text-[11px] uppercase tracking-wide text-muted-foreground">
|
|
113
|
-
Status
|
|
114
|
-
</div>
|
|
115
|
-
<div className="mt-1 font-medium">
|
|
116
|
-
{automation.lastStatus ??
|
|
117
|
-
(automation.enabled ? "ready" : "paused")}
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
<div>
|
|
121
|
-
<div className="text-[11px] uppercase tracking-wide text-muted-foreground">
|
|
122
|
-
Last check
|
|
123
|
-
</div>
|
|
124
|
-
<div className="mt-1 font-medium">
|
|
125
|
-
{automation.lastCheck
|
|
126
|
-
? formatTimestamp(new Date(automation.lastCheck).getTime())
|
|
127
|
-
: "Not recorded"}
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
<div>
|
|
131
|
-
<div className="text-[11px] uppercase tracking-wide text-muted-foreground">
|
|
132
|
-
Created by
|
|
133
|
-
</div>
|
|
134
|
-
<div className="mt-1 break-words font-medium">
|
|
135
|
-
{automation.createdBy ?? "Unknown"}
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
<div>
|
|
139
|
-
<div className="text-[11px] uppercase tracking-wide text-muted-foreground">
|
|
140
|
-
Timezone
|
|
141
|
-
</div>
|
|
142
|
-
<div className="mt-1 font-medium">
|
|
143
|
-
{automation.timezone ?? "Account default"}
|
|
144
|
-
</div>
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
|
|
148
|
-
{automation.lastError ? (
|
|
149
|
-
<div className="flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 p-3">
|
|
150
|
-
<IconAlertTriangle className="mt-0.5 size-4 shrink-0 text-destructive" />
|
|
151
|
-
<div className="min-w-0">
|
|
152
|
-
<p className="text-sm font-medium text-destructive">
|
|
153
|
-
Latest scheduler result
|
|
154
|
-
</p>
|
|
155
|
-
<p className="mt-1 break-words text-sm text-muted-foreground">
|
|
156
|
-
{automation.lastError}
|
|
157
|
-
</p>
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
) : null}
|
|
161
|
-
|
|
162
|
-
<div>
|
|
163
|
-
<div className="flex items-center justify-between gap-3">
|
|
164
|
-
<div>
|
|
165
|
-
<h3 className="text-sm font-semibold text-foreground">
|
|
166
|
-
Past runs
|
|
167
|
-
</h3>
|
|
168
|
-
<p className="mt-1 text-xs text-muted-foreground">
|
|
169
|
-
Only executions that actually started appear here. Blocked
|
|
170
|
-
or skipped scheduler checks are shown above, not as fake
|
|
171
|
-
runs.
|
|
172
|
-
</p>
|
|
173
|
-
</div>
|
|
174
|
-
{runs.length > 0 ? (
|
|
175
|
-
<Badge variant="outline">{runs.length} shown</Badge>
|
|
176
|
-
) : null}
|
|
177
|
-
</div>
|
|
178
|
-
|
|
179
|
-
<div className="mt-3">
|
|
180
|
-
{runsQuery.isLoading ? (
|
|
181
|
-
<div className="space-y-3 rounded-lg border p-4">
|
|
182
|
-
{Array.from({ length: 3 }).map((_, index) => (
|
|
183
|
-
<div key={index} className="space-y-2">
|
|
184
|
-
<Skeleton className="h-4 w-2/3" />
|
|
185
|
-
<Skeleton className="h-3 w-1/2" />
|
|
186
|
-
</div>
|
|
187
|
-
))}
|
|
188
|
-
</div>
|
|
189
|
-
) : runsQuery.isError ? (
|
|
190
|
-
<ActionQueryError
|
|
191
|
-
error={runsQuery.error}
|
|
192
|
-
onRetry={() => void runsQuery.refetch()}
|
|
193
|
-
/>
|
|
194
|
-
) : runs.length === 0 ? (
|
|
195
|
-
<div className="rounded-lg border border-dashed p-4 text-sm text-muted-foreground">
|
|
196
|
-
This automation has not recorded an execution yet. If it is
|
|
197
|
-
enabled but shows no runs, inspect the latest scheduler
|
|
198
|
-
result and last check above.
|
|
199
|
-
</div>
|
|
200
|
-
) : (
|
|
201
|
-
<ul className="divide-y rounded-lg border">
|
|
202
|
-
{runs.map((run) => {
|
|
203
|
-
const debugPath = runDebugPath(run);
|
|
204
|
-
const duration = formatDuration(run);
|
|
205
|
-
return (
|
|
206
|
-
<li key={run.id} className="space-y-2 p-3">
|
|
207
|
-
<div className="flex flex-wrap items-center gap-2 text-sm">
|
|
208
|
-
<Badge variant={runStatusVariant(run.status)}>
|
|
209
|
-
{run.status}
|
|
210
|
-
</Badge>
|
|
211
|
-
<span className="text-muted-foreground">
|
|
212
|
-
{formatTimestamp(run.startedAt)}
|
|
213
|
-
</span>
|
|
214
|
-
{duration ? (
|
|
215
|
-
<span className="inline-flex items-center gap-1 text-xs text-muted-foreground">
|
|
216
|
-
<IconClock className="size-3.5" />
|
|
217
|
-
{duration}
|
|
218
|
-
</span>
|
|
219
|
-
) : null}
|
|
220
|
-
{debugPath ? (
|
|
221
|
-
<Link
|
|
222
|
-
to={debugPath}
|
|
223
|
-
className="ml-auto inline-flex items-center gap-1 text-xs font-medium text-foreground underline-offset-4 hover:underline"
|
|
224
|
-
>
|
|
225
|
-
<IconExternalLink className="size-3.5" />
|
|
226
|
-
Open debug
|
|
227
|
-
</Link>
|
|
228
|
-
) : null}
|
|
229
|
-
</div>
|
|
230
|
-
{run.error ? (
|
|
231
|
-
<p className="break-words text-sm text-destructive">
|
|
232
|
-
{run.error}
|
|
233
|
-
</p>
|
|
234
|
-
) : null}
|
|
235
|
-
</li>
|
|
236
|
-
);
|
|
237
|
-
})}
|
|
238
|
-
</ul>
|
|
239
|
-
)}
|
|
240
|
-
</div>
|
|
241
|
-
</div>
|
|
242
|
-
</div>
|
|
243
|
-
) : null}
|
|
244
|
-
</DialogContent>
|
|
245
|
-
</Dialog>
|
|
246
|
-
);
|
|
247
|
-
}
|