@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.
Files changed (73) hide show
  1. package/dist/app.js +49 -12
  2. package/dist/client.js +94 -63
  3. package/dist/index.js +49 -12
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +11 -5
  6. package/src/app.ts +0 -2
  7. package/src/auth.ts +4 -7
  8. package/src/client/App.tsx +35 -53
  9. package/src/client/components/DetailsPanel.tsx +12 -13
  10. package/src/client/components/Drawer.tsx +46 -23
  11. package/src/client/components/ImageAttachment.tsx +1 -1
  12. package/src/client/components/ParticipantAvatarStack.tsx +4 -1
  13. package/src/client/components/README.md +11 -2
  14. package/src/client/components/ui/alert.tsx +1 -1
  15. package/src/client/components/ui/button-group.tsx +59 -0
  16. package/src/client/components/ui/dialog.tsx +29 -35
  17. package/src/client/components/ui/drawer.tsx +140 -0
  18. package/src/client/components/ui/empty.tsx +30 -1
  19. package/src/client/components/ui/separator.tsx +22 -0
  20. package/src/client/components/ui/useSheet.ts +5 -1
  21. package/src/client/conversations/ComposerImages.tsx +66 -14
  22. package/src/client/conversations/ConversationComposer.tsx +84 -12
  23. package/src/client/conversations/ConversationDetails.tsx +3 -27
  24. package/src/client/conversations/ConversationEventDetails.tsx +26 -10
  25. package/src/client/conversations/ConversationPage.tsx +156 -2
  26. package/src/client/conversations/ConversationTranscript.tsx +48 -16
  27. package/src/client/conversations/DetailsList.tsx +33 -0
  28. package/src/client/conversations/EventDetails.tsx +46 -19
  29. package/src/client/conversations/ForkConversationButton.tsx +79 -73
  30. package/src/client/conversations/PendingMailboxStack.tsx +310 -112
  31. package/src/client/conversations/README.md +30 -13
  32. package/src/client/conversations/SubagentTranscriptDrawer.tsx +19 -22
  33. package/src/client/conversations/TranscriptActivityGroup.tsx +22 -19
  34. package/src/client/conversations/TranscriptAttachment.tsx +39 -4
  35. package/src/client/conversations/TranscriptAutomationTriggerView.tsx +14 -19
  36. package/src/client/conversations/TranscriptFailureView.tsx +10 -13
  37. package/src/client/conversations/TranscriptMarkdown.tsx +232 -436
  38. package/src/client/conversations/TranscriptMessageView.tsx +7 -1
  39. package/src/client/conversations/TranscriptSummary.tsx +5 -31
  40. package/src/client/conversations/TranscriptTurnContextView.tsx +33 -18
  41. package/src/client/conversations/TranscriptView.tsx +7 -3
  42. package/src/client/conversations/UserInputRequestCard.tsx +244 -0
  43. package/src/client/conversations/conversationOutbox.ts +46 -0
  44. package/src/client/conversations/eventTime.ts +13 -1
  45. package/src/client/conversations/eventTranscript.ts +12 -5
  46. package/src/client/conversations/queries.ts +127 -26
  47. package/src/client/conversations/transcriptMarkdownLinks.ts +5 -367
  48. package/src/client/conversations/userInputRequest.ts +56 -0
  49. package/src/client/format.ts +10 -3
  50. package/src/client/pages/automations/{AutomationLifecycle.tsx → AutomationActions.tsx} +60 -69
  51. package/src/client/pages/automations/AutomationDraftChanges.tsx +171 -0
  52. package/src/client/pages/automations/AutomationEditor.tsx +182 -156
  53. package/src/client/pages/automations/AutomationEventFields.tsx +119 -45
  54. package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +1 -1
  55. package/src/client/pages/automations/AutomationFormSection.tsx +20 -2
  56. package/src/client/pages/automations/AutomationOutcomeFields.tsx +1 -1
  57. package/src/client/pages/automations/AutomationOverview.tsx +238 -0
  58. package/src/client/pages/automations/AutomationPage.tsx +343 -0
  59. package/src/client/pages/automations/AutomationRecentRuns.tsx +3 -2
  60. package/src/client/pages/automations/AutomationRow.tsx +11 -13
  61. package/src/client/pages/automations/AutomationRunDetails.tsx +4 -2
  62. package/src/client/pages/automations/{AutomationExecutionsPage.tsx → AutomationRunsTab.tsx} +94 -103
  63. package/src/client/pages/automations/AutomationStatus.tsx +164 -108
  64. package/src/client/pages/automations/AutomationVersionsTab.tsx +518 -0
  65. package/src/client/pages/automations/AutomationsPage.tsx +10 -98
  66. package/src/client/pages/automations/AutomationsPageLayout.tsx +5 -23
  67. package/src/client/pages/automations/README.md +56 -46
  68. package/src/client/types.ts +3 -0
  69. package/src/mock-reporting/fixtures.ts +44 -2
  70. package/src/client/conversations/transcriptStyles.ts +0 -10
  71. package/src/client/pages/automations/AutomationDetails.tsx +0 -193
  72. package/src/client/pages/automations/AutomationEditPage.tsx +0 -176
  73. 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 the Automation list and its selected Automation routes. */
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/executions", pathname))
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 lists, details, and editing. */
45
- export function AutomationsNavigation(props: {
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
- Owners and public Destination readers can edit. For a public reader, the
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 keep the list query string.
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 details
33
-
34
- The URL selects one Automation in the list. Route matching follows React
35
- Router, including trailing slashes. `AutomationDetails` uses the shared
36
- `DetailsPanel`: a 400px panel at 1280px and above, and a Base UI bottom sheet on
37
- smaller screens. The list stays interactive beside the panel. Each pane scrolls
38
- on its own. `VisualViewportShell` owns the viewport height and mobile body lock;
39
- `AutomationsPageLayout` keeps navigation above the list and details.
40
-
41
- Loading and access errors stay inside the details frame. Closing keeps the list
42
- query string and returns focus to the selected row. If that row is not on the
43
- current page, focus returns to the list. Selecting another Automation resets
44
- its disclosures and lifecycle feedback. Deleting the selected Automation closes
45
- its details. Edit and version history retain the list query string.
46
-
47
- The existing Automation browser journeys cover selection, loading and retry,
48
- focus, mobile dismissal, deletion, and lifecycle actions. Use mock reporting to
49
- review scheduled and event Automations in both themes at desktop and mobile
50
- widths. Keep layout and scrollbar checks in manual QA.
51
-
52
- Details lead with lifecycle, the next trigger, and the last execution result.
53
- Instructions follow, then compact trigger and delivery rows. The sheet header
54
- uses the Automation name. `AutomationStatus`
55
- keeps a failed execution distinct from whether future runs remain enabled. It
56
- shows block reasons and links to the last Conversation when attention is needed.
57
- Scheduled times use the shared shadcn Accordion to show the schedule timezone
58
- and browser time. About this Automation uses the same component. Both start
59
- collapsed and reset when the selection changes.
60
- Paused, blocked, completed, and unavailable Automations explain why there is no
61
- next run instead of showing "None".
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
- `AutomationLifecycle` owns revision-checked actions in a fixed footer. Edit and
71
- Delete use labeled icons there; list rows have no action menu. Public readers
72
- can edit settings. Delete and lifecycle actions stay creator-only. The shared
73
- shadcn Popover provides pause help beside those controls. The
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
- Filters use a shadcn Popover at 768px and above and the shared BottomSheet below
79
- that width. Both edit the same URL filters immediately. Active filter chips
80
- remove one filter without clearing search, scope, or other filters. The mobile
81
- sheet fits its controls instead of taking the full details height.
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 closes the selected details
86
- and returns focus to the list. Success toasts confirm Save, Pause, Resume, and
87
- Delete. Their state belongs to the mounted dashboard's Toaster provider.
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.
@@ -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
- text: "Compare the pre/post deploy spans next.",
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>. "Me" and "my" in the instructions mean this person: mention them in the message. If the instructions do not say "me" or "my", do not mention this person.',
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
- }