@sentry/junior-dashboard 0.266.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/DetailsPanel.tsx +12 -13
- package/src/client/components/Drawer.tsx +46 -23
- package/src/client/components/ImageAttachment.tsx +1 -1
- package/src/client/components/ParticipantAvatarStack.tsx +4 -1
- package/src/client/components/README.md +11 -2
- package/src/client/components/ui/alert.tsx +1 -1
- package/src/client/components/ui/button-group.tsx +59 -0
- package/src/client/components/ui/dialog.tsx +29 -35
- package/src/client/components/ui/drawer.tsx +140 -0
- package/src/client/components/ui/empty.tsx +30 -1
- package/src/client/components/ui/separator.tsx +22 -0
- package/src/client/components/ui/useSheet.ts +5 -1
- package/src/client/conversations/ComposerImages.tsx +66 -14
- 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/ConversationPage.tsx +156 -2
- package/src/client/conversations/ConversationTranscript.tsx +48 -16
- package/src/client/conversations/DetailsList.tsx +33 -0
- package/src/client/conversations/EventDetails.tsx +46 -19
- package/src/client/conversations/ForkConversationButton.tsx +79 -73
- package/src/client/conversations/PendingMailboxStack.tsx +310 -112
- package/src/client/conversations/README.md +30 -13
- 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/TranscriptFailureView.tsx +10 -13
- package/src/client/conversations/TranscriptMarkdown.tsx +232 -436
- package/src/client/conversations/TranscriptMessageView.tsx +7 -1
- package/src/client/conversations/TranscriptSummary.tsx +5 -31
- package/src/client/conversations/TranscriptTurnContextView.tsx +33 -18
- 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/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/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/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
|
@@ -9,40 +9,22 @@ import { PageHeader } from "../../components/layout/PageHeader";
|
|
|
9
9
|
import { PageLayout } from "../../components/layout/PageLayout";
|
|
10
10
|
import { SecondaryNavigation } from "../../components/layout/SecondaryNavigation";
|
|
11
11
|
|
|
12
|
-
/** Identify
|
|
13
|
-
function isAutomationListPath(pathname: string): boolean {
|
|
14
|
-
const match = matchPath("/automations/:automationId", pathname);
|
|
15
|
-
return Boolean(match && match.params.automationId?.toLowerCase() !== "runs");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Identify Automation routes whose list scrolls on its own beside details. */
|
|
12
|
+
/** Identify Automation routes whose run list scrolls on its own beside run details. */
|
|
19
13
|
export function hasAutomationDetailsFrame(pathname: string): boolean {
|
|
20
14
|
return (
|
|
21
|
-
isAutomationListPath(pathname) ||
|
|
22
15
|
Boolean(matchPath("/automations/runs", pathname)) ||
|
|
23
|
-
Boolean(matchPath("/automations/:automationId/
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function isAutomationListTabPath(pathname: string): boolean {
|
|
28
|
-
return (
|
|
29
|
-
isAutomationListPath(pathname) ||
|
|
30
|
-
Boolean(matchPath("/automations/:automationId/edit", pathname))
|
|
16
|
+
Boolean(matchPath("/automations/:automationId/runs", pathname))
|
|
31
17
|
);
|
|
32
18
|
}
|
|
33
19
|
|
|
34
20
|
const taskNavigationItems = [
|
|
35
21
|
{ end: true, label: "Overview", to: "/automations" },
|
|
36
|
-
{
|
|
37
|
-
isActive: isAutomationListTabPath,
|
|
38
|
-
label: "Automations",
|
|
39
|
-
to: "/automations/list",
|
|
40
|
-
},
|
|
22
|
+
{ label: "Automations", to: "/automations/list" },
|
|
41
23
|
{ label: "Runs", to: "/automations/runs" },
|
|
42
24
|
];
|
|
43
25
|
|
|
44
|
-
/** Keep Automation navigation consistent across
|
|
45
|
-
|
|
26
|
+
/** Keep Automation navigation consistent across the overview, list, and runs. */
|
|
27
|
+
function AutomationsNavigation(props: {
|
|
46
28
|
children: ReactNode;
|
|
47
29
|
className?: string;
|
|
48
30
|
}) {
|
|
@@ -4,7 +4,7 @@ Core owns validation, edit access, creator-only rules, revision checks,
|
|
|
4
4
|
Schedule previews, and the Event catalog. Preview requests cannot save or run
|
|
5
5
|
work. Completed scheduled Automations remain read-only.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Creators and public Destination readers can edit. For a public reader, the
|
|
8
8
|
editor shows outcomes as read-only and blocks turning on creator credential
|
|
9
9
|
use. It warns that an instruction or event selector change switches to system
|
|
10
10
|
credentials. Pause and resume stay creator-only.
|
|
@@ -18,7 +18,8 @@ can keep their changed fields or load the latest saved version. Unchanged fields
|
|
|
18
18
|
come from that version; core checks its revision again on save.
|
|
19
19
|
|
|
20
20
|
The data router blocks navigation with unsaved changes. Browser unload uses the
|
|
21
|
-
native prompt. Save and Cancel
|
|
21
|
+
native prompt. Save and Cancel return to the Automation page and keep its query
|
|
22
|
+
string.
|
|
22
23
|
|
|
23
24
|
Version history lists saved versions, newest first, and marks the active
|
|
24
25
|
version. "Make active" uses the native confirm dialog, like delete. It then
|
|
@@ -29,36 +30,36 @@ shows its error when a public reader cannot make a version active.
|
|
|
29
30
|
Browser behavior lives in `e2e/automation-editor.spec.ts`. API and persistence
|
|
30
31
|
coverage lives in core's `tests/integration/api/automation-edits.test.ts`.
|
|
31
32
|
|
|
32
|
-
## Automation
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
`
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
33
|
+
## Automation page
|
|
34
|
+
|
|
35
|
+
Each Automation has one page at `/automations/:id`. Overview, Runs, and
|
|
36
|
+
Versions are tabs. They use `SecondaryNavigation`, like the repository page.
|
|
37
|
+
`/automations/:id/edit` is edit mode of the Overview tab. The old
|
|
38
|
+
`/executions` route redirects to `/runs` and keeps the query string. List rows
|
|
39
|
+
link to the page. There is no details slideout.
|
|
40
|
+
|
|
41
|
+
The page keeps the list query string. "All automations" returns to the same
|
|
42
|
+
list page and filters. The back link drops params that belong to one tab:
|
|
43
|
+
`result`, `run`, and `version`. One `range` param drives the Overview numbers
|
|
44
|
+
and the Runs chart.
|
|
45
|
+
|
|
46
|
+
The header shows the name, the state, the trigger, the Destination, and the
|
|
47
|
+
creator. `AutomationStateChip` is a live region, so Pause and Resume results
|
|
48
|
+
are announced. `AutomationActions` owns Edit, Pause or Resume, pause help, and
|
|
49
|
+
Delete. Lifecycle actions re-read the revision before they apply. Public
|
|
50
|
+
readers can edit settings. Pause, Resume, and Delete stay creator-only, so
|
|
51
|
+
other viewers do not see them. The About card names the creator-only actions.
|
|
52
|
+
Completed Automations have no Edit action, and `/edit` returns to Overview.
|
|
53
|
+
|
|
54
|
+
Overview shows `AutomationAttention` first when something needs action: a
|
|
55
|
+
block, an unavailable trigger, or a failed last run. The notice has one main
|
|
56
|
+
action, such as Change trigger or Open last run. While it shows, the header
|
|
57
|
+
Edit button becomes a secondary button. The saved settings follow
|
|
58
|
+
in the same `AutomationFormSection` rows as the editor, so edit mode changes
|
|
59
|
+
them in place. The side column shows the next trigger, the last result, runs,
|
|
60
|
+
failures, and spend for the range, and About. Spend is `costUsd` from the
|
|
61
|
+
Automation summary. On small screens, status comes first and About moves below
|
|
62
|
+
the settings.
|
|
62
63
|
|
|
63
64
|
`AutomationRecentRuns` uses the existing execution-history query and cache. It
|
|
64
65
|
shows up to three newest terminal runs, links to their Conversations when one
|
|
@@ -67,21 +68,30 @@ titles that repeat the Automation name. Its loading,
|
|
|
67
68
|
empty, and retry states do not replace Automation settings. It does not infer
|
|
68
69
|
queued or running work from terminal records.
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
scroll region above it owns the summary and settings. Creator, creation date,
|
|
75
|
-
connected accounts, run counts, and version history stay under About this
|
|
76
|
-
Automation. Selection changes remount the content and actions together.
|
|
71
|
+
In edit mode, "Your changes" lists each changed field with the saved value, the
|
|
72
|
+
new value, and its own Undo. It is beside the form on wide screens and below
|
|
73
|
+
the form on small screens when there are changes. Undo remounts the fields, so fields with local state show the saved value again.
|
|
74
|
+
Changed sections get an accent mark. Save and Cancel return to Overview.
|
|
77
75
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
The Runs tab filters loaded runs by result through the `result` param. Rows
|
|
77
|
+
show the version that ran and the recorded error. A row opens the run details
|
|
78
|
+
panel. The run list scrolls on its own beside it.
|
|
79
|
+
|
|
80
|
+
The Versions tab lists versions and compares the selected one with the version
|
|
81
|
+
before it. The `version` param selects a version; run details link to it.
|
|
82
|
+
Changed fields show the previous value. Instruction changes use a line diff.
|
|
82
83
|
|
|
83
84
|
`AutomationDeleteDialog` names the Automation before deletion. It keeps errors
|
|
84
85
|
and retry inside the dialog and blocks dismissal while the request is pending.
|
|
85
|
-
Cancel returns focus to Delete. Successful deletion
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
Cancel returns focus to Delete. Successful deletion returns to the list.
|
|
87
|
+
Success toasts confirm Save, Pause, Resume, and Delete. Their state belongs to
|
|
88
|
+
the mounted dashboard's Toaster provider.
|
|
89
|
+
|
|
90
|
+
Browser journeys for the page live in `e2e/automations.spec.ts`. Use mock
|
|
91
|
+
reporting to review scheduled and event Automations in both themes at desktop
|
|
92
|
+
and mobile widths. Keep layout checks in manual QA.
|
|
93
|
+
|
|
94
|
+
Filters use a shadcn Popover at 768px and above and the shared BottomSheet below
|
|
95
|
+
that width. Both edit the same URL filters immediately. Active filter chips
|
|
96
|
+
remove one filter without clearing search, scope, or other filters. The mobile
|
|
97
|
+
sheet fits its controls instead of taking the full screen height.
|
package/src/client/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ObjectType } from "@sentry/junior-plugin-api";
|
|
2
2
|
import type { BundledLanguage } from "shiki/bundle/web";
|
|
3
3
|
import type {
|
|
4
|
+
InputImage,
|
|
4
5
|
MessageAttachment,
|
|
5
6
|
MessageCard,
|
|
6
7
|
PluginOperationalReportFeed,
|
|
@@ -151,6 +152,8 @@ export type TranscriptViewMessage = {
|
|
|
151
152
|
failureReason?: ConversationTurnFailureReason;
|
|
152
153
|
sentryEventUrl?: string;
|
|
153
154
|
attachments?: MessageAttachment[];
|
|
155
|
+
/** Local images of a pending send that the server has not stored yet. */
|
|
156
|
+
images?: InputImage[];
|
|
154
157
|
parts: TranscriptViewPart[];
|
|
155
158
|
role: "assistant" | "system" | "tool" | "user";
|
|
156
159
|
source?: "slack" | "web";
|
|
@@ -572,7 +572,16 @@ function activeConversation(nowMs: number): ConversationDetailReport {
|
|
|
572
572
|
type: "message",
|
|
573
573
|
messageId: "active-user-followup",
|
|
574
574
|
role: "user",
|
|
575
|
-
|
|
575
|
+
// Quoted reply keeps blockquote and nested-list rendering in visual QA.
|
|
576
|
+
text: [
|
|
577
|
+
"> error volume is flat, so this looks like slow path not hard fail",
|
|
578
|
+
"",
|
|
579
|
+
"Agreed. Compare the pre/post deploy spans next:",
|
|
580
|
+
"1. `checkout.complete` p95",
|
|
581
|
+
" - stable hosts",
|
|
582
|
+
" - canary hosts",
|
|
583
|
+
"2. ~~error rate~~ serializer span time",
|
|
584
|
+
].join("\n"),
|
|
576
585
|
}),
|
|
577
586
|
reportEvent(8, iso(Date.parse(startedAt), 42_000), {
|
|
578
587
|
type: "turn_lifecycle",
|
|
@@ -1451,7 +1460,7 @@ function eventAutomationRunConversation(
|
|
|
1451
1460
|
"This is a task, not a message from a person.",
|
|
1452
1461
|
"",
|
|
1453
1462
|
"About: Issue · ACME-42",
|
|
1454
|
-
'Created by: <@U123>. "
|
|
1463
|
+
'Created by: <@U123>. This person is the Automation creator. "The Automation creator", "me", and "my" in the instructions mean this person. Where the instructions refer to this person, write this mention in the message. If they do not, do not mention this person.',
|
|
1455
1464
|
"Instructions: Summarize the closed issue",
|
|
1456
1465
|
"",
|
|
1457
1466
|
"Trusted summary: ACME-42 Checkout fails for saved cards was closed by Morgan Lee.",
|
|
@@ -1944,6 +1953,14 @@ export function readMockConversationPendingMessages(
|
|
|
1944
1953
|
role: "user" as const,
|
|
1945
1954
|
source: "web" as const,
|
|
1946
1955
|
text: "Keep the reply in Junior. I will paste the dashboard link next.",
|
|
1956
|
+
attachments: [
|
|
1957
|
+
{
|
|
1958
|
+
id: "qa-chart-png",
|
|
1959
|
+
filename: "queued-chart.png",
|
|
1960
|
+
contentType: "image/png",
|
|
1961
|
+
bytes: 18211,
|
|
1962
|
+
},
|
|
1963
|
+
],
|
|
1947
1964
|
},
|
|
1948
1965
|
{
|
|
1949
1966
|
actorIdentity: actor("dev@example.com", "Morgan Lee", "morgan"),
|
|
@@ -1978,6 +1995,28 @@ export function readMockConversationPendingMessages(
|
|
|
1978
1995
|
source: "web" as const,
|
|
1979
1996
|
text: "Fifth queued message.",
|
|
1980
1997
|
},
|
|
1998
|
+
{
|
|
1999
|
+
actorIdentity: actor("dev@example.com", "Morgan Lee", "morgan"),
|
|
2000
|
+
createdAt: iso(nowMs, -800),
|
|
2001
|
+
delivery: "defer" as const,
|
|
2002
|
+
inboundMessageId: `${conversationId}:pending-sixth`,
|
|
2003
|
+
messageId: `${conversationId}:pending-sixth`,
|
|
2004
|
+
receivedAt: iso(nowMs, -400),
|
|
2005
|
+
role: "user" as const,
|
|
2006
|
+
source: "web" as const,
|
|
2007
|
+
text: "Sixth queued message.",
|
|
2008
|
+
},
|
|
2009
|
+
{
|
|
2010
|
+
actorIdentity: actor("dev@example.com", "Morgan Lee", "morgan"),
|
|
2011
|
+
createdAt: iso(nowMs, -600),
|
|
2012
|
+
delivery: "defer" as const,
|
|
2013
|
+
inboundMessageId: `${conversationId}:pending-seventh`,
|
|
2014
|
+
messageId: `${conversationId}:pending-seventh`,
|
|
2015
|
+
receivedAt: iso(nowMs, -300),
|
|
2016
|
+
role: "user" as const,
|
|
2017
|
+
source: "web" as const,
|
|
2018
|
+
text: "Seventh queued message.",
|
|
2019
|
+
},
|
|
1981
2020
|
]
|
|
1982
2021
|
: [];
|
|
1983
2022
|
|
|
@@ -2949,6 +2988,7 @@ function mockTasks(): AutomationSummary[] {
|
|
|
2949
2988
|
nextRunAt: "2026-08-10T16:00:00.000Z",
|
|
2950
2989
|
credentialMode: "creator",
|
|
2951
2990
|
ownedByViewer: true,
|
|
2991
|
+
costUsd: { 1: 0.42, 7: 1.02, 30: 4.18, 90: 16.4 },
|
|
2952
2992
|
runs: { 1: 1, 7: 3, 30: 12, 90: 48 },
|
|
2953
2993
|
timezone: "America/Los_Angeles",
|
|
2954
2994
|
schedule: "Every Monday at 9:00 AM",
|
|
@@ -2980,6 +3020,7 @@ function mockTasks(): AutomationSummary[] {
|
|
|
2980
3020
|
credentialMode: "creator",
|
|
2981
3021
|
ownedByViewer: true,
|
|
2982
3022
|
resource: "Issue · ACME-42",
|
|
3023
|
+
costUsd: { 1: 0, 7: 0.12, 30: 0.48, 90: 0.84 },
|
|
2983
3024
|
runs: { 1: 0, 7: 1, 30: 4, 90: 7 },
|
|
2984
3025
|
source: "github",
|
|
2985
3026
|
outcomes: [],
|
|
@@ -3008,6 +3049,7 @@ function mockTasks(): AutomationSummary[] {
|
|
|
3008
3049
|
credentialMode: "creator",
|
|
3009
3050
|
ownedByViewer: false,
|
|
3010
3051
|
resource: "Incident · INC-17",
|
|
3052
|
+
costUsd: { 1: 0, 7: 0, 30: 0, 90: 0 },
|
|
3011
3053
|
runs: { 1: 0, 7: 0, 30: 0, 90: 0 },
|
|
3012
3054
|
source: "pagerduty",
|
|
3013
3055
|
outcomes: [
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/** Share the transcript empty/unavailable frame across top-level and segment views. */
|
|
2
|
-
export function transcriptEmptyClass(
|
|
3
|
-
tone: "default" | "error" = "default",
|
|
4
|
-
): string {
|
|
5
|
-
const colors =
|
|
6
|
-
tone === "error"
|
|
7
|
-
? "border-rose-300/25 bg-rose-300/[0.07] text-rose-100"
|
|
8
|
-
: "border-dashboard-border bg-dashboard-fill-soft text-dashboard-text-muted";
|
|
9
|
-
return `rounded-lg border p-5 text-sm leading-relaxed ${colors}`;
|
|
10
|
-
}
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import type { AutomationSummary } from "@sentry/junior/api/schema";
|
|
2
|
-
import type { ReactNode } from "react";
|
|
3
|
-
import { Link, useLocation } from "react-router";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
Accordion,
|
|
7
|
-
AccordionContent,
|
|
8
|
-
AccordionItem,
|
|
9
|
-
AccordionTrigger,
|
|
10
|
-
} from "../../components/ui/accordion";
|
|
11
|
-
import { Button } from "../../components/ui/button";
|
|
12
|
-
import { DetailsPanel } from "../../components/DetailsPanel";
|
|
13
|
-
import { InlineError } from "../../components/InlineError";
|
|
14
|
-
import { PageContentSkeleton } from "../../components/PageContentSkeleton";
|
|
15
|
-
import {
|
|
16
|
-
timeRangeLabel,
|
|
17
|
-
type TimeRangeDays,
|
|
18
|
-
} from "../../components/controls/TimeRangeSelector";
|
|
19
|
-
import { TranscriptText } from "../../conversations/TranscriptText";
|
|
20
|
-
import { peoplePath } from "../../format";
|
|
21
|
-
import { dashboardInlineLinkClass } from "../../styles";
|
|
22
|
-
import { TriggerFilters } from "./TriggerFilters";
|
|
23
|
-
import { AutomationLifecycle } from "./AutomationLifecycle";
|
|
24
|
-
import { AutomationRecentRuns } from "./AutomationRecentRuns";
|
|
25
|
-
import { AutomationStatus } from "./AutomationStatus";
|
|
26
|
-
import {
|
|
27
|
-
automationScheduleLabel,
|
|
28
|
-
formatAutomationRunTime,
|
|
29
|
-
} from "./automationFormat";
|
|
30
|
-
import { automationOutcomeLabel } from "./automationOutcomes";
|
|
31
|
-
|
|
32
|
-
/** Keep Automation context scrollable above persistent, permission-aware actions. */
|
|
33
|
-
export function AutomationDetails(props: {
|
|
34
|
-
onClose(): void;
|
|
35
|
-
onDelete(source: HTMLButtonElement): void;
|
|
36
|
-
onRetry(): void;
|
|
37
|
-
error: boolean;
|
|
38
|
-
range: TimeRangeDays;
|
|
39
|
-
automation: AutomationSummary | undefined;
|
|
40
|
-
}) {
|
|
41
|
-
const location = useLocation();
|
|
42
|
-
const { automation } = props;
|
|
43
|
-
return (
|
|
44
|
-
<DetailsPanel
|
|
45
|
-
label="Automation details"
|
|
46
|
-
sheetTitle={automation?.title}
|
|
47
|
-
onClose={props.onClose}
|
|
48
|
-
title={
|
|
49
|
-
<h2 className="m-0 break-words font-display text-base font-medium text-dashboard-text">
|
|
50
|
-
{automation?.title ?? "Automation details"}
|
|
51
|
-
</h2>
|
|
52
|
-
}
|
|
53
|
-
>
|
|
54
|
-
<div className="grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto]">
|
|
55
|
-
<div
|
|
56
|
-
aria-label="Automation content"
|
|
57
|
-
role="region"
|
|
58
|
-
tabIndex={0}
|
|
59
|
-
className="min-h-0 overflow-y-auto overscroll-contain p-4 outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus [overflow-wrap:anywhere]"
|
|
60
|
-
>
|
|
61
|
-
{automation ? (
|
|
62
|
-
<AutomationDetailsContent
|
|
63
|
-
automation={automation}
|
|
64
|
-
range={props.range}
|
|
65
|
-
/>
|
|
66
|
-
) : null}
|
|
67
|
-
{props.error ? (
|
|
68
|
-
<InlineError>
|
|
69
|
-
Automation details could not be loaded. It may be unavailable or
|
|
70
|
-
you may not have access.{" "}
|
|
71
|
-
<Button onClick={props.onRetry}>Try again</Button>
|
|
72
|
-
</InlineError>
|
|
73
|
-
) : !automation ? (
|
|
74
|
-
<PageContentSkeleton
|
|
75
|
-
label="Loading automation details"
|
|
76
|
-
variant="panel"
|
|
77
|
-
/>
|
|
78
|
-
) : null}
|
|
79
|
-
</div>
|
|
80
|
-
{automation ? (
|
|
81
|
-
<AutomationLifecycle
|
|
82
|
-
automation={automation}
|
|
83
|
-
onDelete={props.onDelete}
|
|
84
|
-
editPath={`/automations/${encodeURIComponent(automation.id)}/edit${location.search}`}
|
|
85
|
-
/>
|
|
86
|
-
) : null}
|
|
87
|
-
</div>
|
|
88
|
-
</DetailsPanel>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function AutomationDetailsContent({
|
|
93
|
-
automation,
|
|
94
|
-
range,
|
|
95
|
-
}: {
|
|
96
|
-
automation: AutomationSummary;
|
|
97
|
-
range: TimeRangeDays;
|
|
98
|
-
}) {
|
|
99
|
-
const location = useLocation();
|
|
100
|
-
return (
|
|
101
|
-
<div className="grid min-w-0 gap-5">
|
|
102
|
-
<AutomationStatus automation={automation} />
|
|
103
|
-
<section aria-label="Instructions" className="text-sm leading-relaxed">
|
|
104
|
-
<TranscriptText
|
|
105
|
-
mentions={automation.mentions ?? []}
|
|
106
|
-
text={automation.instruction}
|
|
107
|
-
/>
|
|
108
|
-
</section>
|
|
109
|
-
<dl className="m-0 grid gap-3 text-xs leading-relaxed">
|
|
110
|
-
<AutomationFact label="Trigger">
|
|
111
|
-
{automation.kind === "scheduled" ? (
|
|
112
|
-
<p className="m-0">{automationScheduleLabel(automation)}</p>
|
|
113
|
-
) : (
|
|
114
|
-
<>
|
|
115
|
-
<p className="m-0 break-words">{automation.resource}</p>
|
|
116
|
-
<p className="m-0 mt-1 text-dashboard-text-muted">
|
|
117
|
-
{automation.trigger.events.join(", ")}
|
|
118
|
-
</p>
|
|
119
|
-
<TriggerFilters filters={automation.trigger.filters} />
|
|
120
|
-
</>
|
|
121
|
-
)}
|
|
122
|
-
</AutomationFact>
|
|
123
|
-
<AutomationFact label="On success">
|
|
124
|
-
{automation.outcomes.length === 0 ? (
|
|
125
|
-
<span className="text-dashboard-text-muted">No message</span>
|
|
126
|
-
) : (
|
|
127
|
-
<ol className="m-0 grid list-none gap-2 p-0">
|
|
128
|
-
{automation.outcomes.map((outcome, index) => (
|
|
129
|
-
<li key={index}>
|
|
130
|
-
Message{" "}
|
|
131
|
-
{automationOutcomeLabel(outcome, automation.destination)}
|
|
132
|
-
</li>
|
|
133
|
-
))}
|
|
134
|
-
</ol>
|
|
135
|
-
)}
|
|
136
|
-
</AutomationFact>
|
|
137
|
-
</dl>
|
|
138
|
-
<AutomationRecentRuns automation={automation} />
|
|
139
|
-
<Accordion className="border-t border-dashboard-border pt-1">
|
|
140
|
-
<AccordionItem value="about">
|
|
141
|
-
<AccordionTrigger className="min-h-11 text-xs text-dashboard-text-muted">
|
|
142
|
-
About this Automation
|
|
143
|
-
</AccordionTrigger>
|
|
144
|
-
<AccordionContent>
|
|
145
|
-
<dl className="m-0 grid gap-3 text-xs leading-relaxed">
|
|
146
|
-
<AutomationFact label="Created by">
|
|
147
|
-
{automation.createdByEmail ? (
|
|
148
|
-
<Link
|
|
149
|
-
className={dashboardInlineLinkClass}
|
|
150
|
-
to={peoplePath(automation.createdByEmail)}
|
|
151
|
-
>
|
|
152
|
-
{automation.ownedByViewer ? "you" : automation.createdBy}
|
|
153
|
-
</Link>
|
|
154
|
-
) : automation.ownedByViewer ? (
|
|
155
|
-
"you"
|
|
156
|
-
) : (
|
|
157
|
-
automation.createdBy
|
|
158
|
-
)}
|
|
159
|
-
</AutomationFact>
|
|
160
|
-
<AutomationFact label="Created">
|
|
161
|
-
{formatAutomationRunTime(automation.createdAt)}
|
|
162
|
-
</AutomationFact>
|
|
163
|
-
<AutomationFact label="Accounts">
|
|
164
|
-
{automation.credentialMode === "creator"
|
|
165
|
-
? `Uses accounts connected by ${automation.ownedByViewer ? "you" : automation.createdBy}.`
|
|
166
|
-
: "Junior’s accounts only"}
|
|
167
|
-
</AutomationFact>
|
|
168
|
-
<AutomationFact label="Runs">
|
|
169
|
-
{automation.runs[range]} runs / {timeRangeLabel(range)} ·{" "}
|
|
170
|
-
{automation.totalRuns} total
|
|
171
|
-
</AutomationFact>
|
|
172
|
-
</dl>
|
|
173
|
-
<Link
|
|
174
|
-
className={`${dashboardInlineLinkClass} mt-3 inline-block py-2 text-xs`}
|
|
175
|
-
to={`/automations/${encodeURIComponent(automation.id)}/versions${location.search}`}
|
|
176
|
-
>
|
|
177
|
-
Version history
|
|
178
|
-
</Link>
|
|
179
|
-
</AccordionContent>
|
|
180
|
-
</AccordionItem>
|
|
181
|
-
</Accordion>
|
|
182
|
-
</div>
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function AutomationFact(props: { label: string; children: ReactNode }) {
|
|
187
|
-
return (
|
|
188
|
-
<div className="grid min-w-0 grid-cols-[5.25rem_minmax(0,1fr)] gap-3">
|
|
189
|
-
<dt className="text-dashboard-text-muted">{props.label}</dt>
|
|
190
|
-
<dd className="m-0 min-w-0">{props.children}</dd>
|
|
191
|
-
</div>
|
|
192
|
-
);
|
|
193
|
-
}
|