@sentry/junior-dashboard 0.265.0 → 0.267.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/app.js +49 -12
- package/dist/client.js +94 -63
- package/dist/index.js +49 -12
- package/dist/tailwind.css +1 -1
- package/package.json +11 -5
- package/src/app.ts +0 -2
- package/src/auth.ts +4 -7
- package/src/client/App.tsx +35 -53
- package/src/client/components/ActorAvatar.tsx +10 -22
- package/src/client/components/ConnectionBanner.tsx +4 -3
- package/src/client/components/CopyButton.tsx +3 -4
- package/src/client/components/DetailsPanel.tsx +12 -13
- package/src/client/components/Drawer.tsx +46 -23
- package/src/client/components/EmptyTelemetry.tsx +5 -3
- package/src/client/components/ImageAttachment.tsx +49 -63
- package/src/client/components/InlineError.tsx +8 -4
- package/src/client/components/Notice.tsx +19 -26
- package/src/client/components/Pagination.tsx +26 -26
- package/src/client/components/ParticipantAvatarStack.tsx +13 -10
- package/src/client/components/README.md +28 -3
- package/src/client/components/SearchInput.tsx +51 -41
- package/src/client/components/VersionDriftBanner.tsx +9 -7
- package/src/client/components/charts/CacheWrites.tsx +52 -43
- package/src/client/components/ui/alert.tsx +64 -0
- package/src/client/components/ui/avatar.tsx +53 -0
- package/src/client/components/ui/button-group.tsx +59 -0
- package/src/client/components/ui/button.tsx +2 -1
- package/src/client/components/ui/dialog.tsx +60 -0
- package/src/client/components/ui/drawer.tsx +140 -0
- package/src/client/components/ui/empty.tsx +61 -0
- package/src/client/components/ui/input-group.tsx +96 -0
- package/src/client/components/ui/pagination.tsx +60 -0
- package/src/client/components/ui/separator.tsx +22 -0
- package/src/client/components/ui/spinner.tsx +21 -0
- package/src/client/components/ui/table.tsx +22 -1
- package/src/client/components/ui/tooltip.tsx +1 -1
- package/src/client/components/ui/useSheet.ts +5 -1
- package/src/client/conversations/ComposerImages.tsx +66 -14
- package/src/client/conversations/ConversationChildAgents.tsx +5 -5
- package/src/client/conversations/ConversationComposer.tsx +84 -12
- package/src/client/conversations/ConversationDetails.tsx +3 -27
- package/src/client/conversations/ConversationEventDetails.tsx +26 -10
- package/src/client/conversations/ConversationHeader.tsx +5 -4
- package/src/client/conversations/ConversationHeaderActions.tsx +7 -11
- package/src/client/conversations/ConversationPage.tsx +156 -2
- package/src/client/conversations/ConversationSidebar.tsx +5 -4
- package/src/client/conversations/ConversationTranscript.tsx +48 -16
- package/src/client/conversations/ConversationUsageBreakdown.tsx +108 -65
- package/src/client/conversations/ConversationWatches.tsx +67 -59
- package/src/client/conversations/DetailsList.tsx +77 -29
- package/src/client/conversations/EventDetails.tsx +46 -19
- package/src/client/conversations/EventUsage.tsx +99 -74
- package/src/client/conversations/ForkConversationButton.tsx +79 -73
- package/src/client/conversations/PendingMailboxStack.tsx +312 -119
- package/src/client/conversations/README.md +30 -10
- package/src/client/conversations/ShareConversationButton.tsx +3 -4
- package/src/client/conversations/SubagentTranscriptDrawer.tsx +19 -22
- package/src/client/conversations/TranscriptActivityGroup.tsx +22 -19
- package/src/client/conversations/TranscriptAttachment.tsx +39 -4
- package/src/client/conversations/TranscriptAutomationTriggerView.tsx +14 -19
- package/src/client/conversations/TranscriptEventView.tsx +35 -16
- package/src/client/conversations/TranscriptFailureView.tsx +10 -13
- package/src/client/conversations/TranscriptMarkdown.tsx +232 -436
- package/src/client/conversations/TranscriptMessageContextView.tsx +31 -20
- package/src/client/conversations/TranscriptMessageView.tsx +7 -1
- package/src/client/conversations/TranscriptStructuredEventView.tsx +25 -15
- package/src/client/conversations/TranscriptSummary.tsx +13 -7
- package/src/client/conversations/TranscriptTurnContextView.tsx +78 -56
- package/src/client/conversations/TranscriptView.tsx +7 -3
- package/src/client/conversations/UserInputRequestCard.tsx +244 -0
- package/src/client/conversations/conversationOutbox.ts +46 -0
- package/src/client/conversations/eventLog.ts +1 -0
- package/src/client/conversations/eventTime.ts +13 -1
- package/src/client/conversations/eventTranscript.ts +12 -5
- package/src/client/conversations/queries.ts +127 -26
- package/src/client/conversations/transcriptMarkdownLinks.ts +5 -367
- package/src/client/conversations/transcriptMentions.tsx +18 -5
- package/src/client/conversations/userInputRequest.ts +56 -0
- package/src/client/format.ts +10 -3
- package/src/client/pages/automations/{AutomationLifecycle.tsx → AutomationActions.tsx} +60 -69
- package/src/client/pages/automations/AutomationDraftChanges.tsx +171 -0
- package/src/client/pages/automations/AutomationEditor.tsx +182 -156
- package/src/client/pages/automations/AutomationEventFields.tsx +119 -45
- package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +1 -1
- package/src/client/pages/automations/AutomationFormSection.tsx +20 -2
- package/src/client/pages/automations/AutomationOutcomeFields.tsx +1 -1
- package/src/client/pages/automations/AutomationOverview.tsx +238 -0
- package/src/client/pages/automations/AutomationPage.tsx +343 -0
- package/src/client/pages/automations/AutomationRecentRuns.tsx +3 -2
- package/src/client/pages/automations/AutomationRow.tsx +11 -13
- package/src/client/pages/automations/AutomationRunDetails.tsx +4 -2
- package/src/client/pages/automations/{AutomationExecutionsPage.tsx → AutomationRunsTab.tsx} +94 -103
- package/src/client/pages/automations/AutomationStatus.tsx +164 -108
- package/src/client/pages/automations/AutomationVersionsTab.tsx +518 -0
- package/src/client/pages/automations/AutomationsPage.tsx +10 -98
- package/src/client/pages/automations/AutomationsPageLayout.tsx +5 -23
- package/src/client/pages/automations/README.md +56 -46
- package/src/client/types.ts +3 -0
- package/src/mock-reporting/fixtures.ts +44 -2
- package/src/mock-reporting/watches.ts +1 -1
- package/src/client/conversations/transcriptStyles.ts +0 -10
- package/src/client/pages/automations/AutomationDetails.tsx +0 -193
- package/src/client/pages/automations/AutomationEditPage.tsx +0 -176
- package/src/client/pages/automations/AutomationVersionsPage.tsx +0 -390
|
@@ -5,7 +5,7 @@ import { Link } from "react-router";
|
|
|
5
5
|
import { ActorAvatar } from "../../components/ActorAvatar";
|
|
6
6
|
import { SelectableRow } from "../../components/SelectableRow";
|
|
7
7
|
import { StatusChip } from "../../components/StatusChip";
|
|
8
|
-
import { formatRelativeTime, peoplePath } from "../../format";
|
|
8
|
+
import { automationPath, formatRelativeTime, peoplePath } from "../../format";
|
|
9
9
|
import { eventTriggerText } from "@sentry/junior-plugin-api";
|
|
10
10
|
import {
|
|
11
11
|
automationScheduleLabel,
|
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Show one automation as a compact card: what it is, where it posts, who owns
|
|
17
|
-
* it, and when it runs. Instructions, timezones, and event filters stay
|
|
18
|
-
* the
|
|
17
|
+
* it, and when it runs. Instructions, timezones, and event filters stay on
|
|
18
|
+
* the Automation page. The title links to that page.
|
|
19
19
|
*/
|
|
20
20
|
export function AutomationRow(props: {
|
|
21
21
|
onSelect(): void;
|
|
22
|
-
selected: boolean;
|
|
23
22
|
automation: AutomationSummary;
|
|
23
|
+
to: string;
|
|
24
24
|
}) {
|
|
25
25
|
const { automation } = props;
|
|
26
26
|
const status = automationStatus(automation);
|
|
@@ -43,15 +43,13 @@ export function AutomationRow(props: {
|
|
|
43
43
|
<SelectableRow
|
|
44
44
|
className="flex items-start gap-3 rounded-lg px-4 py-3.5"
|
|
45
45
|
onSelect={props.onSelect}
|
|
46
|
-
selected={
|
|
46
|
+
selected={false}
|
|
47
47
|
>
|
|
48
48
|
<div className="min-w-0 flex-1">
|
|
49
|
-
<
|
|
50
|
-
aria-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
onClick={props.onSelect}
|
|
54
|
-
type="button"
|
|
49
|
+
<Link
|
|
50
|
+
aria-label={`Open automation: ${automation.title}`}
|
|
51
|
+
className="block w-full min-w-0 rounded-md text-left no-underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
|
|
52
|
+
to={props.to}
|
|
55
53
|
>
|
|
56
54
|
<span className="flex min-w-0 items-center gap-2">
|
|
57
55
|
<span
|
|
@@ -75,7 +73,7 @@ export function AutomationRow(props: {
|
|
|
75
73
|
) : null}
|
|
76
74
|
</span>
|
|
77
75
|
<AutomationSummaryLine automation={automation} />
|
|
78
|
-
</
|
|
76
|
+
</Link>
|
|
79
77
|
<div className="mt-2.5 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs text-dashboard-text-muted">
|
|
80
78
|
{automation.createdByEmail ? (
|
|
81
79
|
<Link
|
|
@@ -187,7 +185,7 @@ function AutomationTiming({ automation }: { automation: AutomationSummary }) {
|
|
|
187
185
|
{lastRunFailed ? (
|
|
188
186
|
<Link
|
|
189
187
|
className="text-amber-300 underline"
|
|
190
|
-
to={
|
|
188
|
+
to={automationPath(automation.id, "runs")}
|
|
191
189
|
>
|
|
192
190
|
Last run {automation.lastRunStatus}
|
|
193
191
|
{automation.lastRunAt
|
|
@@ -186,7 +186,6 @@ function AutomationRunContent(props: {
|
|
|
186
186
|
showAutomation: boolean;
|
|
187
187
|
}) {
|
|
188
188
|
const { run } = props;
|
|
189
|
-
const versionsPath = `${automationPath(run.automationId)}/versions`;
|
|
190
189
|
return (
|
|
191
190
|
<div className="grid min-w-0 gap-3">
|
|
192
191
|
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 px-1">
|
|
@@ -255,7 +254,10 @@ function AutomationRunContent(props: {
|
|
|
255
254
|
) : null}
|
|
256
255
|
{run.automationVersion ? (
|
|
257
256
|
<RunFact label="Version">
|
|
258
|
-
<Link
|
|
257
|
+
<Link
|
|
258
|
+
className={dashboardInlineLinkClass}
|
|
259
|
+
to={`${automationPath(run.automationId, "versions")}?version=${run.automationVersion}`}
|
|
260
|
+
>
|
|
259
261
|
Version {run.automationVersion}
|
|
260
262
|
</Link>
|
|
261
263
|
</RunFact>
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { useMemo,
|
|
2
|
-
import { ArrowLeft } from "lucide-react";
|
|
3
|
-
import { Link, Navigate, useParams, useSearchParams } from "react-router";
|
|
1
|
+
import { useMemo, type KeyboardEvent, type ReactNode } from "react";
|
|
4
2
|
import type {
|
|
5
3
|
AutomationExecution,
|
|
6
4
|
AutomationExecutionList,
|
|
7
5
|
AutomationRun,
|
|
6
|
+
AutomationSummary,
|
|
8
7
|
} from "@sentry/junior/api/schema";
|
|
9
8
|
|
|
10
9
|
import { useAutomationExecutionsData } from "../../api";
|
|
10
|
+
import { Button } from "../../components/ui/button";
|
|
11
|
+
import { FilterTabList } from "../../components/FilterBar";
|
|
11
12
|
import { InlineError } from "../../components/InlineError";
|
|
12
13
|
import { PageContentSkeleton } from "../../components/PageContentSkeleton";
|
|
13
14
|
import { SelectableRow } from "../../components/SelectableRow";
|
|
@@ -18,19 +19,15 @@ import {
|
|
|
18
19
|
type TimeRangeDays,
|
|
19
20
|
} from "../../components/controls/TimeRangeSelector";
|
|
20
21
|
import { Card } from "../../components/layout/Card";
|
|
21
|
-
import { PageHeader } from "../../components/layout/PageHeader";
|
|
22
22
|
import {
|
|
23
23
|
formatCompactNumber,
|
|
24
24
|
formatCostSummary,
|
|
25
25
|
formatRuntime,
|
|
26
|
-
automationPath,
|
|
27
26
|
} from "../../format";
|
|
28
|
-
import {
|
|
29
|
-
import { pathWithSearch } from "../../searchParams";
|
|
27
|
+
import { useSearchParamEnum } from "../../searchParams";
|
|
30
28
|
import { cn } from "../../styles";
|
|
31
29
|
import { AutomationExecutionStatusChart } from "./AutomationExecutionStatusChart";
|
|
32
30
|
import {
|
|
33
|
-
AUTOMATION_RUN_PARAM,
|
|
34
31
|
AutomationRunDetails,
|
|
35
32
|
AutomationRunsFrame,
|
|
36
33
|
RunConversationLink,
|
|
@@ -42,139 +39,109 @@ import {
|
|
|
42
39
|
formatAutomationRunTime,
|
|
43
40
|
} from "./automationFormat";
|
|
44
41
|
|
|
45
|
-
/**
|
|
42
|
+
/** Search param that filters the Runs tab by result. */
|
|
43
|
+
export const AUTOMATION_RESULT_PARAM = "result";
|
|
44
|
+
|
|
45
|
+
const RESULT_OPTIONS = ["", "completed", "failed", "blocked"] as const;
|
|
46
|
+
|
|
47
|
+
/** Leading run column flexes; metric columns stay equal fixed widths. */
|
|
46
48
|
const EXECUTION_GRID =
|
|
47
49
|
"grid-cols-[minmax(0,1fr)_5.5rem_5.5rem_5.5rem_auto_2rem]";
|
|
48
50
|
|
|
49
|
-
/** Render one
|
|
50
|
-
export function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
/** Render one Automation's terminal runs beside the selected run. */
|
|
52
|
+
export function AutomationRunsTab(props: {
|
|
53
|
+
automation: AutomationSummary;
|
|
54
|
+
header: ReactNode;
|
|
55
|
+
range: TimeRangeDays;
|
|
56
|
+
}) {
|
|
57
|
+
const { automation } = props;
|
|
58
|
+
const query = useAutomationExecutionsData(true, automation.id);
|
|
59
|
+
const [result, setResult] = useSearchParamEnum(
|
|
60
|
+
AUTOMATION_RESULT_PARAM,
|
|
61
|
+
"",
|
|
62
|
+
RESULT_OPTIONS,
|
|
63
|
+
);
|
|
64
|
+
const executions = useMemo(
|
|
65
|
+
() =>
|
|
66
|
+
(query.data?.executions ?? []).filter(
|
|
67
|
+
(execution) => !result || execution.status === result,
|
|
68
|
+
),
|
|
69
|
+
[query.data, result],
|
|
59
70
|
);
|
|
71
|
+
// Details look up every loaded run, so a filter never hides the open run.
|
|
60
72
|
const runs = useMemo(
|
|
61
73
|
() =>
|
|
62
|
-
query.data
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
: [],
|
|
72
|
-
[query.data],
|
|
74
|
+
(query.data?.executions ?? []).map(
|
|
75
|
+
(execution): AutomationRun => ({
|
|
76
|
+
...execution,
|
|
77
|
+
automationId: automation.id,
|
|
78
|
+
automationTitle: automation.title,
|
|
79
|
+
kind: automation.kind,
|
|
80
|
+
}),
|
|
81
|
+
),
|
|
82
|
+
[automation, query.data],
|
|
73
83
|
);
|
|
74
84
|
const selection = useAutomationRunSelection(
|
|
75
|
-
|
|
85
|
+
executions.map((execution) => execution.executionId),
|
|
76
86
|
);
|
|
77
|
-
|
|
78
|
-
if (!automationId) {
|
|
79
|
-
return <Navigate replace to="/automations" />;
|
|
80
|
-
}
|
|
81
|
-
const loading = !query.data && !query.error;
|
|
82
87
|
return (
|
|
83
88
|
<AutomationRunsFrame
|
|
84
89
|
details={
|
|
85
90
|
selection.runId ? (
|
|
86
91
|
<AutomationRunDetails
|
|
87
92
|
key={selection.runId}
|
|
88
|
-
loading={
|
|
93
|
+
loading={!query.data && !query.error}
|
|
89
94
|
onClose={selection.close}
|
|
90
95
|
run={runs.find((run) => run.executionId === selection.runId)}
|
|
91
96
|
showAutomation={false}
|
|
92
97
|
/>
|
|
93
98
|
) : null
|
|
94
99
|
}
|
|
95
|
-
label="
|
|
100
|
+
label="Runs list"
|
|
96
101
|
listRef={selection.listRef}
|
|
97
102
|
>
|
|
103
|
+
{props.header}
|
|
98
104
|
{query.data ? (
|
|
99
|
-
<
|
|
100
|
-
backTo={backTo}
|
|
105
|
+
<AutomationRunsView
|
|
101
106
|
data={query.data}
|
|
107
|
+
executions={executions}
|
|
108
|
+
onResultChange={setResult}
|
|
102
109
|
onRowsKeyDown={selection.onRowsKeyDown}
|
|
103
110
|
onSelect={selection.toggle}
|
|
111
|
+
range={props.range}
|
|
112
|
+
result={result}
|
|
104
113
|
selectedRunId={selection.runId}
|
|
105
114
|
/>
|
|
115
|
+
) : query.error ? (
|
|
116
|
+
<InlineError>
|
|
117
|
+
Runs could not be loaded.{" "}
|
|
118
|
+
<Button onClick={() => void query.refetch()}>Try again</Button>
|
|
119
|
+
</InlineError>
|
|
106
120
|
) : (
|
|
107
|
-
|
|
108
|
-
<PageHeader
|
|
109
|
-
description="Terminal runs for one scheduled or event automation."
|
|
110
|
-
title="Automation executions"
|
|
111
|
-
/>
|
|
112
|
-
{loading ? (
|
|
113
|
-
<PageContentSkeleton
|
|
114
|
-
label="Loading automation executions"
|
|
115
|
-
variant="list"
|
|
116
|
-
/>
|
|
117
|
-
) : (
|
|
118
|
-
<Card padding="md">
|
|
119
|
-
<InlineError>
|
|
120
|
-
{query.error instanceof DashboardApiError &&
|
|
121
|
-
query.error.status === 404
|
|
122
|
-
? "This automation was not found or is not visible to you."
|
|
123
|
-
: "Automation executions could not be loaded. Try again."}
|
|
124
|
-
</InlineError>
|
|
125
|
-
<Link
|
|
126
|
-
className="mt-3 inline-flex items-center gap-2 font-sans text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
|
|
127
|
-
to={automationPath(automationId)}
|
|
128
|
-
>
|
|
129
|
-
<ArrowLeft aria-hidden="true" size={14} />
|
|
130
|
-
Back to automations
|
|
131
|
-
</Link>
|
|
132
|
-
</Card>
|
|
133
|
-
)}
|
|
134
|
-
</>
|
|
121
|
+
<PageContentSkeleton label="Loading runs" variant="list" />
|
|
135
122
|
)}
|
|
136
123
|
</AutomationRunsFrame>
|
|
137
124
|
);
|
|
138
125
|
}
|
|
139
126
|
|
|
140
|
-
function
|
|
141
|
-
backTo: string;
|
|
127
|
+
function AutomationRunsView(props: {
|
|
142
128
|
data: AutomationExecutionList;
|
|
129
|
+
executions: AutomationExecution[];
|
|
130
|
+
onResultChange(value: (typeof RESULT_OPTIONS)[number]): void;
|
|
143
131
|
onRowsKeyDown(event: KeyboardEvent<HTMLElement>): void;
|
|
144
132
|
onSelect(executionId: string): void;
|
|
133
|
+
range: TimeRangeDays;
|
|
134
|
+
result: (typeof RESULT_OPTIONS)[number];
|
|
145
135
|
selectedRunId: string | undefined;
|
|
146
136
|
}) {
|
|
147
|
-
const { data } = props;
|
|
148
|
-
const [range, setRange] = useState<TimeRangeDays>(30);
|
|
137
|
+
const { data, range } = props;
|
|
149
138
|
const counts = useMemo(
|
|
150
139
|
() => countByStatus(data.executions),
|
|
151
140
|
[data.executions],
|
|
152
141
|
);
|
|
153
|
-
const statusCounts = `${counts.completed} completed · ${counts.failed} failed · ${counts.blocked} blocked`;
|
|
154
|
-
const statusSummary = data.truncated
|
|
155
|
-
? `${data.automation.totalRuns} total · loaded runs: ${statusCounts}`
|
|
156
|
-
: `${data.automation.totalRuns} total · ${statusCounts}`;
|
|
157
142
|
|
|
158
143
|
return (
|
|
159
144
|
<>
|
|
160
|
-
<div>
|
|
161
|
-
<Link
|
|
162
|
-
className="mb-3 inline-flex items-center gap-2 font-sans text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
|
|
163
|
-
to={props.backTo}
|
|
164
|
-
>
|
|
165
|
-
<ArrowLeft aria-hidden="true" size={14} />
|
|
166
|
-
Back to automation
|
|
167
|
-
</Link>
|
|
168
|
-
<PageHeader
|
|
169
|
-
description={`${data.automation.kind} automation · ${data.automation.destination.label} · ${statusSummary}`}
|
|
170
|
-
{...(data.executionDays.length > 0
|
|
171
|
-
? { onRangeChange: setRange, range }
|
|
172
|
-
: {})}
|
|
173
|
-
documentTitle={`${data.automation.title} · Executions`}
|
|
174
|
-
title={data.automation.title}
|
|
175
|
-
/>
|
|
176
|
-
</div>
|
|
177
|
-
|
|
178
145
|
{data.executionDays.length > 0 ? (
|
|
179
146
|
<AutomationExecutionStatusChart
|
|
180
147
|
bucketUnit={timeRangeBucketUnit(range)}
|
|
@@ -194,18 +161,31 @@ function AutomationExecutionsView(props: {
|
|
|
194
161
|
/>
|
|
195
162
|
) : null}
|
|
196
163
|
|
|
197
|
-
<div className="flex flex-wrap items-
|
|
198
|
-
<
|
|
199
|
-
|
|
200
|
-
{
|
|
201
|
-
|
|
164
|
+
<div className="flex flex-wrap items-end justify-between gap-x-5 gap-y-3">
|
|
165
|
+
<FilterTabList
|
|
166
|
+
ariaLabel="Run result"
|
|
167
|
+
items={[
|
|
168
|
+
{ count: data.executions.length, label: "All", value: "" },
|
|
169
|
+
{ count: counts.completed, label: "Completed", value: "completed" },
|
|
170
|
+
{ count: counts.failed, label: "Failed", value: "failed" },
|
|
171
|
+
{ count: counts.blocked, label: "Blocked", value: "blocked" },
|
|
172
|
+
]}
|
|
173
|
+
onChange={(value) =>
|
|
174
|
+
props.onResultChange(value as (typeof RESULT_OPTIONS)[number])
|
|
175
|
+
}
|
|
176
|
+
value={props.result}
|
|
177
|
+
/>
|
|
178
|
+
<p className="m-0 text-xs text-dashboard-text-muted">
|
|
179
|
+
{data.automation.totalRuns} total · newest first
|
|
202
180
|
</p>
|
|
203
181
|
</div>
|
|
204
182
|
|
|
205
|
-
{
|
|
183
|
+
{props.executions.length === 0 ? (
|
|
206
184
|
<Card padding="md">
|
|
207
185
|
<p className="m-0 text-sm text-dashboard-text-muted">
|
|
208
|
-
|
|
186
|
+
{data.executions.length === 0
|
|
187
|
+
? "This automation has not run yet."
|
|
188
|
+
: `No ${props.result} runs in the loaded history.`}
|
|
209
189
|
</p>
|
|
210
190
|
</Card>
|
|
211
191
|
) : (
|
|
@@ -229,7 +209,7 @@ function AutomationExecutionsView(props: {
|
|
|
229
209
|
onKeyDown={props.onRowsKeyDown}
|
|
230
210
|
role="list"
|
|
231
211
|
>
|
|
232
|
-
{
|
|
212
|
+
{props.executions.map((execution) => (
|
|
233
213
|
<ExecutionRow
|
|
234
214
|
execution={execution}
|
|
235
215
|
fallbackTitle={data.automation.title}
|
|
@@ -246,7 +226,7 @@ function AutomationExecutionsView(props: {
|
|
|
246
226
|
|
|
247
227
|
{data.truncated ? (
|
|
248
228
|
<p className="m-0 text-center text-xs text-dashboard-text-muted">
|
|
249
|
-
Showing the 100 most recent
|
|
229
|
+
Showing the 100 most recent runs.
|
|
250
230
|
</p>
|
|
251
231
|
) : null}
|
|
252
232
|
</>
|
|
@@ -305,7 +285,18 @@ function ExecutionRow(props: {
|
|
|
305
285
|
</span>
|
|
306
286
|
<span className="mt-1 block truncate text-xs leading-relaxed text-dashboard-text-muted">
|
|
307
287
|
{formatAutomationRunTime(execution.executedAt)}
|
|
288
|
+
{execution.automationVersion
|
|
289
|
+
? ` · Version ${execution.automationVersion}`
|
|
290
|
+
: null}
|
|
308
291
|
</span>
|
|
292
|
+
{execution.error ? (
|
|
293
|
+
<span
|
|
294
|
+
className="mt-1 block truncate text-xs leading-relaxed text-rose-300"
|
|
295
|
+
title={execution.error}
|
|
296
|
+
>
|
|
297
|
+
{execution.error}
|
|
298
|
+
</span>
|
|
299
|
+
) : null}
|
|
309
300
|
</button>
|
|
310
301
|
<MetricCell label="Duration" value={durationLabel} />
|
|
311
302
|
<MetricCell label="Tokens" value={tokensLabel} />
|